Initial commit
This commit is contained in:
26
pages/experiments/AudioReactiveMeter.vue
Normal file
26
pages/experiments/AudioReactiveMeter.vue
Normal file
@@ -0,0 +1,26 @@
|
||||
<template>
|
||||
<div>
|
||||
<svg
|
||||
width="150px"
|
||||
height="150px"
|
||||
class="icon"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="#CCCCCC"
|
||||
>
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0" />
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" />
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<path d="M512 669.824a155.584 155.584 0 0 0 155.136-155.136V223.808c0-85.312-69.824-155.136-155.136-155.136S356.864 138.496 356.864 223.808v290.88A155.584 155.584 0 0 0 512 669.824z" fill="" />
|
||||
<path d="M512 625.024a110.4 110.4 0 0 1-110.336-110.336V223.808c0-60.864 49.472-110.336 110.336-110.336s110.336 49.472 110.336 110.336v290.88c0 60.864-49.472 110.336-110.336 110.336z" fill="#e9c045" />
|
||||
<path d="M753.216 398.336a19.2 19.2 0 0 0-19.072-18.624 19.328 19.328 0 0 0-19.136 18.624h-0.32v117.632A203.008 203.008 0 0 1 512 718.976a203.136 203.136 0 0 1-202.752-203.008V398.336h-0.32a19.2 19.2 0 0 0-19.072-18.624 19.328 19.328 0 0 0-19.136 18.624h-0.256v117.632c0 126.656 98.176 229.696 222.144 239.808v108.672H376.256a19.392 19.392 0 1 0 0 38.848h271.488a19.392 19.392 0 0 0 0-38.848H531.392V755.84c123.968-10.112 222.144-113.216 222.144-239.808V398.336h-0.32z" fill="" />
|
||||
<path d="M753.216 398.336a19.2 19.2 0 0 0-19.072-18.624 19.328 19.328 0 0 0-19.136 18.624h-0.32v117.632A203.008 203.008 0 0 1 512 718.976a203.136 203.136 0 0 1-202.752-203.008V398.336h-0.32a19.2 19.2 0 0 0-19.072-18.624 19.328 19.328 0 0 0-19.136 18.624h-0.256v117.632c0 126.656 98.176 229.696 222.144 239.808v108.672H376.256a19.392 19.392 0 1 0 0 38.848h271.488a19.392 19.392 0 0 0 0-38.848H531.392V755.84c123.968-10.112 222.144-113.216 222.144-239.808V398.336h-0.32zM390.72 305.344a16 16 0 0 1 0-32h129.344a16 16 0 0 1 0 32H390.72zM390.72 379.712a16 16 0 0 1 0-32h129.344a16 16 0 0 1 0 32H390.72z" fill="" />
|
||||
<path d="M390.72 454.08a16 16 0 0 1 0-32h129.344a16 16 0 0 1 0 32H390.72z" fill="" />
|
||||
</g>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import Player from '~/components/Player.vue'
|
||||
</script>
|
44
pages/experiments/AudioTag.vue
Normal file
44
pages/experiments/AudioTag.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<AudioFileSelector @file-selected="updateSrc" />
|
||||
<KeyboardPlayHandler />
|
||||
<AudioTag :src="audioSrc" :volume="volume" :play="playing" />
|
||||
<input
|
||||
id="gain-control"
|
||||
v-model="volume"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1.0"
|
||||
step="0.01"
|
||||
@wheel="changeNoiseGain"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import AudioTag from '~/components/experiments/AudioTag.vue'
|
||||
import tracksConfig from '~/tracks.config'
|
||||
import AudioFileSelector from '~/components/AudioFileSelector.vue'
|
||||
import { useAudioStore } from '~/stores/audio'
|
||||
import KeyboardPlayHandler from '~/archive/components/KeyboardPlayHandler.vue'
|
||||
|
||||
const audioSrc = ref('')
|
||||
const volume = ref(0)
|
||||
const playing = computed(() => useAudioStore().playing)
|
||||
const updateSrc = (e) => {
|
||||
useNuxtApp().$logger.log('file Selected update', { e })
|
||||
// Update the audioSrc with the selected file
|
||||
audioSrc.value = e
|
||||
}
|
||||
const changeNoiseGain = (event) => {
|
||||
event.preventDefault()
|
||||
const volumepercents = volume.value * 100
|
||||
// Determine the direction of the scroll
|
||||
const delta = Math.sign(event.deltaY)
|
||||
if (volumepercents - delta < 101 && volumepercents - delta > -1) {
|
||||
volume.value -= delta / 100
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
40
pages/experiments/AudioTagWebAudio.vue
Normal file
40
pages/experiments/AudioTagWebAudio.vue
Normal file
@@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<div>
|
||||
<AudioFileSelector @file-selected="updateSrc" />
|
||||
<AudioTagWebAudio :src="audioSrc" :volume="volume" />
|
||||
<input
|
||||
id="gain-control"
|
||||
v-model="volume"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1.0"
|
||||
step="0.01"
|
||||
@wheel="changeNoiseGain"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import AudioTagWebAudio from '~/components/experiments/AudioTagWebAudio.vue'
|
||||
import tracksConfig from '~/tracks.config'
|
||||
import AudioFileSelector from '~/components/AudioFileSelector.vue'
|
||||
|
||||
const audioSrc = ref('')
|
||||
const volume = ref(0)
|
||||
const updateSrc = (e) => {
|
||||
useNuxtApp().$logger.log('file Selected update', { e })
|
||||
// Update the audioSrc with the selected file
|
||||
audioSrc.value = e
|
||||
}
|
||||
const changeNoiseGain = (event) => {
|
||||
event.preventDefault()
|
||||
const volumepercents = volume.value * 100
|
||||
// Determine the direction of the scroll
|
||||
const delta = Math.sign(event.deltaY)
|
||||
if (volumepercents - delta < 101 && volumepercents - delta > -1) {
|
||||
volume.value -= delta / 100
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
39
pages/experiments/AudioTagWebAudioStreaming.vue
Normal file
39
pages/experiments/AudioTagWebAudioStreaming.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div>
|
||||
<AudioFileSelector @file-selected="updateSrc" />
|
||||
<AudioTagWebAudioStreaming :src="audioSrc" :volume="volume" />
|
||||
<input
|
||||
id="gain-control"
|
||||
v-model="volume"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1.0"
|
||||
step="0.01"
|
||||
@wheel="changeNoiseGain"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import AudioFileSelector from '~/components/AudioFileSelector.vue'
|
||||
import AudioTagWebAudioStreaming from '~/components/experiments/AudioTagWebAudioStreaming.vue'
|
||||
|
||||
const audioSrc = ref('')
|
||||
const volume = ref(0)
|
||||
const updateSrc = (e) => {
|
||||
useNuxtApp().$logger.log('file Selected update', { e })
|
||||
// Update the audioSrc with the selected file
|
||||
audioSrc.value = e
|
||||
}
|
||||
const changeNoiseGain = (event) => {
|
||||
event.preventDefault()
|
||||
const volumepercents = volume.value * 100
|
||||
// Determine the direction of the scroll
|
||||
const delta = Math.sign(event.deltaY)
|
||||
if (volumepercents - delta < 101 && volumepercents - delta > -1) {
|
||||
volume.value -= delta / 100
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
44
pages/experiments/AudioTags.vue
Normal file
44
pages/experiments/AudioTags.vue
Normal file
@@ -0,0 +1,44 @@
|
||||
<template>
|
||||
<div>
|
||||
<AudioFileSelector @file-selected="updateSrc" />
|
||||
|
||||
<AudioTag :src="audioSrc" :volume="volume" />
|
||||
<AudioTagWebAudio :src="audioSrc" :volume="volume" />
|
||||
<AudioTagWebAudioStreaming :src="audioSrc" :volume="volume" />
|
||||
<input
|
||||
id="gain-control"
|
||||
v-model="volume"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1.0"
|
||||
step="0.01"
|
||||
@wheel="changeNoiseGain"
|
||||
>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import AudioTag from '~/components/experiments/AudioTag.vue'
|
||||
import AudioFileSelector from '~/components/AudioFileSelector.vue'
|
||||
import AudioTagWebAudio from '~/components/experiments/AudioTagWebAudio.vue'
|
||||
import AudioTagWebAudioStreaming from '~/components/experiments/AudioTagWebAudioStreaming.vue'
|
||||
|
||||
const audioSrc = ref('')
|
||||
const volume = ref(0)
|
||||
const updateSrc = (e) => {
|
||||
useNuxtApp().$logger.log('file Selected update', { e })
|
||||
// Update the audioSrc with the selected file
|
||||
audioSrc.value = e
|
||||
}
|
||||
const changeNoiseGain = (event) => {
|
||||
event.preventDefault()
|
||||
const volumepercents = volume.value * 100
|
||||
// Determine the direction of the scroll
|
||||
const delta = Math.sign(event.deltaY)
|
||||
if (volumepercents - delta < 101 && volumepercents - delta > -1) {
|
||||
volume.value -= delta / 100
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
9
pages/experiments/MicNoisePatchMusic.vue
Normal file
9
pages/experiments/MicNoisePatchMusic.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<NoisePatchMusic />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import NoisePatchMusic from '~/archive/components/tests/NoisePatchMusic_glitchOnLoad.vue'
|
||||
</script>
|
10
pages/experiments/MicNoisePatchMusic2.vue
Normal file
10
pages/experiments/MicNoisePatchMusic2.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<MicNoisePatchMusic2 />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import Player from '~/components/Player.vue'
|
||||
import MicNoisePatchMusic2 from '~/archive/components/tests/MicNoisePatchMusic2.vue'
|
||||
</script>
|
69
pages/experiments/MicrophoneStore.vue
Normal file
69
pages/experiments/MicrophoneStore.vue
Normal file
@@ -0,0 +1,69 @@
|
||||
<template>
|
||||
<div v-show="true">
|
||||
<h1>Microphone</h1>
|
||||
<button class="btn btn-primary" @click="attach">
|
||||
{{ 'Mikrofon aktivieren' }}
|
||||
</button>
|
||||
<button class="btn btn-secondary" @click="detach">
|
||||
{{ 'Mikrofon trennen' }}
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { ref, onMounted, onUnmounted, watch } from 'vue'
|
||||
import type { Logger } from 'pino'
|
||||
import { useMicStore } from '~/stores/microphone'
|
||||
import { useAudioStore } from '~/stores/audio'
|
||||
|
||||
export default {
|
||||
name: 'MicrophoneStoreHandler',
|
||||
emits: ['update:attach'],
|
||||
setup (_props, { emit }) {
|
||||
const audioStore = useAudioStore()
|
||||
const microphone = ref<Promise<MediaStream> | null>(null)
|
||||
const microphoneActive = ref(false)
|
||||
const logger = useNuxtApp().$logger as Logger
|
||||
|
||||
const attach = () => {
|
||||
logger.info('attach microphone')
|
||||
if (!microphone.value) {
|
||||
microphone.value = navigator.mediaDevices.getUserMedia({
|
||||
audio: {
|
||||
echoCancellation: false,
|
||||
noiseSuppression: false,
|
||||
autoGainControl: false
|
||||
},
|
||||
video: false
|
||||
})
|
||||
microphoneActive.value = true
|
||||
}
|
||||
|
||||
return microphone.value.then((stream) => {
|
||||
emit('update:attach', stream)
|
||||
return stream
|
||||
})
|
||||
}
|
||||
|
||||
const detach = async () => {
|
||||
logger.info('detach microphone')
|
||||
if (microphone.value) {
|
||||
try {
|
||||
const stream = await microphone.value
|
||||
stream.getTracks().forEach(track => track.stop())
|
||||
} catch (error) {
|
||||
}
|
||||
microphone.value = null
|
||||
microphoneActive.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// Return the public properties and methods
|
||||
return {
|
||||
attach,
|
||||
detach,
|
||||
isPlaying: () => audioStore.playing,
|
||||
microphoneActive
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
10
pages/experiments/MicrophoneTest.vue
Normal file
10
pages/experiments/MicrophoneTest.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<StateBar />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Microphone from '~/components/experiments/tests/Microphone.vue'
|
||||
import StateBar from '~/components/experiments/statemanagement/StateBar.vue'
|
||||
</script>
|
9
pages/experiments/Noise3BandGain.vue
Normal file
9
pages/experiments/Noise3BandGain.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<NoiseGain />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import NoiseGain from '~/components/experiments/homepages/NoiseGain.vue'
|
||||
</script>
|
10
pages/experiments/NoiseControlledBand.vue
Normal file
10
pages/experiments/NoiseControlledBand.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<NoiseControlledBand center-frequency="1000" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import Player from '~/components/Player.vue'
|
||||
import NoiseControlledBand from '~/components/experiments/tests/ControlValues/NoiseControlledWebAudioBand.vue'
|
||||
</script>
|
12
pages/experiments/NoiseMusicGain.vue
Normal file
12
pages/experiments/NoiseMusicGain.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<KeyboardPlayHandler />
|
||||
<NoiseMusicGain />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import Player from '~/components/Player.vue'
|
||||
import NoiseMusicGain from '~/components/experiments/tests/NoiseMusicGain.vue'
|
||||
import KeyboardPlayHandler from '~/archive/components/KeyboardPlayHandler.vue'
|
||||
</script>
|
9
pages/experiments/NoiseMusicGainFadeIn.vue
Normal file
9
pages/experiments/NoiseMusicGainFadeIn.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<NoiseMusicGainFadeIn />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import NoiseMusicGainFadeIn from '~/components/experiments/tests/NoiseMusicGainFadeIn.vue'
|
||||
</script>
|
9
pages/experiments/NoiseMusicGainMic.vue
Normal file
9
pages/experiments/NoiseMusicGainMic.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<NoiseMusicGainMic />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import NoiseMusicGainMic from '~/components/experiments/tests/NoiseMusicGainMic.vue'
|
||||
</script>
|
10
pages/experiments/NoiseMusicGainPlayPause.vue
Normal file
10
pages/experiments/NoiseMusicGainPlayPause.vue
Normal file
@@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<div>
|
||||
<NoiseMusicGainPlayPause />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import Player from '~/components/Player.vue'
|
||||
import NoiseMusicGainPlayPause from '~/components/experiments/tests/NoiseMusicGainPlayPause.vue'
|
||||
</script>
|
11
pages/experiments/NoiseMusicGainPlayPauseDevice.vue
Normal file
11
pages/experiments/NoiseMusicGainPlayPauseDevice.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
<KeyboardPlayHandler />
|
||||
<NoiseMusicGainPlayPauseDevice />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import NoiseMusicGainPlayPauseDevice from '~/archive/components/tests/NoiseMusicGainPlayPauseDevice.vue'
|
||||
import KeyboardPlayHandler from '~/archive/components/KeyboardPlayHandler.vue'
|
||||
</script>
|
11
pages/experiments/NoiseMusicWebAudio.vue
Normal file
11
pages/experiments/NoiseMusicWebAudio.vue
Normal file
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<div>
|
||||
Version 1 Noise Music WebAudio:
|
||||
<NoiseMusicWebAudio />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
// import Player from '~/components/Player.vue'
|
||||
import NoiseMusicWebAudio from '~/components/experiments/tests/NoiseMusicWebAudio.vue'
|
||||
</script>
|
77
pages/experiments/Player/HowlWebAudioBridge.vue
Normal file
77
pages/experiments/Player/HowlWebAudioBridge.vue
Normal file
@@ -0,0 +1,77 @@
|
||||
<!-- eslint-disable vue/no-multiple-template-root -->
|
||||
<template>
|
||||
<h4> Die Audio HowlWebAudioBridge sorgt zwar für ein sauberes Streaming, aber lässt sich dann nicht über WebAudio regeln</h4>
|
||||
<div>
|
||||
<HowlWebAudioBridge
|
||||
:src="source"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="handleAudioNode"
|
||||
/>
|
||||
<button @click="testAudio">Test Audio</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import HowlWebAudioBridge from '~/components/Player/HowlWebAudioBridge.vue'
|
||||
import tracksConfig from '~/tracks.config'
|
||||
import { useAudioStore, ensureAudio } from '~/stores/audio'
|
||||
|
||||
const audioContext = useAudioStore().getContext()
|
||||
const source = tracksConfig.debug_src
|
||||
const audioNode = ref({} as AudioBufferSourceNode | MediaElementAudioSourceNode | MediaStreamAudioSourceNode)
|
||||
const gainNode = ref({} as GainNode)
|
||||
const audioReady = ref(false as boolean)
|
||||
const howlElement = ref({} as Howl)
|
||||
|
||||
async function testAudio () {
|
||||
await ensureAudio()
|
||||
if (audioReady && gainNode && howlElement) {
|
||||
howlElement.value.play()
|
||||
const audioDestination = audioContext.destination
|
||||
const gain = gainNode.value as GainNode
|
||||
if (audioNode instanceof AudioBufferSourceNode) {
|
||||
audioNode.loop = true
|
||||
audioNode.playbackRate.value = 1
|
||||
audioNode.connect(gain).connect(audioDestination)
|
||||
}
|
||||
if (audioNode instanceof MediaElementAudioSourceNode) {
|
||||
audioNode.connect(gain).connect(audioDestination)
|
||||
}
|
||||
if (audioNode instanceof MediaStreamAudioSourceNode) {
|
||||
audioNode.connect(gain).connect(audioDestination)
|
||||
}
|
||||
gain.gain.cancelScheduledValues(audioContext.currentTime)
|
||||
gain.gain.linearRampToValueAtTime(1, audioContext.currentTime + 2)
|
||||
|
||||
useNuxtApp().$logger.log('Gain connected to destination')
|
||||
} else {
|
||||
useNuxtApp().$logger.warn('Audio is not yet ready to test it')
|
||||
}
|
||||
}
|
||||
|
||||
function handleAudioNode (node: MediaElementAudioSourceNode | AudioBufferSourceNode | MediaStreamAudioSourceNode, howl: Howl | null) {
|
||||
useNuxtApp().$logger.log('AUDIONODE IST ANGEKOMMEN ')
|
||||
useNuxtApp().$logger.log({ node })
|
||||
useNuxtApp().$logger.log({ howl })
|
||||
audioNode.value = node
|
||||
if (howl) { howlElement.value = howl }
|
||||
|
||||
gainNode.value = audioContext.createGain() as GainNode
|
||||
gainNode.value.gain.setValueAtTime(0, audioContext.currentTime)
|
||||
|
||||
// Not connected to any destination, gain currently muted
|
||||
if (node instanceof AudioBufferSourceNode) {
|
||||
node.loop = true
|
||||
node.playbackRate.value = 1
|
||||
node.connect(gainNode.value)
|
||||
}
|
||||
if (node instanceof MediaElementAudioSourceNode) {
|
||||
node.connect(gainNode.value)
|
||||
}
|
||||
if (node instanceof MediaStreamAudioSourceNode) {
|
||||
node.connect(gainNode.value)
|
||||
}
|
||||
audioReady.value = true
|
||||
}
|
||||
|
||||
</script>
|
12
pages/experiments/Player/RNBODevice.vue
Normal file
12
pages/experiments/Player/RNBODevice.vue
Normal file
@@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<RNBODevice />
|
||||
<GainController />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import GainController from '~/components/experiments/GainController.vue'
|
||||
import RNBODevice from '~/components/experiments/homepages/RNBODevice.vue'
|
||||
import NavigationBar from '~/components/NavigationBar.vue'
|
||||
</script>
|
49
pages/experiments/Player/TestWebAudioControlOfHowl.vue
Normal file
49
pages/experiments/Player/TestWebAudioControlOfHowl.vue
Normal file
@@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<h1 class="text-xl font-bold mb-4">Audio Fade-In Beispiel</h1>
|
||||
<audio
|
||||
ref="audioEl"
|
||||
:src="source"
|
||||
crossorigin="anonymous"
|
||||
/>
|
||||
<button
|
||||
class="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700 transition"
|
||||
@click="playWithFadeIn"
|
||||
>
|
||||
Abspielen mit Fade-In
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import tracksConfig from '~/tracks.config'
|
||||
|
||||
const audioEl = ref(null)
|
||||
const source = ref(tracksConfig.lagoon_48_mp3_src)
|
||||
|
||||
const playWithFadeIn = async () => {
|
||||
const audio = audioEl.value
|
||||
const sink = useUserStore().audioOutputDevice
|
||||
audio.value?.setSinkId(sink.deviceId)
|
||||
|
||||
if (!audio) { return }
|
||||
|
||||
const audioContext = new (window.AudioContext || window.webkitAudioContext)()
|
||||
const sourceNode = audioContext.createMediaElementSource(audio)
|
||||
const gainNode = audioContext.createGain()
|
||||
|
||||
// Anfangslautstärke auf 0 setzen
|
||||
gainNode.gain.setValueAtTime(0, audioContext.currentTime)
|
||||
|
||||
// Ziel-Lautstärke in 3 Sekunden erreichen
|
||||
gainNode.gain.linearRampToValueAtTime(1.0, audioContext.currentTime + 3)
|
||||
|
||||
// Verkabeln
|
||||
sourceNode.connect(gainNode)
|
||||
gainNode.connect(audioContext.destination)
|
||||
|
||||
// Abspielen
|
||||
await audio.play()
|
||||
}
|
||||
</script>
|
@@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="p-4">
|
||||
<h1 class="text-xl font-bold mb-4">Howler → MediaStream + volume Fade-In</h1>
|
||||
<h3>Startet, Kontrolle der Lautstärke über volume-API, aber erst kommt 100% Lautstärke Audio, bevor es verstummt und dann einfaded</h3>
|
||||
<button
|
||||
class="px-4 py-2 bg-indigo-600 text-white rounded hover:bg-indigo-700 transition"
|
||||
@click="startCaptureWithVolumeRamp"
|
||||
>
|
||||
Starten & Lautstärke einblenden
|
||||
</button>
|
||||
<div v-if="mediaStream" class="mt-4 text-green-700 font-mono">
|
||||
✅ MediaStream erzeugt!
|
||||
</div>
|
||||
<p v-if="error" class="text-red-600 mt-2">{{ error }}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { Howl } from 'howler'
|
||||
import tracksConfig from '~/tracks.config'
|
||||
|
||||
const mediaStream = ref(null)
|
||||
const error = ref(null)
|
||||
|
||||
const startCaptureWithVolumeRamp = async () => {
|
||||
try {
|
||||
const sound = new Howl({
|
||||
src: [tracksConfig.lagoon_48_mp3_src],
|
||||
html5: true,
|
||||
preload: true,
|
||||
muted: true
|
||||
})
|
||||
|
||||
await new Promise(resolve => sound.once('load', resolve))
|
||||
|
||||
const audioElement = sound._sounds[0]._node
|
||||
|
||||
// Volume auf 0 (mute) setzen
|
||||
audioElement.volume = 0
|
||||
|
||||
// captureStream vor play aufrufen
|
||||
const stream = audioElement.captureStream()
|
||||
|
||||
// Starte Audio
|
||||
sound.play()
|
||||
|
||||
// Starte Volume-Ramp (linear über ~3 Sekunden)
|
||||
let volume = 0
|
||||
const step = 0.05 // Schrittgröße
|
||||
const interval = 150 // ms zwischen Schritten
|
||||
|
||||
const ramp = setInterval(() => {
|
||||
volume += step
|
||||
audioElement.volume = Math.min(1, volume)
|
||||
|
||||
if (volume >= 1) {
|
||||
clearInterval(ramp)
|
||||
}
|
||||
}, interval)
|
||||
|
||||
mediaStream.value = stream
|
||||
} catch (err) {
|
||||
useNuxtApp().$logger.error(err)
|
||||
error.value = 'Fehler beim Start: ' + err.message
|
||||
}
|
||||
}
|
||||
</script>
|
9
pages/experiments/Player/index.vue
Normal file
9
pages/experiments/Player/index.vue
Normal file
@@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<div>
|
||||
<Player />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Player from '~/components/experiments/tests/showcases/PlayerComponent.vue'
|
||||
</script>
|
168
pages/experiments/Player/newRNBOPlayer.vue
Normal file
168
pages/experiments/Player/newRNBOPlayer.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Test Fall 4</h1>
|
||||
<h3>Howler als Stream mit der HowlWebAudioBridge (createMediaElementSource)</h3>
|
||||
<KeyboardPlayHandler />
|
||||
<button @click="play">Play</button>
|
||||
<div v-if="false" id="statistics">
|
||||
{{ Object.keys(gains).length }}
|
||||
{{ Object.keys(nodes).length }}
|
||||
</div>
|
||||
<HowlWebAudioBridge
|
||||
:src="source[0]"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="(node, howl) => registerAudioNode(node, howl, 63)"
|
||||
/>
|
||||
<HowlWebAudioBridge
|
||||
:src="source[1]"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="(node, howl) => registerAudioNode(node, howl, 125)"
|
||||
/>
|
||||
<HowlWebAudioBridge
|
||||
:src="source[2]"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="(node, howl) => registerAudioNode(node, howl, 250)"
|
||||
/>
|
||||
<HowlWebAudioBridge
|
||||
:src="source[3]"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="(node, howl) => registerAudioNode(node, howl, 500)"
|
||||
/>
|
||||
<HowlWebAudioBridge
|
||||
:src="source[4]"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="(node, howl) => registerAudioNode(node, howl, 1000)"
|
||||
/>
|
||||
<HowlWebAudioBridge
|
||||
:src="source[5]"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="(node, howl) => registerAudioNode(node, howl, 2000)"
|
||||
/>
|
||||
<HowlWebAudioBridge
|
||||
:src="source[6]"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="(node, howl) => registerAudioNode(node, howl, 4000)"
|
||||
/>
|
||||
<HowlWebAudioBridge
|
||||
:src="source[7]"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="(node, howl) => registerAudioNode(node, howl, 8000)"
|
||||
/>
|
||||
<HowlWebAudioBridge
|
||||
:src="source[8]"
|
||||
:audio-context="audioContext"
|
||||
:on-ready="(node, howl) => registerAudioNode(node, howl, 16000)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { Logger } from 'pino'
|
||||
import { useAudioStore } from '~/stores/audio'
|
||||
import tracksConfig from '~/tracks.config'
|
||||
import HowlWebAudioBridge from '~/components/Player/HowlWebAudioBridge.vue'
|
||||
import KeyboardPlayHandler from '~/archive/components/KeyboardPlayHandler.vue'
|
||||
|
||||
const source = ref([tracksConfig['63_src'], tracksConfig['125_src'], tracksConfig['250_src'], tracksConfig['500_src'], tracksConfig['1000_src'], tracksConfig['2000_src'], tracksConfig['4000_src'], tracksConfig['8000_src'], tracksConfig['16000_src']])
|
||||
const audioContext = ref(useAudioStore().getContext()as AudioContext)
|
||||
const nodes = ref([] as Record<number, AudioNode>)
|
||||
const gains = ref([] as Record<number, GainNode>)
|
||||
const howls = ref([] as Array<Howl>)
|
||||
const masterGain = ref(null as (GainNode | null))
|
||||
const lastAction = audioContext.value.currentTime
|
||||
const logger = useNuxtApp().$logger as Logger
|
||||
|
||||
const registerAudioNode = (node:AudioNode | null, howl: Howl| null, freq: number) => {
|
||||
if (howl) {
|
||||
logger.info('register new Node push it', howl.state)
|
||||
howls.value.push(howl)
|
||||
const mediaElement = (howl as any)._sounds[0]._node as HTMLAudioElement
|
||||
mediaElement.muted = false
|
||||
logger.info('mediaElement muted')
|
||||
}
|
||||
if (node) {
|
||||
const gainNode = node.context.createGain()
|
||||
const pannerNode = audioContext.value.createStereoPanner()
|
||||
pannerNode.pan.value = 1
|
||||
|
||||
gainNode.gain.setValueAtTime(0, audioContext.value.currentTime)
|
||||
gainNode.gain.linearRampToValueAtTime(0.5, audioContext.value.currentTime + 2)
|
||||
const master = masterGain.value || useAudioStore().getMasterGainNoise()
|
||||
nodes.value[freq] = node
|
||||
gains.value[freq] = gainNode
|
||||
node.connect(gainNode)
|
||||
gainNode.connect(master)
|
||||
pannerNode.connect(master)
|
||||
|
||||
logger.info('mediaElement nicht mehr gemutet')
|
||||
logger.info('AudioNode ready, but not connected to destination', { node })
|
||||
logger.info('Number of GainNodes', Object.keys(gains.value).length)
|
||||
logger.info('Number of AudioNodes', Object.keys(nodes.value).length)
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => useAudioStore().playing, // Überwache explizit den state
|
||||
(newState) => {
|
||||
if (newState) {
|
||||
play()
|
||||
} else {
|
||||
pause()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
watch(() => lastAction, // Überwache explizit den state
|
||||
(lastTimestamp) => {
|
||||
if (lastTimestamp > 50) {
|
||||
play() // Overlay anzeigen
|
||||
} else {
|
||||
pause()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const pause = () => {
|
||||
logger.info('pause')
|
||||
const master = masterGain.value
|
||||
const currentTime = audioContext.value.currentTime
|
||||
// master?.gain.cancelScheduledValues(currentTime)
|
||||
master?.gain.linearRampToValueAtTime(0, currentTime + 5)
|
||||
const lastAction = currentTime
|
||||
howls.value.forEach((howl) => {
|
||||
howl.pause()
|
||||
}
|
||||
)
|
||||
master?.disconnect()
|
||||
// if (gainNode) {
|
||||
// gainNode.connect(audioContext.value.destination)
|
||||
// gainNode.gain.cancelScheduledValues(audioContext.value.currentTime)
|
||||
// gainNode.gain.setValueAtTime(0, audioContext.value.currentTime)
|
||||
// gainNode.gain.linearRampToValueAtTime(2, audioContext.value.currentTime + 4)
|
||||
// } else {
|
||||
// logger.info('GAINNODE NOT CREATED WE WILL NOT START')
|
||||
// }
|
||||
}
|
||||
|
||||
const play = () => {
|
||||
const newHowls = howls.value
|
||||
const master = masterGain.value || useAudioStore().getMasterGainNoise()
|
||||
logger.info('Start this', { newHowls })
|
||||
howls.value.forEach(async (howl) => {
|
||||
howl.play()
|
||||
logger.info('Start this', { newHowls })
|
||||
masterGain?.value?.gain.linearRampToValueAtTime(1, audioContext.value.currentTime + 3)
|
||||
const mediaElement = await (howl as any)._sounds[0]._node as HTMLAudioElement
|
||||
mediaElement.muted = false
|
||||
}
|
||||
)
|
||||
master.connect(audioContext.value.destination)
|
||||
// if (gainNode) {
|
||||
// gainNode.connect(audioContext.value.destination)
|
||||
// gainNode.gain.cancelScheduledValues(audioContext.value.currentTime)
|
||||
// gainNode.gain.setValueAtTime(0, audioContext.value.currentTime)
|
||||
// gainNode.gain.linearRampToValueAtTime(2, audioContext.value.currentTime + 4)
|
||||
// } else {
|
||||
// logger.info('GAINNODE NOT CREATED WE WILL NOT START')
|
||||
// }
|
||||
}
|
||||
</script>
|
105
pages/experiments/Player/test1.vue
Normal file
105
pages/experiments/Player/test1.vue
Normal file
@@ -0,0 +1,105 @@
|
||||
<!--
|
||||
/**
|
||||
* @component ControlValueBasedPlayer
|
||||
* @description A component that renders multiple NoiseControlledBand components,
|
||||
* each centered around a specific frequency. This can be used for
|
||||
* audio spectrum visualization or control. Now includes a loading spinner
|
||||
* that disappears when all NoiseControlledBand components are ready.
|
||||
*
|
||||
* @uses NoiseControlledBand
|
||||
*
|
||||
* @example
|
||||
* <ControlValueBasedPlayer />
|
||||
*
|
||||
* @remarks
|
||||
* - Utilizes Vue 3 Composition API
|
||||
* - Renders NoiseControlledBand components for standard audio frequency bands
|
||||
* - Frequencies: 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000 Hz
|
||||
* - Includes a loading spinner that disappears when all components are ready
|
||||
*/
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1> Test Fall 1 mit Control Value Patch</h1>
|
||||
|
||||
<h3> Ein AudioTag, dass über die Volume-APi gesteuert wird</h3>
|
||||
<div v-if="isExperimentsRoute">
|
||||
<KeyboardPlayHandler />
|
||||
<PlayButton />
|
||||
</div>
|
||||
<div v-if="loading" class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">{{ t("Loading...") }}</span>
|
||||
</div>
|
||||
Loaded Bands: {{ loadedBands }}
|
||||
<div>
|
||||
<label>Attack: {{ (masterAttack / 480000).toFixed(2) }}s</label>
|
||||
<input
|
||||
v-model.number="masterAttack"
|
||||
type="range"
|
||||
:min="4800"
|
||||
:max="1920000"
|
||||
>
|
||||
<label>Release: {{ (masterRelease / 480000).toFixed(2) }}s</label>
|
||||
<input
|
||||
v-model.number="masterRelease"
|
||||
type="range"
|
||||
:min="4800"
|
||||
:max="1920000"
|
||||
>
|
||||
</div>
|
||||
<NoiseControlledBand
|
||||
v-for="frequency in frequencies"
|
||||
:key="frequency"
|
||||
:master-attack="masterAttack"
|
||||
:master-release="masterRelease"
|
||||
:center-frequency="frequency"
|
||||
@ready="onBandReady"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import KeyboardPlayHandler from '~/archive/components/KeyboardPlayHandler.vue'
|
||||
import PlayButton from '~/components/experiments/statemanagement/PlayButton.vue'
|
||||
import NoiseControlledBand from '~/components/experiments/tests/ControlValues/NoiseControlledBand.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NoiseControlledBand,
|
||||
KeyboardPlayHandler,
|
||||
PlayButton
|
||||
},
|
||||
setup () {
|
||||
const { t } = useI18n()
|
||||
const frequencies = ref([63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000])
|
||||
const loadedBands = ref(0)
|
||||
|
||||
const route = useRoute()
|
||||
const isExperimentsRoute = computed(() => route.path.match(/\/[a-z]{2}\/experiments/))
|
||||
|
||||
const masterAttack = ref(120000) // Beispielwert in Samples
|
||||
const masterRelease = ref(144000)
|
||||
|
||||
const loading = computed(() => loadedBands.value < frequencies.value.length)
|
||||
|
||||
const onBandReady = () => {
|
||||
loadedBands.value++
|
||||
}
|
||||
|
||||
return {
|
||||
frequencies,
|
||||
loading,
|
||||
onBandReady,
|
||||
t,
|
||||
loadedBands,
|
||||
masterAttack,
|
||||
masterRelease,
|
||||
isExperimentsRoute
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
74
pages/experiments/Player/test2.vue
Normal file
74
pages/experiments/Player/test2.vue
Normal file
@@ -0,0 +1,74 @@
|
||||
<template>
|
||||
<div>
|
||||
<KeyboardPlayHandler />
|
||||
<h1> Test Fall 2 mit Control Value Patch</h1>
|
||||
<h2>Use AudioBufferSourceNode over controlled NoiseControlledWebAudioBand</h2>
|
||||
<h3> press space to start</h3>
|
||||
<div>
|
||||
Masking Gain : <input
|
||||
id="gain-control"
|
||||
:onchange="updateMasterGain"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
>
|
||||
</div>
|
||||
<div v-if="loading" class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">{{ t("Loading...") }}</span>
|
||||
</div>
|
||||
Loaded Bands: {{ loadedBands }}
|
||||
<NoiseControlledWebAudioBand
|
||||
v-for="frequency in frequencies"
|
||||
v-show="false"
|
||||
:key="frequency"
|
||||
:center-frequency="frequency"
|
||||
:master-gain="masterGainNode"
|
||||
@ready="onBandReady"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import KeyboardPlayHandler from '~/archive/components/KeyboardPlayHandler.vue'
|
||||
import NoiseControlledWebAudioBand from '~/components/experiments/tests/ControlValues/NoiseControlledWebAudioBand.vue'
|
||||
import { useAudioStore } from '~/stores/audio'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NoiseControlledWebAudioBand,
|
||||
KeyboardPlayHandler
|
||||
},
|
||||
setup () {
|
||||
const { t } = useI18n()
|
||||
const frequencies = ref([63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000])
|
||||
const loadedBands = ref(0)
|
||||
const masterGainNode = computed(() => { return useAudioStore().getMasterGainNoise() })
|
||||
const masterAttack = ref(120000 * 2) // Beispielwert in Samples
|
||||
const masterRelease = ref(144000 * 2)
|
||||
const loading = computed(() => loadedBands.value < frequencies.value.length)
|
||||
|
||||
const updateMasterGain = (changeEvent:Event) => {
|
||||
const newValue = changeEvent?.target as any
|
||||
masterGainNode.value.gain.linearRampToValueAtTime(newValue.value, masterGainNode.value.context.currentTime + 0.25)
|
||||
}
|
||||
const onBandReady = () => {
|
||||
loadedBands.value++
|
||||
}
|
||||
|
||||
return {
|
||||
frequencies,
|
||||
loading,
|
||||
onBandReady,
|
||||
t,
|
||||
loadedBands,
|
||||
masterGainNode,
|
||||
updateMasterGain,
|
||||
masterAttack,
|
||||
masterRelease
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
110
pages/experiments/Player/test3.vue
Normal file
110
pages/experiments/Player/test3.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<!--
|
||||
/**
|
||||
* @component ControlValueBasedPlayer
|
||||
* @description A component that renders multiple NoiseControlledBand components,
|
||||
* each centered around a specific frequency. This can be used for
|
||||
* audio spectrum visualization or control. Now includes a loading spinner
|
||||
* that disappears when all NoiseControlledBand components are ready.
|
||||
*
|
||||
* @uses NoiseControlledBand
|
||||
*
|
||||
* @example
|
||||
* <ControlValueBasedPlayer />
|
||||
*
|
||||
* @remarks
|
||||
* - Utilizes Vue 3 Composition API
|
||||
* - Renders NoiseControlledBand components for standard audio frequency bands
|
||||
* - Frequencies: 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000 Hz
|
||||
* - Includes a loading spinner that disappears when all components are ready
|
||||
*/
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h1> Test Fall 3 mit Control Value Patch</h1>
|
||||
<h3> Drei AudioTags gesteuert über 3 RNBOControlValues. Über die Volume-APi wird die Lautstärke gesteuert</h3>
|
||||
<div v-if="isExperimentsRoute">
|
||||
<KeyboardPlayHandler />
|
||||
<PlayButton />
|
||||
|
||||
<div v-if="loading" class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">{{ t("Loading...") }}</span>
|
||||
</div>
|
||||
Loaded Bands: {{ loadedBands }}
|
||||
<div>
|
||||
<label>Attack: {{ (masterAttack / 480000).toFixed(2) }}s</label>
|
||||
<input
|
||||
v-model.number="masterAttack"
|
||||
type="range"
|
||||
:min="4800"
|
||||
:max="1920000"
|
||||
>
|
||||
<label>Release: {{ (masterRelease / 480000).toFixed(2) }}s</label>
|
||||
<input
|
||||
v-model.number="masterRelease"
|
||||
type="range"
|
||||
:min="4800"
|
||||
:max="1920000"
|
||||
>
|
||||
</div>
|
||||
<NoiseControlled3Band
|
||||
v-for="(frequency, index) in frequencies"
|
||||
:key="frequency"
|
||||
:master-attack="masterAttack"
|
||||
:master-release="masterRelease"
|
||||
:center-frequency="frequency"
|
||||
:q-factor="qFactors[index]"
|
||||
@ready="onBandReady"
|
||||
@update:mid-volume="controlMusicGain"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import KeyboardPlayHandler from '~/archive/components/KeyboardPlayHandler.vue'
|
||||
import PlayButton from '~/components/experiments/statemanagement/PlayButton.vue'
|
||||
import NoiseControlled3Band from '~/components/experiments/tests/ControlValues/NoiseControlled3Band.vue'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NoiseControlled3Band,
|
||||
KeyboardPlayHandler,
|
||||
PlayButton
|
||||
},
|
||||
setup () {
|
||||
const { t } = useI18n()
|
||||
const frequencies = ref([150, 1500, 8000])
|
||||
const qFactors = ref([0.8, 0.9, 0.6])
|
||||
const loadedBands = ref(0)
|
||||
|
||||
const route = useRoute()
|
||||
const isExperimentsRoute = computed(() => route.path.match(/\/[a-z]{2}\/experiments/))
|
||||
|
||||
const masterAttack = ref(120000 * 2) // Beispielwert in Samples
|
||||
const masterRelease = ref(144000 * 2)
|
||||
|
||||
const loading = computed(() => loadedBands.value < frequencies.value.length)
|
||||
|
||||
const onBandReady = () => {
|
||||
loadedBands.value++
|
||||
}
|
||||
|
||||
return {
|
||||
frequencies,
|
||||
loading,
|
||||
onBandReady,
|
||||
t,
|
||||
loadedBands,
|
||||
masterAttack,
|
||||
masterRelease,
|
||||
isExperimentsRoute,
|
||||
qFactors,
|
||||
controlMusicGain
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
180
pages/experiments/Player/test4.vue
Normal file
180
pages/experiments/Player/test4.vue
Normal file
@@ -0,0 +1,180 @@
|
||||
<!--
|
||||
/**
|
||||
* @component ControlValueBasedPlayer
|
||||
* @description A component that renders multiple NoiseControlledBand components,
|
||||
* each centered around a specific frequency. This can be used for
|
||||
* audio spectrum visualization or control. Now includes a loading spinner
|
||||
* that disappears when all NoiseControlledBand components are ready.
|
||||
*
|
||||
* @uses NoiseControlledBand
|
||||
*
|
||||
* @example
|
||||
* <ControlValueBasedPlayer />
|
||||
*
|
||||
* @remarks
|
||||
* - Utilizes Vue 3 Composition API
|
||||
* - Renders NoiseControlledBand components for standard audio frequency bands
|
||||
* - Frequencies: 63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000 Hz
|
||||
* - Includes a loading spinner that disappears when all components are ready
|
||||
*/
|
||||
-->
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<div v-if="isExperimentsRoute">
|
||||
<h1> Test Fall 4 mit Control Value Patch</h1>
|
||||
<h3> Drei WebAudioTag gesteuert über 3 RNBOControlValues. Über die WebAudio-APi wird die Lautstärke gesteuert</h3>
|
||||
<KeyboardPlayHandler />
|
||||
<PlayButton />
|
||||
|
||||
<div v-if="loading" class="spinner-border spinner-border-sm" role="status">
|
||||
<span class="sr-only">{{ t("Loading...") }}</span>
|
||||
</div>
|
||||
Loaded Bands: {{ loadedBands }}
|
||||
<div>
|
||||
<label>Attack: {{ (masterAttack / 480000).toFixed(2) }}s</label>
|
||||
<input
|
||||
v-model.number="masterAttack"
|
||||
type="range"
|
||||
:min="4800"
|
||||
:max="1920000"
|
||||
>
|
||||
<label>Release: {{ (masterRelease / 480000).toFixed(2) }}s</label>
|
||||
<input
|
||||
v-model.number="masterRelease"
|
||||
type="range"
|
||||
:min="4800"
|
||||
:max="1920000"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<NoiseControlled3BandWebAudio
|
||||
v-for="(frequency, index) in frequencies"
|
||||
v-show="false"
|
||||
:key="frequency"
|
||||
:master-attack="masterAttack"
|
||||
:master-release="masterRelease"
|
||||
:center-frequency="frequency"
|
||||
:master-gain="masterGain"
|
||||
:q-factor="qFactors[index]"
|
||||
@ready="onBandReady"
|
||||
@update:mid-volume="controlMusicGain"
|
||||
/>
|
||||
<div class="slider-wrapper">
|
||||
<img
|
||||
v-if="muted"
|
||||
style="width: 25px; height: 25px;"
|
||||
src="~/assets/image/sound_muted.svg"
|
||||
title="Click to unmute"
|
||||
@click="toggleMute()"
|
||||
>
|
||||
<img
|
||||
v-else
|
||||
style="width: 25px; height: 25px;"
|
||||
src="~/assets/image/sound.svg"
|
||||
title="Click to mute"
|
||||
@click="toggleMute()"
|
||||
>
|
||||
<div class="slider">
|
||||
<input
|
||||
id="gain-control"
|
||||
v-model="masterGain.gain.value"
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.02"
|
||||
data-toggle="tooltip"
|
||||
data-placement="top"
|
||||
title="Change the volume by click, scroll or touch"
|
||||
@wheel.prevent="changeVolumeOnWheel"
|
||||
>
|
||||
<span
|
||||
class="slider-progress-bar"
|
||||
:style="{ width: `${masterGain.gain.value * 100}%` }"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, computed } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useRoute } from 'vue-router'
|
||||
import KeyboardPlayHandler from '~/archive/components/KeyboardPlayHandler.vue'
|
||||
import PlayButton from '~/components/experiments/statemanagement/PlayButton.vue'
|
||||
import NoiseControlled3BandWebAudio from '~/components/experiments/tests/ControlValues/NoiseControlledWebAudio3Band.vue'
|
||||
import { useAudioStore } from '~/stores/audio'
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
NoiseControlled3BandWebAudio,
|
||||
KeyboardPlayHandler,
|
||||
PlayButton
|
||||
},
|
||||
setup () {
|
||||
const masterGain = ref(useAudioStore().getMasterGainNoise())
|
||||
const { t } = useI18n()
|
||||
const frequencies = ref([150, 1500, 8000])
|
||||
const qFactors = ref([0.8, 0.9, 0.6])
|
||||
const loadedBands = ref(0)
|
||||
const muted = computed(() => useAudioStore().getMasterGainNoise().gain.value === 0)
|
||||
let oldVolume = 0
|
||||
|
||||
const route = useRoute()
|
||||
const isExperimentsRoute = computed(() => route.path.match(/\/[a-z]{2}\/experiments/))
|
||||
|
||||
const masterAttack = ref(120000 * 2) // Beispielwert in Samples
|
||||
const masterRelease = ref(144000 * 2)
|
||||
|
||||
const loading = computed(() => loadedBands.value < frequencies.value.length)
|
||||
|
||||
const onBandReady = () => {
|
||||
loadedBands.value++
|
||||
}
|
||||
const toggleMute = () => {
|
||||
if (!muted.value) {
|
||||
oldVolume = masterGain.value.gain.value
|
||||
masterGain.value.gain.linearRampToValueAtTime(0, masterGain.value.context.currentTime + 0.4)
|
||||
} else if (oldVolume > 0) {
|
||||
masterGain.value.gain.linearRampToValueAtTime(oldVolume, masterGain.value.context.currentTime + 0.4)
|
||||
} else {
|
||||
masterGain.value.gain.linearRampToValueAtTime(1, masterGain.value.context.currentTime + 0.4)
|
||||
}
|
||||
}
|
||||
|
||||
const controlMusicGain = (value: string) => {
|
||||
|
||||
}
|
||||
const changeVolumeOnWheel = (event:WheelEvent) => {
|
||||
let gainValue = masterGain.value.gain.value
|
||||
// Adjust volume on wheel scroll
|
||||
const deltaY = event.deltaY
|
||||
if (deltaY < 0) {
|
||||
const volumeAdd = (Math.min(1, gainValue + 0.02))
|
||||
gainValue = volumeAdd
|
||||
} else {
|
||||
const volumeCut = (Math.max(0, gainValue - 0.02))
|
||||
gainValue = volumeCut
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
frequencies,
|
||||
loading,
|
||||
onBandReady,
|
||||
t,
|
||||
loadedBands,
|
||||
masterAttack,
|
||||
masterRelease,
|
||||
isExperimentsRoute,
|
||||
qFactors,
|
||||
controlMusicGain,
|
||||
masterGain,
|
||||
changeVolumeOnWheel,
|
||||
toggleMute,
|
||||
muted
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
50
pages/experiments/Spotify.vue
Normal file
50
pages/experiments/Spotify.vue
Normal file
@@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<div id="spotify">
|
||||
<spotify-embed :spotify-uri="currentSpotifyUri" />
|
||||
<pomodoro-playlist />
|
||||
<state-bar />
|
||||
<button @click="changeTrack">
|
||||
Change Track
|
||||
</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import SpotifyEmbed from '@/components/experiments/spotify/SpotifyEmbed.vue'
|
||||
import PomodoroPlaylist from '@/components/experiments/spotify/PomodoroPlaylist.vue'
|
||||
import StateBar from '@/components/experiments/statemanagement/StateBar.vue'
|
||||
export default {
|
||||
components: {
|
||||
SpotifyEmbed, PomodoroPlaylist, StateBar
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
audioList: [
|
||||
{ id: 1, title: 'Lagoon', src: window.location.origin + '/sounds/lagoon.m4a', spotifyUri: 'track/4O2wnyNQ2NLmd5BHkLTgu2' },
|
||||
{ id: 2, title: 'Forest', src: window.location.origin + '/sounds/forest.m4a', spotifyUri: 'track/0MEMvf26SLFCXhnPj1qXJ1' },
|
||||
{ id: 3, title: 'Meadow', src: window.location.origin + '/sounds/meadow.m4a', spotifyUri: 'track/48NC8HTZo0N3Kdpicx9wVp' },
|
||||
{ id: 4, title: 'Tropics', src: window.location.origin + '/sounds/tropics.m4a', spotifyUri: 'track/12QLlNzfZTmSO6OMrjujKt' }
|
||||
],
|
||||
currentSpotifyUri: 'track/1hR0fIFK2qRG3f3RF70pb7' // Default URI
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeTrack () {
|
||||
const newSong = this.getSong(this.currentSpotifyUri, 'next')
|
||||
this.currentSpotifyUri = newSong.spotifyUri // Change this to the new URI
|
||||
},
|
||||
getSong (currentTitle, direction) {
|
||||
const index = this.audioList.findIndex(song => song.spotifyUri === 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]
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
14
pages/experiments/controlGainsByValue/ControlGains.vue
Normal file
14
pages/experiments/controlGainsByValue/ControlGains.vue
Normal file
@@ -0,0 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<h3>
|
||||
component: TotalGainController darin enthalten, AudioTagWebAudio & RNBOControlValue, AudioFileSelector <br>
|
||||
features: Anpassbare Ramptime <br>
|
||||
rnbo: ja
|
||||
</h3>
|
||||
<GainController audio-src="" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import GainController from '~/components/experiments/TotalGainController.vue'
|
||||
</script>
|
7
pages/experiments/controlValues/ControlValues.vue
Normal file
7
pages/experiments/controlValues/ControlValues.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<RNBOControlValue />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import RNBOControlValue from '@/components/experiments/tests/ControlValues/RNBOControlValue.vue'
|
||||
</script>
|
106
pages/experiments/controlValues/ControlValues9.vue
Normal file
106
pages/experiments/controlValues/ControlValues9.vue
Normal file
@@ -0,0 +1,106 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Test Fall 3</h1>
|
||||
<h3>Dynamic Patches in parallel</h3>
|
||||
<div>
|
||||
<label for="patchCount">Number of Patches:</label>
|
||||
<input
|
||||
id="patchCount"
|
||||
v-model.number="patchCount"
|
||||
type="number"
|
||||
min="1"
|
||||
max="20"
|
||||
:disabled="isStarted"
|
||||
>
|
||||
</div>
|
||||
<button :disabled="isStarted" @click="startAllPatches">Start All Patches</button>
|
||||
<table v-if="isStarted">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="index in patchCount" :key="index">Patch {{ index }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td v-for="(value, index) in controlValues" :key="index">{{ value?.value.toFixed(2) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<RNBOControlValue
|
||||
v-for="index in patchCount"
|
||||
:key="index"
|
||||
:ref="el => { if (el) rnboRefs[index - 1] = el }"
|
||||
:center-frequency="centerFrequencies.at(index - 1)"
|
||||
@control-value-change="updateValueTable(index - 1, $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import RNBOControlValue from '@/components/experiments/tests/ControlValues/RNBOControlValue.vue'
|
||||
|
||||
const patchCount = ref(9)
|
||||
const controlValues = ref([])
|
||||
const rnboRefs = ref([])
|
||||
const isStarted = ref(false)
|
||||
const centerFrequencies = ref([63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000])
|
||||
const updateValueTable = (index, value) => {
|
||||
controlValues.value[index] = value
|
||||
}
|
||||
|
||||
const startAllPatches = () => {
|
||||
isStarted.value = true
|
||||
rnboRefs.value.forEach((rnbo) => {
|
||||
if (rnbo && typeof rnbo.testControlValuesDevice === 'function') {
|
||||
rnbo.testControlValuesDevice()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const formatValue = (value) => {
|
||||
return Number(value).toFixed(2)
|
||||
}
|
||||
|
||||
watch(patchCount, (newCount) => {
|
||||
controlValues.value = new Array(newCount).fill(0)
|
||||
rnboRefs.value = new Array(newCount).fill(null)
|
||||
})
|
||||
onMounted(() => {
|
||||
controlValues.value = new Array(patchCount.value).fill(0)
|
||||
rnboRefs.value = new Array(patchCount.value).fill(null)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
button, input {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input:disabled, button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
155
pages/experiments/controlValues/ControlValuesAudio.vue
Normal file
155
pages/experiments/controlValues/ControlValuesAudio.vue
Normal file
@@ -0,0 +1,155 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Dynamic Patches in parallel mit knistern</h1>
|
||||
<p>In diesem Test funktioniert die Messung der Störwerte sowie eine Umwandlung nach StevensLoudness. Verwendet wird hier eine AudioBufferSourceNode pro Band.</p>
|
||||
<div>
|
||||
<label for="patchCount">Number of Patches:</label>
|
||||
<input
|
||||
id="patchCount"
|
||||
v-model.number="patchCount"
|
||||
type="number"
|
||||
min="1"
|
||||
max="20"
|
||||
:disabled="isStarted"
|
||||
>
|
||||
</div>
|
||||
<button :disabled="isStarted" @click="startAllPatches">Start All Patches</button>
|
||||
<table v-if="isStarted">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="index in patchCount" :key="index">Patch {{ index }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td v-for="(value, index) in controlValues" :key="index">{{ formatValue(value.value) }} </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-for="(value, index) in controlValues" :key="index">{{ formatValue(dBToStevensLoudness(value?.value)) }} </td>
|
||||
</tr>
|
||||
<!--
|
||||
<tr>
|
||||
<td v-for="(value, index) in controlValues" :key="index"> <AudioTagWebAudio :src="audioSources[index]" :volume="dBToStevensLoudness(value?.value)" /> </td>
|
||||
</tr>
|
||||
-->
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<AudioTagWebAudio
|
||||
v-for="index in patchCount"
|
||||
:key="index"
|
||||
:ref="el => { if (el) audioRefs[index - 1] = el }"
|
||||
:src="audioSources.at(index-1)"
|
||||
:volume="dBToStevensLoudness(controlValues[index])"
|
||||
/>
|
||||
|
||||
<RNBOControlValue
|
||||
v-for="index in patchCount"
|
||||
:key="index"
|
||||
:ref="el => { if (el) rnboRefs[index - 1] = el }"
|
||||
:center-frequency="centerFrequencies.at(index-1)"
|
||||
@control-value-change="updateValueTable(index - 1, $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import RNBOControlValue from '@/components/experiments/tests/ControlValues/RNBOControlValue.vue'
|
||||
import AudioTagWebAudio from '~/components/experiments/AudioTagWebAudio.vue'
|
||||
import { calculateNormalizedVolume } from '~/lib/AudioFunctions'
|
||||
|
||||
const patchCount = ref(9)
|
||||
const controlValues = ref([])
|
||||
const rnboRefs = ref([])
|
||||
const audioRefs = ref([])
|
||||
const isStarted = ref(false)
|
||||
const centerFrequencies = ref([63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000])
|
||||
const audioSources = ref(
|
||||
[
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen63.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen125.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen250.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen500.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen1000.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen2000.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen4000.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen8000.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen16000.wav'
|
||||
])
|
||||
const updateValueTable = (index, value) => {
|
||||
controlValues.value[index] = value
|
||||
}
|
||||
|
||||
const startAllPatches = () => {
|
||||
isStarted.value = true
|
||||
rnboRefs.value.forEach((rnbo) => {
|
||||
if (rnbo && typeof rnbo.testControlValuesDevice === 'function') {
|
||||
rnbo.testControlValuesDevice()
|
||||
}
|
||||
})
|
||||
audioRefs.value.forEach((audio) => {
|
||||
useNuxtApp().$logger.log('AudioRef = ', { audio })
|
||||
})
|
||||
}
|
||||
|
||||
const formatValue = (value) => {
|
||||
return Number(value).toFixed(2)
|
||||
}
|
||||
|
||||
const mapLinearRange = (
|
||||
value,
|
||||
inMin,
|
||||
inMax,
|
||||
outMin,
|
||||
outMax
|
||||
) => {
|
||||
const clamped = Math.max(inMin, Math.min(value, inMax)) // optional: clamp
|
||||
return ((clamped - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin
|
||||
}
|
||||
|
||||
const dBToStevensLoudness = (db) => {
|
||||
return calculateNormalizedVolume(db)
|
||||
}
|
||||
|
||||
watch(patchCount, (newCount) => {
|
||||
controlValues.value = new Array(newCount).fill(0)
|
||||
rnboRefs.value = new Array(newCount).fill(null)
|
||||
})
|
||||
onMounted(() => {
|
||||
controlValues.value = new Array(patchCount.value).fill(0)
|
||||
rnboRefs.value = new Array(patchCount.value).fill(null)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
button, input {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input:disabled, button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
143
pages/experiments/controlValues/ControlValuesAudioStreaming.vue
Normal file
143
pages/experiments/controlValues/ControlValuesAudioStreaming.vue
Normal file
@@ -0,0 +1,143 @@
|
||||
<template>
|
||||
<div>
|
||||
<h1>Dynamic Patches in parallel</h1>
|
||||
<p>In diesem Test funktioniert die Messung der Störwerte sowie eine Umwandlung nach StevensLoudness, nach dem Starten der Audio-Nodes (fetch) wird der AudioContext gestoppt. Eventuell von Howler </p>
|
||||
<div>
|
||||
<label for="patchCount">Number of Patches:</label>
|
||||
<input
|
||||
id="patchCount"
|
||||
v-model.number="patchCount"
|
||||
type="number"
|
||||
min="1"
|
||||
max="20"
|
||||
:disabled="isStarted"
|
||||
>
|
||||
</div>
|
||||
<button :disabled="isStarted" @click="startAllPatches">Start All Patches</button>
|
||||
<table v-if="isStarted">
|
||||
<thead>
|
||||
<tr>
|
||||
<th v-for="index in patchCount" :key="index">Patch {{ index }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td v-for="(value, index) in controlValues" :key="index">{{ formatValue(value.value) }} </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-for="(value, index) in controlValues" :key="index">{{ formatValue(dBToStevensLoudness(value?.value)) }} </td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td v-for="(value, index) in controlValues" :key="index"> <AudioTagWebAudioStreaming :src="audioSources[index]" :volume="dBToStevensLoudness(value?.value)" /> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<RNBOControlValue
|
||||
v-for="index in patchCount"
|
||||
:key="index"
|
||||
:ref="el => { if (el) rnboRefs[index - 1] = el }"
|
||||
:center-frequency="centerFrequencies.at(index-1)"
|
||||
@control-value-change="updateValueTable(index - 1, $event)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, watch } from 'vue'
|
||||
import RNBOControlValue from '@/components/experiments/tests/ControlValues/RNBOControlValue.vue'
|
||||
import AudioTagWebAudioStreaming from '~/components/experiments/AudioTagWebAudioStreaming.vue'
|
||||
|
||||
const patchCount = ref(9)
|
||||
const controlValues = ref([])
|
||||
const rnboRefs = ref([])
|
||||
const isStarted = ref(false)
|
||||
const centerFrequencies = ref([63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000])
|
||||
const audioSources = ref(
|
||||
[
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen63.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen125.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen250.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen500.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen1000.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen2000.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen4000.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen8000.wav',
|
||||
'https://192.168.188.182:3000/masking/bands/Quellrauschen16000.wav'
|
||||
])
|
||||
const updateValueTable = (index, value) => {
|
||||
controlValues.value[index] = value
|
||||
}
|
||||
|
||||
const startAllPatches = () => {
|
||||
isStarted.value = true
|
||||
rnboRefs.value.forEach((rnbo) => {
|
||||
if (rnbo && typeof rnbo.testControlValuesDevice === 'function') {
|
||||
rnbo.testControlValuesDevice()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const formatValue = (value) => {
|
||||
return Number(value).toFixed(2)
|
||||
}
|
||||
|
||||
const mapLinearRange = (
|
||||
value,
|
||||
inMin,
|
||||
inMax,
|
||||
outMin,
|
||||
outMax
|
||||
) => {
|
||||
const clamped = Math.max(inMin, Math.min(value, inMax)) // optional: clamp
|
||||
return ((clamped - inMin) / (inMax - inMin)) * (outMax - outMin) + outMin
|
||||
}
|
||||
|
||||
const dBToStevensLoudness = (db, exponent = 0.3, minDb = -60) => {
|
||||
const clampedDb = Math.max(db, minDb)
|
||||
const intensity = Math.pow(10, clampedDb / 10)
|
||||
const loudness = Math.pow(intensity, exponent)
|
||||
return loudness
|
||||
}
|
||||
|
||||
watch(patchCount, (newCount) => {
|
||||
controlValues.value = new Array(newCount).fill(0)
|
||||
rnboRefs.value = new Array(newCount).fill(null)
|
||||
})
|
||||
onMounted(() => {
|
||||
controlValues.value = new Array(patchCount.value).fill(0)
|
||||
rnboRefs.value = new Array(patchCount.value).fill(null)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
th, td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
button, input {
|
||||
margin: 10px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
button {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input:disabled, button:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
Reference in New Issue
Block a user