// Looping ringtones. Two patterns: // - outgoing: long calling tone, 3s cycle (oscillator only) // - incoming: classic "ring ring" double beep (oscillator), optionally // upgraded to a user-supplied audio file stored in ringtoneStorage // // The custom file plays immediately if we can load it; otherwise we fall // back to the generated oscillator pattern so ringing never misses an // incoming call due to an IO failure. import { getIncomingRingtone } from './ringtoneStorage'; type Pattern = 'outgoing' | 'incoming'; class Ringtone { private ctx: AudioContext | null = null; private interval: number | null = null; private pattern: Pattern | null = null; // Custom-file playback path (incoming only). private audioEl: HTMLAudioElement | null = null; private customUrl: string | null = null; // Sequence token to ignore slow IO completing after user changed state. private startSeq = 0; start(pattern: Pattern): void { if (this.pattern === pattern) return; // already playing this pattern this.stop(); this.pattern = pattern; const seq = ++this.startSeq; if (pattern === 'incoming') { // Kick off oscillator immediately so we never miss ringing feedback // while the custom file (if any) loads asynchronously. Once the blob // is ready we hand playback over to the