/** * Pre-generated notification sounds for the robot. * * All sounds are raw S16LE mono PCM at 16 kHz, ready to pass to AudioService.play(). * Generated once at import time — zero runtime cost per playback. */ const SAMPLE_RATE = 16_000; /** * Generate a sine wave tone with attack/release envelope to prevent speaker clicks. */ function generateTone(freqHz: number, durationMs: number, amplitude = 0.5): Buffer { const sampleCount = Math.floor((SAMPLE_RATE * durationMs) / 1000); const buf = Buffer.alloc(sampleCount * 2); // 16-bit = 2 bytes per sample const amp = Math.max(0, Math.min(1, amplitude)) * 32767; const twoPiF = (2 * Math.PI * freqHz) / SAMPLE_RATE; // 5ms linear attack/release envelope const rampSamples = Math.floor((SAMPLE_RATE * 5) / 1000); for (let i = 0; i < sampleCount; i++) { let env = 1; if (i < rampSamples) env = i / rampSamples; else if (i > sampleCount - rampSamples) env = (sampleCount - i) / rampSamples; const s = Math.round(Math.sin(i * twoPiF) * amp * env); buf.writeInt16LE(Math.max(-32768, Math.min(32767, s)), i * 2); } return buf; } /** * Concatenate multiple PCM buffers with silence gaps between them. */ function concat(parts: Buffer[], gapMs = 0): Buffer { if (gapMs <= 0) return Buffer.concat(parts); const gapBytes = Math.floor((SAMPLE_RATE * gapMs) / 1000) * 2; const silence = Buffer.alloc(gapBytes); const result: Buffer[] = []; for (let i = 0; i < parts.length; i++) { result.push(parts[i]); if (i < parts.length - 1) result.push(silence); } return Buffer.concat(result); } // ── Pre-generated sounds ────────────────────────────────────────── /** * Short rising two-tone chirp: "bi-bip!" (80ms + 80ms) * Played when a remote trigger starts a conversation. */ export const SOUND_TRIGGER = concat( [ generateTone(660, 80, 0.4), // E5 generateTone(880, 80, 0.4), // A5 ], 30, // 30ms gap ); /** * Single soft beep for generic notifications. */ export const SOUND_NOTIFY = generateTone(440, 120, 0.3); /** * Three descending tones for errors. */ export const SOUND_ERROR = concat( [ generateTone(880, 100, 0.35), generateTone(660, 100, 0.35), generateTone(440, 150, 0.35), ], 40, );