This commit is contained in:
2026-04-18 23:11:35 +02:00
commit f7cfd2a86e
196 changed files with 35538 additions and 0 deletions
+73
View File
@@ -0,0 +1,73 @@
// One-shot call sound effects via WebAudio. Generated on the fly so no
// binary assets needed. Intentionally short + low-volume — these fire
// multiple times per call and shouldn't feel intrusive.
type Sfx = 'join' | 'leave' | 'end';
let ctx: AudioContext | null = null;
function getCtx(): AudioContext | null {
if (ctx) return ctx;
const AudioCtx =
window.AudioContext ??
(window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
if (!AudioCtx) return null;
ctx = new AudioCtx();
return ctx;
}
function beep(freq: number, durationSec: number, delaySec: number, gain = 0.15): void {
const c = getCtx();
if (!c) return;
const osc = c.createOscillator();
const g = c.createGain();
osc.type = 'sine';
osc.frequency.value = freq;
osc.connect(g);
g.connect(c.destination);
const t0 = c.currentTime + delaySec;
g.gain.setValueAtTime(0, t0);
g.gain.linearRampToValueAtTime(gain, t0 + 0.02);
g.gain.exponentialRampToValueAtTime(0.001, t0 + durationSec);
osc.start(t0);
osc.stop(t0 + durationSec + 0.02);
}
export async function playSfx(kind: Sfx): Promise<void> {
const c = getCtx();
if (!c) return;
if (c.state === 'suspended') {
try {
await c.resume();
} catch {
/* ignore */
}
}
switch (kind) {
case 'join':
// Rising two-note chirp — someone entered.
beep(523.25, 0.12, 0, 0.16); // C5
beep(783.99, 0.18, 0.1, 0.16); // G5
break;
case 'leave':
// Falling two-note — someone left.
beep(659.25, 0.12, 0, 0.14); // E5
beep(329.63, 0.18, 0.1, 0.14); // E4
break;
case 'end':
// Soft descending thud — call ended.
beep(440, 0.18, 0, 0.14); // A4
beep(293.66, 0.26, 0.14, 0.14); // D4
break;
}
}
export function playJoinBeep(): Promise<void> {
return playSfx('join');
}
export function playLeaveBeep(): Promise<void> {
return playSfx('leave');
}
export function playEndBeep(): Promise<void> {
return playSfx('end');
}