SwayNotificationCenter
SwayNotificationCenter copied to clipboard
Play sounds for new notifications
Some notification daemons support playing sounds when a new notification pops up, some don't. The xdg notification spec even describes hints for controlling the sound behaviour, such as suppress, specify an xdg-sound-name to play or directly pass the path of a file.
In the weekend I should be able to setup a new branch and play around with the code. I'll probably use libcanberra, but I also saw that GSound and Gstreamer are available in case good 'ol canberra is unsuitable.
I think that elementary users canberra for audio so it should be doable :)
Working on it right now, sorry for the delay, had a busy weekend. Should dnd affect the sound as it does for the notiWindow?
Oh nw! Thanks for the help! Dnd shouldn't play any sounds
What about urgent notifications? They pop up even when dnd is enabled
Hmmm. I'm not sure about that one. A config option would be good though
So like DND on android, where you can have urgent visual notifications turned on, but sound disallowed right? If so, I'm almost suggesting we give the option to completely hide even urgent notifications.
I don't think I see a predefined precedence for sounds defined in the spec. What if a notification happens to have both sound-file and sound-name defined?
I think only sound-file should be played, since the client might have defined it to play a special sound. Maybe sound-name should be picked up as a fallback if the above file cannot be played, for whathever reason?
File over name sounds like the best solution. Should we play a default sound of there is none specified? Maybe another config option?
File over name sounds like the best solution. Should we play a default sound of there is none specified? Maybe another config option?
Sure! Unless, for any reason, suppress-sound is specified: in that case no sound is played.
I'd also play the default sound if the sound-name is not recognised, unless canberra already takes care of it.
Ok, work is almost done, there's just a couple of things to iron out.
Apparently libcanberra, if provided with both sound name and sound file, chooses first the named sound, then the sound file; we kinda inverted the behaviour, is it still good? I would like to make it behave similar to other daemons, to avoid possible confusion.
What should we do about the default sound? At the moment, it is hardcoded to the named sound "message-new-instant": this means the sound changes upon installing a different sound theme following the relevant xdg spec. We could provide a setting to change the default named sound to something else, like "message-new-email" of "dialog-error" for example.
There's also the possibility of allowing users to set a default sound file, which supposedly could be put as a first fallback, while leaving the named sound fallback as last resort, but 4 tries to just play a bling seem a little overkill IMHO. See the player code here for clarifications on this last statement
Thoughts?
The default sound file could get pretty messy so I'd stick with a configurable sound name as a fallback.
As mentioned in another issue, I think that the spec includes notification categories like mail and social so a different sound could be set for each
I need to fiddle a bit with categories first, but I should be done tonight.
https://github.com/ErikReider/SwayNotificationCenter/blob/0f69f080b40d3621069bfadea0a414ba2e96bb3a/src/notiModel/notiModel.vala#L154-L158 We should probably convert this into an enum :)
Thanks for the suggestion! If anything else needs to be ported, but is not related to this issue, I'm gonna open new ones ;)
mhm, categories are specified as class.specific_case, like network.disconnected. In case a non standard category like network.banana is passed, should we try to pick network (as it exists in this case) or just drop to the default category?
Enums are flat, but maybe a conversion function string hint -> enum could split on the dot and pick the class first.
Nevermind, x-vendor.class.name would create problems... (and it's allowed)
We could decide to not support them and just fallback to the default category, or try and implement something like nested enums..? I'm not even sure if vala has them.
We should stick with the standardized categories and add some gnome / KDE specific ones too just to be safe
Just to be sure, at this point, would you support specifyng a sound for every subclass or just for classes?
Like, would you want network.error to be different from network.connected or would you just use network?
Hmmm. What about using the same error sound for all errors?
Something like this
case "network.error":
case "something.error":
// play error sound
break;
Some of my toughts on these proposals (I'm also proposing another solution):
- support every vendor.class.subclass by specifying a variable in the config:
- gets big pretty fast (cons)
- allows fine grained control of category sounds (pro)
- if handled tree-like (not flat), allows falling back gracefully (pro)
- support only vendor.class by specifying a variable in the config:
- not as big as the first case and still explicitly declared in config (pro)
- network error and network connected would have the same sound, which is confusing (cons)
- group errors and others subclasses together:
- doesn't allow for fine grained control of sounds (cons)
- there aren't many common groups in the spec as of now, although we could consider deviced.added, transfer.complete and other successful actions simply as "success" (kinda cons IMHO)
- search for vendor.class.subclass, vendor.class or class.subclass audio files in a predefined directory:
- category - sound is not explicit in the config file (cons)
- at the same time, it allows to slim down the relevant code (pro)
- is pretty similar to xdg-sound-theme spec, which we decided is a little bit messy to be relied upon for defaults (cons)
Maybe I have a solution which could get all the pros of the above one.
Suppose we can get at max a 3 level deep tree, as it seems to be the case in the spec. Each sound could be identified in nested dicts, vendor -> class -> subclass. With this structure xdg classes could go under xdg or freedesktop vendor, while others like gnome, kde and the likes would get their fancy group.
Each class could have an entry named "default" just for the sake of having something play if a notification only ships with the category class.
Users could provide their preference without us putting in any new code just to support a specific sound if we allowed them to be written in nested objects in the config, like this:
"sounds": {
"xdg": {
"network": {
"default": "network.ogg",
"connected": "network-connected.ogg"
},
"email": {
"default": "email.ogg"
}
},
"gnome": {
"screenshot": {
"error": "gnome-screenshot-error.ogg"
}
}
}
This way, configs get bigger, not the code itself, and the solution is more generic (at least it sounds so to me, correct if I'm wrong).
I suppose the json scheme should be flexible enough to allow this king of structures, right?
We could also do the same thing for showing category-based images, as you proposed in #70
I was just thinking about this when I saw an issue was already raised. good job guys
Woke up with an idea to simplify my last proposal.
First of all, the config could be written without nested structure by reusing the "dot notation" from xdg.
"sounds": {
"network.connected": "netconn.ogg",
"network.disconnected": "netdisc.ogg",
"email": "maildefault.ogg",
"email.new": "newmail.ogg",
"gnome.screenshot.error": "screenerror.ogg"
}
This is smaller and certainly plain. Parent group - child element rations are also somewhat clear by the notation itself.
On the code side, nested structures are also droped in favor of only one dict which uses the vendor.class.subclass strings as keys.
This allows to do a direct lookup when a notification first comes up, while with yesterday night's proposal had to decompose the category string by splitting on the dots.
Furthermore, if the category is found, the sound file is already on our hands, else the noti category string is modified by removing the last ".something" portion from it. After that, the new string is searched again in the dictionary.
Repeat until a sound is found or no match is detected, which leads to the usage of a fallback or default sound.
vendor.class.subclass vendor.class vendor ""
In case the vendor is not specified, like with standard proposal classes
class.subclass class ""
This loop does at most 3 cycles.
That sounds like a good plan! But to future proof this I have a suggestion. Instead of specifying a sound, we could add a dict for sounds, images, icons, etc... The icons would ofc not be added by this pr
That would certainly prevent deduplication. Something like this then?
"categories-settings": {
"network.connected": {
"sound": "netconn.ogg",
"icon": "icon-network-conn"
},
"network.disconnected": {
"sound": "netdisc.ogg",
"icon": "icon-network-disc"
},
"email": {
"sound": "maildefault.ogg",
"icon": "letter"
},
"email.new": {
"sound": "newmail.ogg"
},
"gnome.screenshot.error": {
"sound": "screenerror.ogg",
"icon": "ui-dialog-error"
}
}
With a structure like this one, it would be better to associate a category class to the key.
That looks really good. Makes it easier to add more features in the future if needed :)
Good! I'm proceeding this way then
yet another halt... Vala doesn't have native dictionaries support.
We could add yet another dependency, libGee, or we could use a custom made structure.
I figured if we use a list to hold the cats, sorted so sibling items go side by side, the lookup shouldn't be too much expensive. I'm not a big fan of this solution to be honest, but is doable.