Attempt to fix audio playing on iOS
This commit is contained in:
@@ -54,7 +54,7 @@ export function useAudio() {
|
|||||||
globalAudioContext = new AudioContext()
|
globalAudioContext = new AudioContext()
|
||||||
audioContext.value = globalAudioContext
|
audioContext.value = globalAudioContext
|
||||||
}
|
}
|
||||||
|
|
||||||
if (globalAudioContext.state === 'suspended') {
|
if (globalAudioContext.state === 'suspended') {
|
||||||
try {
|
try {
|
||||||
await globalAudioContext.resume()
|
await globalAudioContext.resume()
|
||||||
@@ -62,6 +62,20 @@ export function useAudio() {
|
|||||||
console.warn('AudioContext resume failed, user interaction required:', error)
|
console.warn('AudioContext resume failed, user interaction required:', error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Play a silent buffer to truly unlock AudioContext on iOS PWA
|
||||||
|
// On iOS, resume() alone is insufficient — audio must be routed through the context during a user gesture
|
||||||
|
if (globalAudioContext.state === 'running') {
|
||||||
|
try {
|
||||||
|
const silentBuffer = globalAudioContext.createBuffer(1, 1, 22050)
|
||||||
|
const source = globalAudioContext.createBufferSource()
|
||||||
|
source.buffer = silentBuffer
|
||||||
|
source.connect(globalAudioContext.destination)
|
||||||
|
source.start(0)
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Silent buffer unlock failed:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load a single sound file
|
// Load a single sound file
|
||||||
@@ -94,8 +108,7 @@ export function useAudio() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
console.log('Starting to load all sounds...')
|
console.log('Starting to load all sounds...')
|
||||||
soundsLoaded = true
|
|
||||||
|
|
||||||
// Load basic sounds
|
// Load basic sounds
|
||||||
const basicSounds = {
|
const basicSounds = {
|
||||||
intro: '/sounds/intro.wav',
|
intro: '/sounds/intro.wav',
|
||||||
@@ -135,22 +148,30 @@ export function useAudio() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
soundsLoaded = true
|
||||||
console.log('All sounds loaded and ready to play')
|
console.log('All sounds loaded and ready to play')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading sounds:', error)
|
console.error('Error loading sounds:', error)
|
||||||
|
// Don't set soundsLoaded so a retry can happen on next play attempt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Play a sound buffer
|
// Play a sound buffer
|
||||||
const playSoundBuffer = async (buffer: AudioBuffer) => {
|
const playSoundBuffer = async (buffer: AudioBuffer) => {
|
||||||
if (!appStore.settings.soundEnabled) return
|
if (!appStore.settings.soundEnabled) return
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await initAudioContext()
|
await initAudioContext()
|
||||||
if (!globalAudioContext) {
|
if (!globalAudioContext) {
|
||||||
console.error('AudioContext not initialized')
|
console.error('AudioContext not initialized')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If AudioContext exists but sounds never loaded successfully, retry
|
||||||
|
if (!soundsLoaded) {
|
||||||
|
await loadAllSounds()
|
||||||
|
}
|
||||||
|
|
||||||
const source = globalAudioContext.createBufferSource()
|
const source = globalAudioContext.createBufferSource()
|
||||||
source.buffer = buffer
|
source.buffer = buffer
|
||||||
source.connect(globalAudioContext.destination)
|
source.connect(globalAudioContext.destination)
|
||||||
@@ -411,17 +432,22 @@ export function useAudio() {
|
|||||||
audioSystemInitialized = true
|
audioSystemInitialized = true
|
||||||
|
|
||||||
// Set up user gesture listeners to initialize audio and load sounds
|
// Set up user gesture listeners to initialize audio and load sounds
|
||||||
|
// Include touchstart for iOS PWA standalone mode where it fires before click
|
||||||
|
const gestureEvents = ['click', 'keydown', 'touchstart'] as const
|
||||||
const initializeAudio = async () => {
|
const initializeAudio = async () => {
|
||||||
|
// Remove all gesture listeners immediately so this only fires once
|
||||||
|
for (const event of gestureEvents) {
|
||||||
|
document.removeEventListener(event, initializeAudio)
|
||||||
|
}
|
||||||
console.log('User interaction detected, initializing audio system...')
|
console.log('User interaction detected, initializing audio system...')
|
||||||
await initAudioOnUserGesture()
|
await initAudioOnUserGesture()
|
||||||
await loadAllSounds() // Load sounds after user interaction
|
await loadAllSounds() // Load sounds after user interaction
|
||||||
console.log('Audio system initialized')
|
console.log('Audio system initialized')
|
||||||
document.removeEventListener('click', initializeAudio)
|
|
||||||
document.removeEventListener('keydown', initializeAudio)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('click', initializeAudio, { once: true })
|
for (const event of gestureEvents) {
|
||||||
document.addEventListener('keydown', initializeAudio, { once: true })
|
document.addEventListener(event, initializeAudio)
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize voices for speech synthesis
|
// Initialize voices for speech synthesis
|
||||||
if ('speechSynthesis' in window) {
|
if ('speechSynthesis' in window) {
|
||||||
|
|||||||
Reference in New Issue
Block a user