From f7afffba1f71cb3e370b0f84bf8d93df48ae802e Mon Sep 17 00:00:00 2001 From: ciregenz Date: Wed, 5 Aug 2026 19:54:33 -0700 Subject: [PATCH] [eric] voice: start/stop cues are Material's forward/backward-selection-minimal (CC BY 4.0), Eric's pick --- frontend/src/shared/voice/voiceCueSounds.ts | 5 ++ frontend/src/shared/voice/voiceCues.ts | 81 +++++---------------- 2 files changed, 25 insertions(+), 61 deletions(-) create mode 100644 frontend/src/shared/voice/voiceCueSounds.ts diff --git a/frontend/src/shared/voice/voiceCueSounds.ts b/frontend/src/shared/voice/voiceCueSounds.ts new file mode 100644 index 00000000..6228ed36 --- /dev/null +++ b/frontend/src/shared/voice/voiceCueSounds.ts @@ -0,0 +1,5 @@ +// Google Material product sounds (CC BY 4.0, https://m2.material.io/design/sound/sound-resources.html): +// navigation_forward-selection-minimal (start) and navigation_backward-selection-minimal (stop). +// Embedded as data URIs so the packaged file:// + asar build needs no asset paths and no webpack rule. +export const VOICE_CUE_START = 'data:audio/ogg;base64,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'; +export const VOICE_CUE_STOP = 'data:audio/ogg;base64,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'; diff --git a/frontend/src/shared/voice/voiceCues.ts b/frontend/src/shared/voice/voiceCues.ts index 7513f965..85b2cf15 100644 --- a/frontend/src/shared/voice/voiceCues.ts +++ b/frontend/src/shared/voice/voiceCues.ts @@ -1,68 +1,27 @@ -// WhisperFlow-grade start/stop cues, synthesized in WebAudio so they ship weightless and always -// match. Not a chime: a soft percussive "pop" (tiny click transient + falling tone), like a water -// drop. Start pops slightly higher than stop, so the pair reads as press/release, not as an alarm. +import { VOICE_CUE_START, VOICE_CUE_STOP } from './voiceCueSounds'; -let ctx: AudioContext | null = null; +// Eric picked these from Google's Material product sound set after a bake-off against Wispr Flow's +// real cues; the synth versions never survived an ear test. Files are embedded data URIs (see +// voiceCueSounds.ts), pre-instantiated so playback is instant on the press. -function ensureCtx(): AudioContext | null { - try { - if (!ctx || ctx.state === 'closed') ctx = new AudioContext(); - if (ctx.state === 'suspended') void ctx.resume(); - return ctx; - } catch { - return null; +const CUE_VOLUME = 0.35; + +const p_players: Record<'start' | 'stop', HTMLAudioElement | null> = { start: null, stop: null }; + +function player(kind: 'start' | 'stop'): HTMLAudioElement { + let a = p_players[kind]; + if (!a) { + a = new Audio(kind === 'start' ? VOICE_CUE_START : VOICE_CUE_STOP); + a.volume = CUE_VOLUME; + p_players[kind] = a; } -} - -// The transient: 6ms of bandpassed noise gives the pop its tactile "tick" edge. -function tick(ac: AudioContext, at: number, freq: number, peak: number): void { - const len = Math.floor(ac.sampleRate * 0.006); - const buf = ac.createBuffer(1, len, ac.sampleRate); - const data = buf.getChannelData(0); - for (let i = 0; i < len; i++) data[i] = (Math.random() * 2 - 1) * (1 - i / len); - const src = ac.createBufferSource(); - src.buffer = buf; - const bp = ac.createBiquadFilter(); - bp.type = 'bandpass'; - bp.frequency.value = freq; - bp.Q.value = 1.2; - const gain = ac.createGain(); - gain.gain.value = peak; - src.connect(bp); - bp.connect(gain); - gain.connect(ac.destination); - src.start(at); -} - -// The body: a sine gliding down fast with an exponential decay reads as a "plop", not a beep. -function pop(ac: AudioContext, at: number, from: number, to: number, dur: number, peak: number): void { - const osc = ac.createOscillator(); - const gain = ac.createGain(); - const lp = ac.createBiquadFilter(); - lp.type = 'lowpass'; - lp.frequency.value = 3200; - osc.type = 'sine'; - osc.frequency.setValueAtTime(from, at); - osc.frequency.exponentialRampToValueAtTime(to, at + dur * 0.7); - gain.gain.setValueAtTime(0, at); - gain.gain.linearRampToValueAtTime(peak, at + 0.006); - gain.gain.exponentialRampToValueAtTime(0.0004, at + dur); - osc.connect(lp); - lp.connect(gain); - gain.connect(ac.destination); - osc.start(at); - osc.stop(at + dur + 0.02); + return a; } export function playVoiceCue(kind: 'start' | 'stop'): void { - const ac = ensureCtx(); - if (!ac) return; - const t = ac.currentTime + 0.01; - if (kind === 'start') { - tick(ac, t, 2200, 0.05); - pop(ac, t, 760, 520, 0.11, 0.06); - } else { - tick(ac, t, 1600, 0.04); - pop(ac, t, 520, 360, 0.13, 0.055); - } + try { + const a = player(kind); + a.currentTime = 0; + void a.play(); + } catch { /* a missing audio device must never break dictation */ } }