226 lines
7.5 KiB
TypeScript
226 lines
7.5 KiB
TypeScript
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()
|
|
}
|