2024-07-08 22:46:35 +02:00

173 lines
4.7 KiB
JavaScript
Executable File

// SPDX-FileCopyrightText: GSConnect Developers https://github.com/GSConnect
//
// SPDX-License-Identifier: GPL-2.0-or-later
import Gdk from 'gi://Gdk';
import Gio from 'gi://Gio';
import GLib from 'gi://GLib';
let GSound = null;
try {
GSound = (await import('gi://GSound')).default;
} catch (e) {}
const Player = class Player {
constructor() {
this._playing = new Set();
}
get backend() {
if (this._backend === undefined) {
// Prefer GSound
if (GSound !== null) {
this._gsound = new GSound.Context();
this._gsound.init(null);
this._backend = 'gsound';
// Try falling back to libcanberra, otherwise just re-run the test
// in case one or the other is installed later
} else if (GLib.find_program_in_path('canberra-gtk-play') !== null) {
this._canberra = new Gio.SubprocessLauncher({
flags: Gio.SubprocessFlags.NONE,
});
this._backend = 'libcanberra';
} else {
return null;
}
}
return this._backend;
}
_canberraPlaySound(name, cancellable) {
const proc = this._canberra.spawnv(['canberra-gtk-play', '-i', name]);
return proc.wait_check_async(cancellable);
}
async _canberraLoopSound(name, cancellable) {
while (!cancellable.is_cancelled())
await this._canberraPlaySound(name, cancellable);
}
_gsoundPlaySound(name, cancellable) {
return new Promise((resolve, reject) => {
this._gsound.play_full(
{'event.id': name},
cancellable,
(source, res) => {
try {
resolve(source.play_full_finish(res));
} catch (e) {
reject(e);
}
}
);
});
}
async _gsoundLoopSound(name, cancellable) {
while (!cancellable.is_cancelled())
await this._gsoundPlaySound(name, cancellable);
}
_gdkPlaySound(name, cancellable) {
if (this._display === undefined)
this._display = Gdk.Display.get_default();
let count = 0;
GLib.timeout_add(GLib.PRIORITY_DEFAULT, 200, () => {
try {
if (count++ < 4 && !cancellable.is_cancelled()) {
this._display.beep();
return GLib.SOURCE_CONTINUE;
}
return GLib.SOURCE_REMOVE;
} catch (e) {
logError(e);
return GLib.SOURCE_REMOVE;
}
});
return !cancellable.is_cancelled();
}
_gdkLoopSound(name, cancellable) {
this._gdkPlaySound(name, cancellable);
GLib.timeout_add(
GLib.PRIORITY_DEFAULT,
1500,
this._gdkPlaySound.bind(this, name, cancellable)
);
}
async playSound(name, cancellable) {
try {
if (!(cancellable instanceof Gio.Cancellable))
cancellable = new Gio.Cancellable();
this._playing.add(cancellable);
switch (this.backend) {
case 'gsound':
await this._gsoundPlaySound(name, cancellable);
break;
case 'canberra':
await this._canberraPlaySound(name, cancellable);
break;
default:
await this._gdkPlaySound(name, cancellable);
}
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
logError(e);
} finally {
this._playing.delete(cancellable);
}
}
async loopSound(name, cancellable) {
try {
if (!(cancellable instanceof Gio.Cancellable))
cancellable = new Gio.Cancellable();
this._playing.add(cancellable);
switch (this.backend) {
case 'gsound':
await this._gsoundLoopSound(name, cancellable);
break;
case 'canberra':
await this._canberraLoopSound(name, cancellable);
break;
default:
await this._gdkLoopSound(name, cancellable);
}
} catch (e) {
if (!e.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED))
logError(e);
} finally {
this._playing.delete(cancellable);
}
}
destroy() {
for (const cancellable of this._playing)
cancellable.cancel();
}
};
/**
* The service class for this component
*/
export default Player;