// stores/counter.js import { defineStore, type _ActionsTree, type StoreDefinition, type StateTree } from 'pinia' import { type AudioNodeItem } from '../AudioNodeItem' import { useUserStore } from '../../user' import { connectAudioNodes } from '~/lib/AudioFunctions' import type Player from '~/components/Player/Player' export const useAudioStore: StoreDefinition = defineStore<'audio', StateTree, _ActionsTree>('audio', { state: () => { return { audioContext: new AudioContext() as AudioContext, nodes: new Map() as Map, playing: false, scene: 'Lagoon', acusticCheckResult: 0, scenePlayer: null as Player | null, noisePlayer: null as Player | null } }, actions: { setPlayers ({ scenePlayer, noisePlayer }:any) { this.scenePlayer = scenePlayer this.noisePlayer = noisePlayer }, // // Audio Context Actions // isPlaying (): boolean { return this.playing }, pauseContext (): void { const audioContext = this.getAudioContext() if (audioContext) { audioContext.playbackRate = 0 audioContext.suspend() } }, async getAudioContext () : Promise { // useNuxtApp().$logger.log('getAudioContext') if (Howler.ctx) { // useNuxtApp().$logger.log('Audio Context is there...') if (Howler.ctx.state === 'suspended') { Howler.ctx.resume() } return Howler.ctx } else { return await this.initializeAudioContext() } }, getContext () : AudioContext { // useNuxtApp().$logger.log('getContext') if (this.audioContext) { return this.audioContext } else { this.audioContext = new AudioContext() return this.audioContext } }, async initializeAudioContext (): Promise { // useNuxtApp().$logger.log('initializeAudioContext') return await this.audioContext }, resetAudioContext () { if (Howler.ctx) { // useNuxtApp().$logger.log('AudioContext has been reset') Howler.unload() } else if (this.audioContext instanceof AudioContext && this.audioContext.state !== 'closed') { try { Howler.stop() Howler.unload() this.audioContext = null this.setPlaying(false) // useNuxtApp().$logger.log('AudioContext has been reset') } catch (_error) { // useNuxtApp().$logger.error('Error closing AudioContext:', _error) } } }, setPlaying (stateChange: boolean): void { this.playing = stateChange }, // // Working with Web Audio Nodes // removeNodeByType (name: string): void { if (this.nodes && this.nodes.has(name)) { this.nodes.delete(name) } }, getNodeByType (name: string): AudioNodeItem | null { if (this.nodes instanceof Map) { return this.nodes.get(name) as AudioNodeItem } return null }, getMicrophoneNode (): AudioNodeItem | null { return this.nodes.get('microphone') }, async getBufferSourceNode (name: string): Promise { const node:AudioNodeItem = this.nodes.has(name) if (node) { // useNuxtApp().$logger.log('Node gibts schon') return node } else { await this.createBufferSource(name) const createdNode = this.getNodeByType(name) // useNuxtApp().$logger.log('created node ', { createdNode }) return createdNode } }, async createMediaStreamNodeAsync (steam: MediaStream): Promise { const audioCtx: AudioContext = await this.getAudioContext() // useNuxtApp().$logger.log({ audioCtx }) const streamNode = audioCtx.createMediaStreamSource(steam) this.addNode('microphone', streamNode) return streamNode }, createMediaStreamNode (steam: MediaStream, audioContext: AudioContext): MediaStreamAudioSourceNode { const audioCtx: AudioContext = audioContext // useNuxtApp().$logger.log({ audioCtx }) const streamNode = audioCtx.createMediaStreamSource(steam) this.addNode('microphone', streamNode) return streamNode }, async createBufferSource (name:string): Promise { const bufferStore = this.bufferStore() const audioBuffer = await bufferStore.getAudioBufferByName(name) // if (!audioBuffer) { useNuxtApp().$logger.log({ bufferStore }) } const audioCtx = await this.getAudioContext() const abn = audioCtx.createBufferSource() abn.buffer = audioBuffer const result = this.addNode(name, abn) return result }, disconnectAllNodes (): void { if (this.nodes.length > 0) { this.nodes.forEach((n:AudioNodeItem) => n.node?.disconnect()) this.nodes = {} as Map } }, startNode (node:AudioNodeItem) { if (node.node instanceof AudioBufferSourceNode) { node.node?.start(0) // useNuxtApp().$logger.log('started Node' + node.type, { nod1 }) } else { // useNuxtApp().$logger.error('Node not defined in start node ') } }, addNode (type: string, node: AudioNode): AudioNodeItem { this.nodes.set(type, { type, node, started: false }) // useNuxtApp().$logger.log('adding node', { values: [...this.nodes.values()] }) return { type, node, started: false } as AudioNodeItem }, connectNodes (nodeFrom: AudioNodeItem, nodeTo: AudioNodeItem, channelOutput?: number, channelInput?:number): void { if (nodeFrom && nodeTo && !channelInput && !channelOutput) { connectAudioNodes(nodeFrom.node as AudioNode, nodeTo.node as AudioNode) } if (nodeFrom && nodeTo && channelOutput && !channelInput) { connectAudioNodes(nodeFrom.node as AudioNode, nodeTo.node as AudioNode, channelOutput) } if (nodeFrom && nodeTo && channelOutput && channelInput) { connectAudioNodes(nodeFrom.node as AudioNode, nodeTo.node as AudioNode, channelOutput, channelInput) } // useNuxtApp().$logger.log('nodeTo tpye: ' + nodeTo.type) if (nodeTo.type) { nodeFrom.nextNodeName = nodeTo.type } nodeTo.previousNodeName = nodeFrom.type }, startAllSources () : void { const app = useNuxtApp() // @ts-ignore const user = useUserStore(app.$pinia) const userObj = user.user as any // useNuxtApp().$logger.log({ userObj }) this.nodes.forEach((node: AudioNodeItem, key: string) => { const userSoundscape = userObj.settings.soundscape === 'Tropics' ? 'tropics' : userObj.settings.soundscape || 'meadow' if (key === userSoundscape || key === 'noise') { if (node.node instanceof AudioBufferSourceNode && !node.started) { this.startBufferSourceNode(node.node) } } }) }, startBufferSourceNode (node: AudioBufferSourceNode) { // useNuxtApp().$logger.log('startBufferSourceNode ') this.nodes.forEach((value: AudioNodeItem) => { if (value.node === node) { const c = value.node as AudioBufferSourceNode // useNuxtApp().$logger.log('AudioContext ' + audioContext.state) c.start(0) value.started = true } }) }, replaceMusicNode (oldNode: AudioNodeItem, newNode: AudioNodeItem):AudioNodeItem { if (!this.nodes.has(oldNode.type) || !this.nodes.has(newNode.type)) { // this.$logger.warn('AudioNodes not present in nodes Map') } if (oldNode.node ! instanceof AudioBufferSourceNode || oldNode.node ! instanceof AudioBufferSourceNode) { // createLogger().warn('AudioNodes not AudioBufferSourceNodes') } const nextNode = oldNode.nextNodeName ? this.nodes.get(oldNode.nextNodeName) : null if (!nextNode || (nextNode && nextNode.node ! instanceof ChannelSplitterNode)) { // createLogger().warn('AudioNodes not ChannelSplitterNodes') } else { oldNode.node?.disconnect() oldNode.started = false oldNode = {} as AudioNodeItem if (newNode && newNode.node) { if (nextNode.type) { newNode.nextNodeName = nextNode.type as string } newNode.node.connect(nextNode?.node as AudioNode) return newNode } } return newNode } } })