From 2726cc5c5a3822010a13b842af2ed09b1adb111b Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Sun, 3 May 2026 00:57:26 +0200 Subject: [PATCH] feat(web): expose AudioManager helpers for device introspection + test tone --- packages/web/src/audio/AudioManager.ts | 40 ++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/packages/web/src/audio/AudioManager.ts b/packages/web/src/audio/AudioManager.ts index dc610068..6ba63b29 100644 --- a/packages/web/src/audio/AudioManager.ts +++ b/packages/web/src/audio/AudioManager.ts @@ -389,6 +389,46 @@ export class AudioManager { return this.masterBoost!; } + /** + * Returns the deviceId of the currently active mic stream (the one being + * captured by getUserMedia). May differ from the persisted store value when + * the store says 'default' but Chromium has resolved that to a concrete ID. + */ + getCurrentInputDeviceId(): string { + return this.currentInputDeviceId; + } + + /** + * True if a live mic stream is currently captured. Used by the global + * devicechange handler to decide whether to force-reacquire on OS-default + * change. + */ + hasActiveStream(): boolean { + return !!this.currentStream?.active; + } + + /** + * Plays a short test tone through the master output bus, exercising the + * current setSinkId binding. Used by the "Test Sound" button in audio + * settings to confirm that audio is reaching the chosen output device. + */ + async playTestTone(): Promise { + await this.resumeContext(); + if (!this.ctx) return; + const osc = this.ctx.createOscillator(); + osc.frequency.value = 440; + const gain = this.ctx.createGain(); + // Soft envelope to avoid pop on start/stop. + const now = this.ctx.currentTime; + gain.gain.setValueAtTime(0, now); + gain.gain.linearRampToValueAtTime(0.15, now + 0.02); + gain.gain.linearRampToValueAtTime(0, now + 0.4); + osc.connect(gain); + gain.connect(this.masterBoost!); + osc.start(now); + osc.stop(now + 0.45); + } + getContext(): AudioContext | null { return this.ctx; }