import { defineStore } from 'pinia' import type { Logger } from 'pino' interface State { audioContext: AudioContext | null, masterGainNoise: GainNode | null, masterGainMusic: GainNode | null, audioSink: typeof Audio | null, showOverlay: boolean, monitoringInterval: NodeJS.Timeout | null, playing: boolean, scene: string, state: string | null, volume: number, noiseVolume: number } function createOverlay (audioContext: AudioContext) { const logger = useNuxtApp().$logger as Logger logger.info('Create Overlay') const overlay = document.createElement('div') overlay.className = 'overlay' // Erstelle den Button const button = document.createElement('button') button.className = 'btn btn-primary btn-lg' button.style.backgroundColor = '#e9c046' button.style.border = 'none' button.style.color = 'black' button.innerText = 'Ready' // Füge Event-Listener hinzu, um den AudioContext zu entsperren button.addEventListener('click', async () => { try { await audioContext.resume() logger.info('AudioContext resumed.') overlay.remove() // Entferne das Overlay nach der Interaktion } catch (error) { useAudioStore().resetAudioContext() useAudioStore().initializeAudioContext() logger.error('Error resuming AudioContext:', error) } }) // Füge Button dem Overlay hinzu overlay.appendChild(button) // Füge Overlay zum DOM hinzu document.body.appendChild(overlay) } export const useAudioStore = defineStore('audio', { state: (): State => ({ audioContext: null as AudioContext | null, masterGainNoise: null as GainNode | null, masterGainMusic: null as GainNode | null, audioSink: null as typeof Audio | null, showOverlay: false, monitoringInterval: null as NodeJS.Timeout | null, playing: false, scene: 'Lagoon', state: null as string | null, volume: parseFloat(localStorage.getItem('volume') || '1'), noiseVolume: parseFloat(localStorage.getItem('noiseVolume') || '1') }), actions: { getNewAudioElement (newTrack: string) { const logger = useNuxtApp().$logger as Logger logger.info(`Current track changed to: ${newTrack}`) return useMediaProvider().getAudioElementForTitle(newTrack) // Additional logic for changing tracks can be added here // For example, you might want to stop the current audio, load the new track, etc. // This depends on how your audio playback is implemented }, async ensureAudioContextRunning () { if (!this.audioContext) { this.initializeAudioContext() } if (this.audioContext?.state === 'suspended') { try { await this.audioContext.resume() } catch (error) { const logger = useNuxtApp().$logger as Logger logger.error('Error resuming AudioContext:', error) this.showOverlay = true } } }, initializeAudioContext () { const logger = useNuxtApp().$logger as Logger if (!this.audioContext) { this.audioContext = new AudioContext() this.prepareGains(this.audioContext) logger.info('AudioContext initialized and Gains prepared.') // Attach some error handling, to ensure audiocontext is running all the time. this.startAudioContextMonitor() this.addStateMonitor() } }, prepareGains(audioContext: AudioContext){ this.masterGainNoise = null this.masterGainMusic = null this.masterGainNoise = audioContext.createGain() this.masterGainMusic = audioContext.createGain() this.masterGainNoise.connect(audioContext.destination) this.masterGainMusic.connect(audioContext.destination) }, addStateMonitor () { const logger = useNuxtApp().$logger as Logger if (!this.audioContext) { this.initializeAudioContext() return } // Überwache Änderungen im AudioContext-State this.audioContext.onstatechange = () => { const currentState = this.audioContext?.state logger.info('AudioContext state changed:', currentState) if (currentState === 'suspended') { createOverlay(this.getContext()) } } // Optional: Überwache Änderungen in den Audioausgabe-Sinks if ('onsinkchange' in this.audioContext) { this.audioContext.onsinkchange = () => { logger.info('Audio sink configuration has changed.') // Füge hier zusätzliche Logik hinzu, falls erforderlich } } }, getMasterGainNoise (): GainNode { if(this.masterGainNoise) { return this.masterGainNoise } else { const context = this.getContext() this.masterGainNoise = context.createGain() this.masterGainNoise.connect(context.destination) return this.masterGainNoise } }, getContext (): AudioContext { if (!this.audioContext ) { this.audioContext ||= new AudioContext() } return this.audioContext }, async resumeAudioContext () { const logger = useNuxtApp().$logger as Logger if (this.audioContext?.state === 'suspended') { try { await this.audioContext.resume() this.state = this.audioContext.state const state = this.state logger.info('AudioContext resumed. ', { state }) this.stopAudioContextMonitor() } catch (error) { logger.error('Error resuming AudioContext:', error) this.resetAudioContext() this.initializeAudioContext() await this.ensureAudioContextRunning() } } }, togglePlaying (): void { this.playing = !this.playing }, isPlaying (): boolean { return this.playing }, setPlaying (stateChange: boolean): void { this.playing = stateChange }, setVolume (newVolume: number) { this.volume = newVolume localStorage.setItem('volume', newVolume.toString()) }, setNoiseVolume (newVolume: number) { this.noiseVolume = newVolume localStorage.setItem('noiseVolume', newVolume.toString()) }, startAudioContextMonitor () { const logger = useNuxtApp().$logger as Logger // Starte ein Intervall, wenn keines aktiv ist if (!this.monitoringInterval) { this.monitoringInterval = setInterval(() => { if (this.audioContext?.state === 'suspended') { logger.info('AudioContext is suspended. Attempting to resume...') this.audioContext.resume() } }, 500) // Überprüft alle 500ms logger.info('AudioContext monitoring started.') } }, stopAudioContextMonitor () { const logger = useNuxtApp().$logger as Logger // Stoppe das Intervall, falls es läuft if (this.monitoringInterval) { clearInterval(this.monitoringInterval) this.monitoringInterval = null logger.info('AudioContext monitoring stopped.') } }, resetAudioContext () { const logger = useNuxtApp().$logger as Logger if (this.audioContext) { this.audioContext.close() this.audioContext = null logger.info('AudioContext has been closed.') } } }, getters: { getPlaying: state => state.playing, getVolume: state => state.volume, getNoiseVolume: state => state.noiseVolume } }) // Provide a method to ensure audio context is running export const ensureAudio = async () => { const audioStore = useAudioStore() await audioStore.ensureAudioContextRunning() }