| const VOLUME = 0.176; |
|
|
| let sharedContext: AudioContext | null = null; |
|
|
| function getContext(): AudioContext | null { |
| if (typeof window === 'undefined') return null; |
| if (sharedContext && sharedContext.state !== 'closed') return sharedContext; |
| const AC = window.AudioContext || (window as any).webkitAudioContext; |
| sharedContext = AC ? new AC() : null; |
| return sharedContext; |
| } |
|
|
| export function playTaskCompleteSound() { |
| const ac = getContext(); |
| if (!ac) return; |
|
|
| const t0 = ac.currentTime; |
| const freqs = [523.3, 784]; |
| const spd = 0.11; |
| const sw = 0.080; |
|
|
| const dly = ac.createDelay(1.0); |
| dly.delayTime.value = spd * 1.5; |
| const dlyG = ac.createGain(); |
| dlyG.gain.value = 0.12; |
| const fb = ac.createGain(); |
| fb.gain.value = 0.3; |
| dly.connect(dlyG).connect(ac.destination); |
| dly.connect(fb).connect(dly); |
|
|
| freqs.forEach((freq, i) => { |
| const t = t0 + i * spd + (i % 2 === 1 ? spd * sw : 0); |
| const prev = i > 0 ? freqs[i - 1] : freq; |
|
|
| const o1 = ac.createOscillator(); |
| const o2 = ac.createOscillator(); |
| o1.type = o2.type = 'sine'; |
| o1.frequency.setValueAtTime(prev, t); |
| o1.frequency.exponentialRampToValueAtTime(freq, t + 0.02); |
| o2.frequency.setValueAtTime(prev * 1.004, t); |
| o2.frequency.exponentialRampToValueAtTime(freq * 1.004, t + 0.02); |
|
|
| const flt = ac.createBiquadFilter(); |
| flt.type = 'lowpass'; |
| flt.Q.value = 1; |
| flt.frequency.setValueAtTime(3500, t); |
| flt.frequency.exponentialRampToValueAtTime(3000, t + spd + 0.2); |
|
|
| const g = ac.createGain(); |
| g.gain.setValueAtTime(0, t); |
| g.gain.linearRampToValueAtTime(VOLUME, t + 0.015); |
| g.gain.setValueAtTime(VOLUME, t + spd * 0.4); |
| g.gain.exponentialRampToValueAtTime(0.001, t + spd + 0.2); |
|
|
| const mx = ac.createGain(); |
| mx.gain.value = 0.5; |
| o1.connect(mx); |
| o2.connect(mx); |
| mx.connect(flt); |
| flt.connect(g).connect(ac.destination); |
| g.connect(dly); |
| o1.start(t); |
| o1.stop(t + spd + 0.2 + 0.1); |
| o2.start(t); |
| o2.stop(t + spd + 0.2 + 0.1); |
| }); |
| } |
|
|
| export function playTaskCompleteSoundSubtle() { |
| const ac = getContext(); |
| if (!ac) return; |
|
|
| const t0 = ac.currentTime; |
| const freq = 784; |
| const spd = 0.11; |
|
|
| const o1 = ac.createOscillator(); |
| const o2 = ac.createOscillator(); |
| o1.type = o2.type = 'sine'; |
| o1.frequency.setValueAtTime(freq, t0); |
| o2.frequency.setValueAtTime(freq * 1.004, t0); |
|
|
| const flt = ac.createBiquadFilter(); |
| flt.type = 'lowpass'; |
| flt.Q.value = 1; |
| flt.frequency.setValueAtTime(3500, t0); |
| flt.frequency.exponentialRampToValueAtTime(3000, t0 + spd + 0.2); |
|
|
| const g = ac.createGain(); |
| g.gain.setValueAtTime(0, t0); |
| g.gain.linearRampToValueAtTime(VOLUME, t0 + 0.015); |
| g.gain.setValueAtTime(VOLUME, t0 + spd * 0.4); |
| g.gain.exponentialRampToValueAtTime(0.001, t0 + spd + 0.2); |
|
|
| const mx = ac.createGain(); |
| mx.gain.value = 0.5; |
| o1.connect(mx); |
| o2.connect(mx); |
| mx.connect(flt); |
| flt.connect(g).connect(ac.destination); |
| o1.start(t0); |
| o1.stop(t0 + spd + 0.2 + 0.1); |
| o2.start(t0); |
| o2.stop(t0 + spd + 0.2 + 0.1); |
| } |
|
|