fix: warm up AudioContext on first click, clean up code

This commit is contained in:
Mikhail Kiselev
2026-02-10 17:54:31 +03:00
parent 6dac14e0c1
commit 5131a72983
2 changed files with 62 additions and 46 deletions

View File

@@ -40,15 +40,9 @@
return audioContext
}
async function playBeep(frequency, duration, type = 'sine') {
function playBeep(frequency, duration, type = 'sine') {
try {
const ctx = getAudioContext()
// Resume context if suspended (required by browsers on first interaction)
if (ctx.state === 'suspended') {
await ctx.resume()
// Wait a frame for currentTime to update after resume
await new Promise(resolve => setTimeout(resolve, 50))
}
const oscillator = ctx.createOscillator()
const gainNode = ctx.createGain()
@@ -58,20 +52,17 @@
oscillator.frequency.value = frequency
oscillator.type = type
// Use small offset to ensure sound is scheduled in the future
const startTime = ctx.currentTime + 0.01
gainNode.gain.setValueAtTime(0.3, startTime)
gainNode.gain.exponentialRampToValueAtTime(0.01, startTime + duration)
gainNode.gain.setValueAtTime(0.3, ctx.currentTime)
gainNode.gain.exponentialRampToValueAtTime(0.01, ctx.currentTime + duration)
oscillator.start(startTime)
oscillator.stop(startTime + duration)
oscillator.start(ctx.currentTime)
oscillator.stop(ctx.currentTime + duration)
} catch (e) {
console.error('Failed to play sound:', e)
alert('Sound error: ' + e.message)
}
}
async function testSound(name) {
function testSound(name) {
// If custom sound exists, play it
if (customSounds[name]) {
playCustomSound(name)
@@ -81,16 +72,16 @@
// Otherwise play default beep sounds
switch (name) {
case 'warning':
await playBeep(880, 0.15)
playBeep(880, 0.15)
setTimeout(() => playBeep(880, 0.15), 200)
break
case 'timeup':
await playBeep(1200, 0.2)
playBeep(1200, 0.2)
setTimeout(() => playBeep(900, 0.2), 250)
setTimeout(() => playBeep(600, 0.3), 500)
break
case 'meeting_end':
await playBeep(523, 0.2)
playBeep(523, 0.2)
setTimeout(() => playBeep(659, 0.2), 200)
setTimeout(() => playBeep(784, 0.4), 400)
break
@@ -172,9 +163,29 @@
updateComplete = true
})
// Warm up AudioContext on first user interaction (for sound tests)
const warmUpAudio = async () => {
const ctx = getAudioContext()
if (ctx.state === 'suspended') {
await ctx.resume()
}
// Play silent sound to fully unlock audio
const oscillator = ctx.createOscillator()
const gainNode = ctx.createGain()
oscillator.connect(gainNode)
gainNode.connect(ctx.destination)
gainNode.gain.value = 0 // Silent
oscillator.start()
oscillator.stop(ctx.currentTime + 0.001)
// Remove listener after first interaction
document.removeEventListener('click', warmUpAudio)
}
document.addEventListener('click', warmUpAudio)
return () => {
EventsOff('update:progress')
EventsOff('update:complete')
document.removeEventListener('click', warmUpAudio)
}
})