Initial commit
This commit is contained in:
498
archive/components/tests/AudioNoiseTest.vue
Normal file
498
archive/components/tests/AudioNoiseTest.vue
Normal file
@@ -0,0 +1,498 @@
|
||||
<template>
|
||||
<div class="rnboplayer">
|
||||
<button v-if="preparedForWebAudio" @click="skip('next')">
|
||||
skip sound
|
||||
</button>
|
||||
<button v-if="preparedForWebAudio" @click="skip('previous')">
|
||||
previous sound
|
||||
</button>
|
||||
|
||||
<button v-if="preparedForWebAudio" @click="mute">
|
||||
mute AudioElements
|
||||
</button>
|
||||
<button v-if="preparedForWebAudio" @click="connectMusicDevice">
|
||||
connect Music
|
||||
</button>
|
||||
<button v-if="preparedForWebAudio" @click="connectNoiseNode">
|
||||
connect Noise & Music
|
||||
</button>
|
||||
<button v-if="preparedForWebAudio" @click="disconnectNodes">
|
||||
disconnect Nodes
|
||||
</button>
|
||||
<button v-if="preparedForWebAudio" @click="disconnectMusicNodes">
|
||||
disconnnect Music Nodes
|
||||
</button>
|
||||
<AudioElement
|
||||
ref="Noise"
|
||||
key="5"
|
||||
:src="sourceNoiseURL"
|
||||
title="Noise"
|
||||
@volume:update="changeNoiseVolume"
|
||||
>
|
||||
<template #default="{}">
|
||||
<!-- tropic icon -->
|
||||
<img style="width: 25px" src="~/assets/image/noiseicon.svg">
|
||||
</template>
|
||||
<!-- Slot content for AudioElement, if needed -->
|
||||
</AudioElement>
|
||||
<AudioElement
|
||||
v-if="selectedAudioTitle"
|
||||
:ref="selectedAudio.title"
|
||||
:key="selectedAudio.id"
|
||||
:src="selectedAudio.src"
|
||||
:title="selectedAudio.title"
|
||||
@update:playing="handlePlayingUpdate"
|
||||
@update:paused="handlePausedUpdate"
|
||||
@volume:update="changeMusicVolume"
|
||||
>
|
||||
<template #default="{ }">
|
||||
<div class="icon">
|
||||
<!-- tropic icon -->
|
||||
<img style="width: 25px" src="~/assets/image/musicicon.svg">
|
||||
</div>
|
||||
</template>
|
||||
<!-- Slot content for AudioElement, if needed -->
|
||||
</AudioElement>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import AudioElement from '@/components/experiments/AudioElement.vue'
|
||||
import importedMusicPatcher from '@/assets/patch/music_patch.export.json'
|
||||
import importedNoisePatcher from '@/assets/patch/noise_patch.export.json'
|
||||
import importedTestPatcher from '@/assets/patch/test_patch.export.json'
|
||||
import { createRNBODevice, getAttenuationFactor } from '@/lib/AudioFunctions'
|
||||
import { useAudioStore } from '~/stores/audio'
|
||||
import { useUserStore } from '~/stores/user'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
AudioElement
|
||||
},
|
||||
props: {
|
||||
soundscape: {
|
||||
type: String,
|
||||
default: 'Lagoon'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
audioList: [
|
||||
{ id: 1, title: 'Lagoon', src: window.location.origin + '/sounds/lagoon.m4a' },
|
||||
{ id: 2, title: 'Forest', src: window.location.origin + '/sounds/forest.m4a' },
|
||||
{ id: 3, title: 'Meadow', src: window.location.origin + '/sounds/meadow.m4a' },
|
||||
{ id: 4, title: 'Tropics', src: window.location.origin + '/sounds/tropics.m4a' }
|
||||
],
|
||||
noise: { id: 5, title: 'Noise', src: window.location.origin + '/masking/noise.flac' },
|
||||
selectedAudioTitle: '',
|
||||
currentElement: {},
|
||||
createdNodes: {},
|
||||
createdNodesCount: 0,
|
||||
noiseRNBODevice: {},
|
||||
musicPatch: importedMusicPatcher,
|
||||
noisePatch: importedTestPatcher,
|
||||
audioContext: null,
|
||||
toggleMute: false,
|
||||
readyPlayer: false,
|
||||
createMusicNodeCounter: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
selectedAudio () {
|
||||
return this.audioList.find(audio => audio.title === this.selectedAudioTitle) || null
|
||||
},
|
||||
isPlaying () {
|
||||
return useAudioStore().isPlaying()
|
||||
},
|
||||
preparedForWebAudio () {
|
||||
return this.areAllNodesAvailable()
|
||||
},
|
||||
sourceNoiseURL () {
|
||||
return window.location.origin + '/masking/noise.flac'
|
||||
}
|
||||
},
|
||||
onRenderTriggered () {
|
||||
// useNuxtApp().$logger.log('render AudioNoiseTest-' + props.title)
|
||||
},
|
||||
watch: {
|
||||
createdNodesCount: {
|
||||
handler () {
|
||||
if (this.areAllNodesAvailable()) {
|
||||
// useNuxtApp().$logger.log('we are complete with the node generation , lets start the noise once ')
|
||||
this.connectNoiseNode()
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
beforeUnmount () {
|
||||
this.disconnectNodes()
|
||||
if (this.audioContext) { this.audioContext.close() }
|
||||
this.audioContext = null
|
||||
},
|
||||
beforeMount () {
|
||||
this.audioContext = useAudioStore().getContext()
|
||||
},
|
||||
mounted () {
|
||||
this.selectAudioByTitle(this.audioList[0].title)
|
||||
this.currentElement = this.audioList[0]
|
||||
this.addUserNavigationHandling()
|
||||
this.musicPatch = importedMusicPatcher
|
||||
this.noisePatch = importedNoisePatcher
|
||||
this.testPatch = importedTestPatcher
|
||||
},
|
||||
|
||||
methods: {
|
||||
unmute () {
|
||||
const musicAudioElement = this.$refs[this.currentElement.title].audioElement
|
||||
const noiseAudioElement = this.$refs.Noise.audioElement
|
||||
noiseAudioElement.volume = 1
|
||||
musicAudioElement.volume = 1
|
||||
musicAudioElement.muted = false
|
||||
noiseAudioElement.muted = false
|
||||
const musicGain = this.createdNodes.musicGain
|
||||
const audioContext = this.createdNodes.musicGain.context
|
||||
const noiseGain = this.createdNodes.musicGain
|
||||
if (musicGain.gain.value === 0) { musicGain.gain.linearRampToValueAtTime(1, audioContext.currentTime + 10) }
|
||||
noiseGain.gain.linearRampToValueAtTime(1, audioContext.currentTime + 10)
|
||||
useAudioStore().setPlaying(false)
|
||||
},
|
||||
changeMusicVolume (newValue) {
|
||||
if (!this.createdNodes.musicGain.gain) { return }
|
||||
// useNuxtApp().$logger.log(this.createdNodes.musicGain.gain)
|
||||
this.createdNodes.musicGain.gain.cancelScheduledValues(this.createdNodes.musicGain.context.currentTime)
|
||||
this.createdNodes.musicGain.gain.linearRampToValueAtTime(newValue / 100, this.createdNodes.musicGain.context.currentTime + 0.30)
|
||||
},
|
||||
changeNoiseVolume (newValue) {
|
||||
const noiseGain = this.createdNodes.noiseGain
|
||||
if (!noiseGain) { return }
|
||||
noiseGain.gain.cancelScheduledValues(noiseGain.context.currentTime)
|
||||
noiseGain.gain.linearRampToValueAtTime(newValue / 100, noiseGain.context.currentTime + 0.30)
|
||||
},
|
||||
addUserNavigationHandling () {
|
||||
if ('mediaSession' in navigator) {
|
||||
// Set the handler for the next track action
|
||||
navigator.mediaSession.setActionHandler('nexttrack', () => {
|
||||
this.skip('next')
|
||||
})
|
||||
|
||||
// Set the handler for the previous track action
|
||||
navigator.mediaSession.setActionHandler('previoustrack', () => {
|
||||
this.skip('previous')
|
||||
})
|
||||
}
|
||||
},
|
||||
getSong (currentTitle, direction) {
|
||||
const index = this.audioList.findIndex(song => song.title === currentTitle)
|
||||
let adjacentIndex = index + (direction === 'next' ? 1 : -1)
|
||||
// Loop back to the first song if 'next' goes beyond the last index
|
||||
if (adjacentIndex >= this.audioList.length) {
|
||||
adjacentIndex = 0
|
||||
} else if (adjacentIndex < 0) {
|
||||
adjacentIndex = this.audioList.length - 1
|
||||
}
|
||||
return this.audioList[adjacentIndex]
|
||||
},
|
||||
skip (_direction) {
|
||||
// useNuxtApp().$logger.log('fade out the song')
|
||||
const musicGain = this.createdNodes.musicGain
|
||||
if (musicGain instanceof GainNode) {
|
||||
musicGain.gain.setValueAtTime(musicGain.gain.value, this.createdNodes.musicGain.context.currentTime)
|
||||
musicGain.gain.linearRampToValueAtTime(0, musicGain.context.currentTime + 2)
|
||||
// useNuxtApp().$logger.log('volume of musicAudio ' + musicAudioElement.volume)
|
||||
}
|
||||
setTimeout(() => {
|
||||
// useNuxtApp().$logger.log('gain of musicAudio ' + musicGain.gain.value)
|
||||
}, 6000)
|
||||
|
||||
// useNuxtApp().$logger.log(nextSong)
|
||||
/*
|
||||
setTimeout(() => {
|
||||
// useNuxtApp().$logger.log('replace the song')
|
||||
this.selectAudioByTitle(nextSong.title)
|
||||
}, 2000) */
|
||||
},
|
||||
selectAudioByTitle (title) {
|
||||
this.selectedAudioTitle = title // update the selected Audio what is a reactive prop that triggers rerenderin the Audioelement
|
||||
setTimeout(() => {
|
||||
this.replaceMusicNode()
|
||||
// this line triggers a rerender of the noise audio element what is not what we want
|
||||
this.currentElement = this.audioList[this.audioList.findIndex(e => e.title === title)]
|
||||
}, 100)
|
||||
},
|
||||
areAllNodesAvailable () {
|
||||
// List of required node keys as you've named them in this.createdNodes
|
||||
const requiredNodes = [
|
||||
'musicSource', 'musicSplitter', 'microphoneSource',
|
||||
'musicDevice', 'outputSplitter', 'musicGain',
|
||||
'noiseSource', 'noiseSplitter', 'noiseDevice',
|
||||
'noiseGain', 'merger'
|
||||
]
|
||||
|
||||
// Check if each required node exists and is not undefined in this.createdNodes
|
||||
return requiredNodes.every(nodeKey => this.createdNodes[nodeKey] !== undefined)
|
||||
},
|
||||
connectMusicDevice () {
|
||||
const { musicDevice, microphoneSource, musicSplitter, musicSource, musicGain, outputSplitter } = this.createdNodes
|
||||
// useNuxtApp().$logger.log('connect Music device')
|
||||
musicSource.connect(musicSplitter, 0, 0)
|
||||
// musicSource.connect(musicSource.context.destination)
|
||||
microphoneSource.connect(musicDevice, 0, 0)
|
||||
musicSplitter.connect(musicDevice, 0, 1)
|
||||
musicSplitter.connect(musicDevice, 1, 2)
|
||||
musicDevice.connect(musicGain)
|
||||
musicDevice.connect(outputSplitter)
|
||||
musicGain.connect(this.audioContext.destination)
|
||||
// useNuxtApp().$logger.log('Music device connected')
|
||||
musicGain.gain.value = 1
|
||||
},
|
||||
|
||||
//
|
||||
// hier Version 2 testen Noise und Music rein, dann Gain
|
||||
//
|
||||
|
||||
//
|
||||
// hier Version 1 testen Noise und Music Mergen dann Gain
|
||||
//
|
||||
|
||||
connectNoiseNode () {
|
||||
// useNuxtApp().$logger.log('CONNECT NOISE NODE')
|
||||
const {
|
||||
noiseSplitter, microphoneSource, noiseDevice,
|
||||
musicSplitter, noiseSource, noiseGain
|
||||
} = this.createdNodes
|
||||
|
||||
this.connectMusicDevice()
|
||||
noiseSource.connect(noiseSplitter, 0, 0)
|
||||
microphoneSource.connect(noiseDevice, 0, 0)
|
||||
noiseSplitter.connect(noiseDevice, 0, 1)
|
||||
noiseSplitter.connect(noiseDevice, 1, 2) // 2 channels
|
||||
musicSplitter.connect(noiseDevice, 0, 3)
|
||||
musicSplitter.connect(noiseDevice, 1, 4)
|
||||
const attenuation = this.noiseRNBODevice.parametersById.get('attenuation')
|
||||
attenuation.value = getAttenuationFactor()
|
||||
noiseDevice.connect(noiseGain)
|
||||
noiseGain.connect(noiseGain.context.destination)
|
||||
setTimeout(() => {
|
||||
this.unmute()
|
||||
}, 300)
|
||||
},
|
||||
connectNoiseNodeOrginal () {
|
||||
// useNuxtApp().$logger.log('CONNECT NOISE NODE')
|
||||
const {
|
||||
noiseSplitter, microphoneSource, noiseDevice,
|
||||
outputSplitter, noiseSource, noiseGain
|
||||
} = this.createdNodes
|
||||
|
||||
this.connectMusicDevice()
|
||||
noiseSource.connect(noiseSplitter, 0, 0)
|
||||
microphoneSource.connect(noiseDevice, 0, 0)
|
||||
noiseSplitter.connect(noiseDevice, 0, 1)
|
||||
noiseSplitter.connect(noiseDevice, 1, 2) // 2 channels
|
||||
outputSplitter.connect(noiseDevice, 0, 3)
|
||||
outputSplitter.connect(noiseDevice, 1, 4)
|
||||
const attenuation = this.noiseRNBODevice.parametersById.get('attenuation')
|
||||
attenuation.value = getAttenuationFactor()
|
||||
noiseDevice.connect(noiseGain)
|
||||
noiseGain.connect(noiseGain.context.destination)
|
||||
setTimeout(() => {
|
||||
this.unmute()
|
||||
}, 300)
|
||||
},
|
||||
handleLoadingUpdate () {
|
||||
// useNuxtApp().$logger.log('Loaded Audio File we are ready to connect everything')
|
||||
},
|
||||
handlePlayingUpdate (isPlaying) {
|
||||
// useNuxtApp().$logger.log('prepared AudioNodes ' + this.createMusicNodeCounter)
|
||||
if (!isPlaying) { this.disconnectNodes() } else if (this.areAllNodesAvailable()) {
|
||||
this.replaceMusicNode()
|
||||
} else {
|
||||
// Whatever was before, reset
|
||||
if (this.createdNodes.length > 0) {
|
||||
this.createdNodes.musicGain.gain.linearRampToValueAtTime(0, this.audioContext.currentTime + 2)
|
||||
this.disconnectNodes()
|
||||
}
|
||||
this.prepareWebAudioNodes()
|
||||
}
|
||||
},
|
||||
prepareWebAudioNodes () {
|
||||
this.createMusicNodeCounter++
|
||||
const userStore = useUserStore()
|
||||
const isAuth = userStore.isAuthenticated
|
||||
// Create new Context there might be some nodes missing.
|
||||
// const ContextClass = (window.AudioContext || window.webkitAudioContext || window.mozAudioContext || window.oAudioContext || window.msAudioContext)
|
||||
// useNuxtApp().$logger.log('user', { userStore }, { isAuth })
|
||||
if (!isAuth) {
|
||||
// useNuxtApp().$logger.error('User no logged in ')
|
||||
return
|
||||
}
|
||||
// Take the microphone and all the other nodes and make them available for mindbooost
|
||||
navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
echoCancellation: false,
|
||||
noiseSuppression: false,
|
||||
autoGainControl: false
|
||||
},
|
||||
video: false
|
||||
}).then((micStream) => {
|
||||
const audioContext = this.audioContext ? this.audioContext : useAudioStore().getContext()
|
||||
const microphoneSource = audioContext.createMediaStreamSource(micStream)
|
||||
this.createdNodes.microphoneSource ||= microphoneSource
|
||||
// Return both audioContext and microphoneSource to the next link in the chain
|
||||
return { audioContext, microphoneSource }
|
||||
})
|
||||
.then(({ audioContext, microphoneSource }) => {
|
||||
// First RNBO device creation
|
||||
return createRNBODevice(audioContext, this.testPatch).then((noiseRNBODevice) => {
|
||||
this.createdNodes.noiseRNBODevice ||= noiseRNBODevice
|
||||
return { audioContext, microphoneSource, noiseRNBODevice }
|
||||
})
|
||||
})
|
||||
.then(({ audioContext, microphoneSource, noiseRNBODevice }) => {
|
||||
// Second RNBO device creation
|
||||
return createRNBODevice(audioContext, this.musicPatch).then((musicRNBODevice) => {
|
||||
this.createdNodes.musicRNBODevice ||= musicRNBODevice
|
||||
|
||||
return { audioContext, microphoneSource, noiseRNBODevice, musicRNBODevice }
|
||||
})
|
||||
}).then(({ audioContext, microphoneSource, noiseRNBODevice, musicRNBODevice }) => {
|
||||
this.createdNodes ||= {}
|
||||
this.noiseRNBODevice = noiseRNBODevice
|
||||
const noiseAudioElement = this.$refs.Noise.$refs.audioElement
|
||||
const musicAudioElement = this.$refs[this.currentElement.title].$refs.audioElement
|
||||
// Assign nodes if they don't exist
|
||||
this.createdNodes.microphoneSource ||= microphoneSource
|
||||
this.createdNodes.musicDevice ||= musicRNBODevice.node
|
||||
this.createdNodes.noiseDevice ||= noiseRNBODevice.node
|
||||
this.createdNodes.noiseSource ||= audioContext.createMediaElementSource(noiseAudioElement)
|
||||
this.createdNodes.musicSource ||= audioContext.createMediaElementSource(musicAudioElement)
|
||||
this.createdNodes.musicSplitter ||= audioContext.createChannelSplitter(2)
|
||||
this.createdNodes.noiseSplitter ||= audioContext.createChannelSplitter(2)
|
||||
this.createdNodes.outputSplitter ||= audioContext.createChannelSplitter(2)
|
||||
this.createdNodes.musicGain ||= audioContext.createGain()
|
||||
this.createdNodes.noiseGain ||= audioContext.createGain()
|
||||
this.createdNodes.merger ||= audioContext.createChannelMerger(4)
|
||||
this.createdNodesCount = this.createdNodes.length
|
||||
})
|
||||
},
|
||||
replaceMusicNode () {
|
||||
if (this.areAllNodesAvailable()) {
|
||||
const { musicSource, musicGain } = this.createdNodes
|
||||
setTimeout(() => {
|
||||
const musicAudioElement = this.$refs[this.currentElement.title].$refs.audioElement
|
||||
const currentGain = musicGain.gain.value
|
||||
const newMusicNode = musicSource.context.createMediaElementSource(musicAudioElement)
|
||||
musicSource.disconnect()
|
||||
this.connectMusicDevice()
|
||||
this.createdNodes.musicSource = newMusicNode
|
||||
// newMusicNode.connect(musicSplitter)
|
||||
musicGain.gain.linearRampToValueAtTime(currentGain, this.audioContext.currentTime + 2)
|
||||
}, 2100)
|
||||
}
|
||||
},
|
||||
disconnectMusicNodes () {
|
||||
const { musicDevice, musicSplitter, musicSource, outputSplitter, musicGain, noiseGain } = this.createdNodes
|
||||
// useNuxtApp().$logger.log('disconnect Music device')
|
||||
if (this.areAllNodesAvailable()) {
|
||||
if (musicGain instanceof GainNode) { musicGain.gain.linearRampToValueAtTime(0, this.audioContext.currentTime + 2) }
|
||||
if (noiseGain instanceof GainNode) { noiseGain.gain.linearRampToValueAtTime(noiseGain.gain.value, this.audioContext.currentTime) }
|
||||
setTimeout(() => {
|
||||
musicSource.disconnect()
|
||||
musicSplitter.disconnect()
|
||||
musicDevice.disconnect()
|
||||
outputSplitter.disconnect()
|
||||
// useNuxtApp().$logger.log('Music device disconnected')
|
||||
musicSource.disconnect()
|
||||
}, 2050)
|
||||
} else {
|
||||
// useNuxtApp().$logger.log('No nodes present to disconnect')
|
||||
}
|
||||
},
|
||||
disconnectNodes () {
|
||||
// useNuxtApp().$logger.log('DISCONNECT ALL NODES')
|
||||
if (this.createdNodes.microphoneSource) { this.createdNodes.microphoneSource.mediaStream.getTracks().forEach(track => track.stop()) }
|
||||
// Ensure this.createdNodes is defined and is an object
|
||||
if (typeof this.createdNodes === 'object' && this.createdNodes !== null) {
|
||||
Object.values(this.createdNodes).forEach((node) => {
|
||||
// Check if the node exists and has a disconnect method
|
||||
if (node && typeof node.disconnect === 'function') {
|
||||
node.disconnect()
|
||||
}
|
||||
// Additional handling for specific types of nodes, if necessary
|
||||
// For example, stopping an OscillatorNode
|
||||
if (node instanceof OscillatorNode) {
|
||||
node.stop()
|
||||
}
|
||||
})
|
||||
}
|
||||
useAudioStore().setPlaying(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.rnboplayer {
|
||||
position: fixed;
|
||||
width: 220px; /* Or specify a fixed width like 220px if you prefer */
|
||||
max-width: 220px; /* This line might be redundant depending on your width strategy */
|
||||
height: fit-content;
|
||||
display: inline-grid;
|
||||
z-index: 2;
|
||||
bottom: 11%;
|
||||
left: 0;
|
||||
right: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.play.yellow {
|
||||
background: rgba(255, 176, 66, 0.008);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 0 rgba(255, 177, 66, 1);
|
||||
animation: pulse-yellow 4s infinite;
|
||||
position: fixed;
|
||||
bottom: 40%;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background-image: url('/images/playbtn.svg');
|
||||
background-repeat:no-repeat;
|
||||
background-attachment:fixed;
|
||||
background-position: 58% 55%;
|
||||
}
|
||||
.pause.yellow {
|
||||
background: rgba(255, 176, 66, 0.008);
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 0 0 0 rgba(255, 177, 66, 1);
|
||||
opacity: 0.05;
|
||||
position: fixed;
|
||||
bottom: 40%;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
background-image: url('/images/pausebtn.svg');
|
||||
background-size: 130px 100px;
|
||||
background-repeat:no-repeat;
|
||||
background-attachment:fixed;
|
||||
background-position: center;
|
||||
}
|
||||
.pause.yellow:hover{
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
@keyframes pulse-yellow {
|
||||
0% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba(255, 177, 66, 0.7);
|
||||
}
|
||||
|
||||
70% {
|
||||
transform: scale(1);
|
||||
box-shadow: 0 0 0 10px rgba(255, 177, 66, 0);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: scale(0.95);
|
||||
box-shadow: 0 0 0 0 rgba(255, 177, 66, 0);
|
||||
}
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user