This commit is contained in:
2026-04-18 23:11:35 +02:00
commit f7cfd2a86e
196 changed files with 35538 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
// Looping WebAudio ringtones. Two patterns:
// - outgoing: long calling tone, 3s cycle
// - incoming: classic "ring ring" double beep, 2s cycle
type Pattern = 'outgoing' | 'incoming';
class Ringtone {
private ctx: AudioContext | null = null;
private interval: number | null = null;
private pattern: Pattern | null = null;
start(pattern: Pattern): void {
if (this.pattern === pattern) return; // already playing this pattern
this.stop();
const AudioCtx =
window.AudioContext ??
(window as unknown as { webkitAudioContext?: typeof AudioContext }).webkitAudioContext;
if (!AudioCtx) return;
this.ctx = new AudioCtx();
this.pattern = pattern;
const play = pattern === 'outgoing' ? this.playOutgoing : this.playIncoming;
play.call(this);
this.interval = window.setInterval(
() => play.call(this),
pattern === 'outgoing' ? 3000 : 2000,
);
}
stop(): void {
if (this.interval !== null) {
window.clearInterval(this.interval);
this.interval = null;
}
if (this.ctx) {
void this.ctx.close().catch(() => {
/* ignore */
});
this.ctx = null;
}
this.pattern = null;
}
private beep(freq: number, durationSec: number, delaySec: number, gain = 0.18): void {
const ctx = this.ctx;
if (!ctx) return;
const osc = ctx.createOscillator();
const g = ctx.createGain();
osc.type = 'sine';
osc.frequency.value = freq;
osc.connect(g);
g.connect(ctx.destination);
const t0 = ctx.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);
}
private playOutgoing(): void {
// Soft calling tone — single warm note.
this.beep(440, 0.4, 0, 0.14);
this.beep(440, 0.4, 0.6, 0.14);
}
private playIncoming(): void {
// Classic double-ring "ring ring".
this.beep(880, 0.18, 0, 0.22);
this.beep(660, 0.18, 0.22, 0.22);
this.beep(880, 0.18, 0.6, 0.22);
this.beep(660, 0.18, 0.82, 0.22);
}
}
export const ringtone = new Ringtone();