// Alarma corta generada con Web Audio (sin ficheros). Dos pitidos rápidos. let ctx: AudioContext | null = null; export function playAlarm() { try { const Ctor = window.AudioContext || (window as unknown as { webkitAudioContext: typeof AudioContext }).webkitAudioContext; ctx = ctx ?? new Ctor(); if (ctx.state === "suspended") void ctx.resume(); const now = ctx.currentTime; for (let i = 0; i < 2; i++) { const osc = ctx.createOscillator(); const gain = ctx.createGain(); osc.type = "square"; osc.frequency.value = 880; const t = now + i * 0.18; gain.gain.setValueAtTime(0.0001, t); gain.gain.exponentialRampToValueAtTime(0.25, t + 0.01); gain.gain.exponentialRampToValueAtTime(0.0001, t + 0.15); osc.connect(gain); gain.connect(ctx.destination); osc.start(t); osc.stop(t + 0.16); } } catch { // sin audio disponible: ignoramos } } // Parsea "mm", "mm:ss" o "m.5" a segundos. Devuelve null si no es válido. export function parseTime(input: string): number | null { const s = input.trim(); if (!s) return null; if (s.includes(":")) { const [m, sec] = s.split(":"); const mm = Number(m); const ss = Number(sec); if (!Number.isFinite(mm) || !Number.isFinite(ss) || ss < 0 || ss >= 60) return null; const total = Math.round(mm * 60 + ss); return total > 0 ? total : null; } const mins = Number(s); if (!Number.isFinite(mins) || mins <= 0) return null; return Math.round(mins * 60); }