/** * 매매 시그널 알림음 */ export type TradeAlertSoundId = | 'silent' | 'bell' | 'chime' | 'alert' | 'success' | 'ding' | 'message' | 'notification' | 'ring' | 'positive' | 'coin' | 'pop' | 'tone' | 'complete'; export const TRADE_ALERT_SOUND_OPTIONS: { id: TradeAlertSoundId; label: string }[] = [ { id: 'bell', label: '벨 (기본)' }, { id: 'chime', label: '차임' }, { id: 'alert', label: '경고' }, { id: 'success', label: '성공' }, { id: 'ding', label: '딩동' }, { id: 'message', label: '메시지' }, { id: 'notification', label: '알림 톤' }, { id: 'ring', label: '전화 벨' }, { id: 'positive', label: '긍정 알림' }, { id: 'coin', label: '코인' }, { id: 'pop', label: '팝' }, { id: 'tone', label: '클린 톤' }, { id: 'complete', label: '완료' }, { id: 'silent', label: '무음' }, ]; const SOUND_URLS: Record, string> = { bell: 'https://assets.mixkit.co/active_storage/sfx/2869/2869-preview.mp3', chime: 'https://assets.mixkit.co/active_storage/sfx/2870/2870-preview.mp3', alert: 'https://assets.mixkit.co/active_storage/sfx/2868/2868-preview.mp3', success: 'https://assets.mixkit.co/active_storage/sfx/2018/2018-preview.mp3', ding: 'https://assets.mixkit.co/active_storage/sfx/2354/2354-preview.mp3', message: 'https://assets.mixkit.co/active_storage/sfx/2357/2357-preview.mp3', notification: 'https://assets.mixkit.co/active_storage/sfx/2359/2359-preview.mp3', ring: 'https://assets.mixkit.co/active_storage/sfx/2356/2356-preview.mp3', positive: 'https://assets.mixkit.co/active_storage/sfx/2000/2000-preview.mp3', coin: 'https://assets.mixkit.co/active_storage/sfx/1998/1998-preview.mp3', pop: 'https://assets.mixkit.co/active_storage/sfx/2355/2355-preview.mp3', tone: 'https://assets.mixkit.co/active_storage/sfx/2867/2867-preview.mp3', complete: 'https://assets.mixkit.co/active_storage/sfx/2001/2001-preview.mp3', }; export function normalizeTradeAlertSoundId(id?: string | null): TradeAlertSoundId { if (!id) return 'bell'; const found = TRADE_ALERT_SOUND_OPTIONS.some(o => o.id === id); return found ? (id as TradeAlertSoundId) : 'bell'; } export function getTradeAlertSoundUrl(soundId: TradeAlertSoundId): string { if (soundId === 'silent') return ''; return SOUND_URLS[soundId] ?? SOUND_URLS.bell; } let lastPlayAt = 0; /** 새 매매 시그널 알림 시 사운드 재생 */ export function playTradeAlertSound(soundId: TradeAlertSoundId, volume = 0.55): void { if (soundId === 'silent') return; const url = getTradeAlertSoundUrl(soundId); if (!url) return; const now = Date.now(); if (now - lastPlayAt < 400) return; lastPlayAt = now; try { const audio = new Audio(url); audio.volume = Math.min(1, Math.max(0, volume)); void audio.play().catch(() => { /* autoplay 정책 등 */ }); } catch { /* ignore */ } } export function previewTradeAlertSound(soundId: TradeAlertSoundId): void { lastPlayAt = 0; playTradeAlertSound(soundId); }