You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
43 lines
1.5 KiB
43 lines
1.5 KiB
import { createEngineSound } from '$sound';
|
|
import type { EngineSound, SoundDom, SoundTimers } from '$sound';
|
|
import type { AppServiceFactory } from '../services.ts';
|
|
|
|
/**
|
|
* `defineEngineSound()` produces the service factory for the `sound` slot — the
|
|
* shared Web Audio runtime extracted from sema's `SoundChannel` (2026-07-30).
|
|
*
|
|
* It is a SINGLE engine on purpose: the browser caps concurrent
|
|
* `AudioContext`s and the autoplay unlock gesture is per-context, so a second
|
|
* one would leave either the UI earcons or the app's own content mute. Every
|
|
* consumer — sema's sound channel, a media player, a waveform, a visualiser —
|
|
* meets here and hangs its graph off `sound.context`.
|
|
*
|
|
* Needs the DOM service (the global unlock listener) via
|
|
* `serviceDependencies: ['dom']`, and the core `timers` for the earcon's
|
|
* completion. Both degrade: without `dom` a suspended context is resumed on the
|
|
* next `prime()` instead of on the next gesture; without `timers` the engine
|
|
* falls back to `setTimeout`.
|
|
*/
|
|
export function defineEngineSound(): AppServiceFactory<
|
|
'sound',
|
|
readonly ['timers'],
|
|
readonly ['dom'],
|
|
EngineSound
|
|
> {
|
|
return {
|
|
name: 'sound',
|
|
coreDependencies: ['timers'],
|
|
serviceDependencies: ['dom'],
|
|
initMode: 'lazy',
|
|
create({ core, services }): EngineSound {
|
|
return createEngineSound({
|
|
dom: services.dom as SoundDom | undefined,
|
|
timers: core.timers as SoundTimers | undefined
|
|
});
|
|
},
|
|
dispose(instance) {
|
|
instance.dispose();
|
|
}
|
|
};
|
|
}
|