import { KEYS, store } from './storage'; /** * صداهای کوتاهِ رابط. یک AudioContext برای کلِ اپ؛ ساختنِ یکی برای هر صدا * در Safari بعد از چند بار شکست می‌خورد. */ let ctx: AudioContext | null = null; function context(): AudioContext | null { if (ctx) return ctx; const Ctor = window.AudioContext ?? window.webkitAudioContext; if (!Ctor) return null; try { ctx = new Ctor(); } catch { return null; } return ctx; } export function soundEnabled(): boolean { return store.get(KEYS.sound) !== 'off'; } export function setSoundEnabled(on: boolean) { store.set(KEYS.sound, on ? 'on' : 'off'); } function tone(frequency: number, start: number, duration: number, volume = 0.05) { const audio = context(); if (!audio || !soundEnabled()) return; if (audio.state === 'suspended') void audio.resume(); const t = audio.currentTime + start; const osc = audio.createOscillator(); const gain = audio.createGain(); osc.type = 'sine'; osc.frequency.setValueAtTime(frequency, t); gain.gain.setValueAtTime(0.0001, t); gain.gain.exponentialRampToValueAtTime(volume, t + 0.01); gain.gain.exponentialRampToValueAtTime(0.0001, t + duration); osc.connect(gain).connect(audio.destination); osc.start(t); osc.stop(t + duration + 0.02); } /** * لرزشِ کوتاه کنارِ صدا، با همان کلیدِ «صدا» خاموش می‌شود. فقط اندروید/کروم دارد؛ * Safariِ آیفون `vibrate` ندارد و این‌جا هیچ کاری نمی‌کند. */ function buzz(pattern: number | number[]) { if (!soundEnabled() || typeof navigator.vibrate !== 'function') return; try { navigator.vibrate(pattern); } catch { /* بعضی WebViewها بی‌اجازه throw می‌کنند؛ لرزش فقط تزئینی است */ } } export const sounds = { tick: () => tone(720, 0, 0.045, 0.04), correct: () => { tone(660, 0, 0.12); tone(990, 0.1, 0.18); buzz(30); }, wrong: () => { tone(220, 0, 0.25, 0.06); buzz([40, 60, 40]); }, finish: () => [523, 659, 784, 1047].forEach((f, i) => tone(f, i * 0.11, 0.2)), };