Provide a system sounds API
Background
GTK currently only has a "beep" API which is plugged into the display connection:
- on X11, we call
XkbBell()
- on Wayland, we call into the
gtk_shell1.system_bell()
interface, which is GTK-specific - on Windows, we call
Beep()
- on macOS, we call
NSBeep()
How each windowing system translates those calls is entirely up to them. This covers the "system alert" use case, which is fine for most notifications, and keeps the soundscape limited. Other platforms additionally have an API for playing a sound that is not the system alert one.
In the past, we used libcanberra on platforms that used PulseAudio, but:
- libcanberra has been unmaintained for the past 8 years, at the time of writing
- the API is fairly overengineered, with a lot of work going into setting up timing and source of each sound
- libcanberra is X11-centric, and relies on things like global and device coordinates that are simply unavailable on Wayland
- libcanberra is tied to the concept of a "sound theme", which is an overengineered approach to soundscapes and it's been largely irrelevant for the past 10+ years
Requirements
Applications have two needs, when it comes to sound:
- emit the system alert sound
- play a custom sound, either as an on-disk asset or as a binary blob
The current best practice for the latter option is to literally set up a GStreamer pipeline. We can do a little bit better.
API proposal
Interface pseudo-code:
namespace Gtk {
class SoundPlayer: Gio.Initable {
@property Gio.InputStream sound_stream is read-write, construct-only;
@property bool is_playing is read-only;
@property string name is read-write;
@property double progress is read-only;
@property bool loop is read-write;
@constructor new_for_file (Gio.File file, Gio.Cancellable? cancellable, Gio.AsyncReadyCallback callback);
@constructor new_for_resource (string resource, Gio.Cancellable? cancellable, Gio.AsyncReadyCallback callback);
@constructor new_for_bytes (GLib.Bytes data, Gio.Cancellable? cancellable, Gio.AsyncReadyCallback callback);
void play ();
void pause ();
void stop ();
}
}
The SoundPlayer instance is immutable after creation, and bound to a specific buffer, to ensure people don't use it for writing media players—they should really be using GstPlayer for that. This also avoids having a "please load this file" API.
Prior art
- GSound, a (long since unmaintained) wrapper around libcanberra that adds an introspectable GObject layer around the original C-only API
- NSSound on AppKit
- SystemSounds and SoundPlayer on Windows