mindboost-rnbo-test-project/components/experiments/GainController.vue

170 lines
4.9 KiB
Vue

<template>
<div>
<div v-if="error" class="error-message">
{{ error }}
</div>
<AudioFileSelector @file-selected="onFileSelected" />
<!-- Ramp Time Control -->
<div class="ramp-time-control">
<label for="rampTime">Ramp Time (ms):</label>
<input
id="rampTime"
v-model="rampTime"
type="range"
min="100"
max="50000"
step="100"
>
<span>{{ rampTime }}ms</span>
</div>
<div class="gain-controller">
<div v-for="frequency in frequencies" :key="frequency" class="frequency-control">
<RNBOControlValue
:center-frequency="frequency"
@control-value-change="(value) => handleValueChange(value.frequency, value.value)"
/>
<AudioTagWebAudio
:ref="el => { if (el) audioElements[frequency] = el }"
:src="audioSrc"
:volume="currentVolumes[frequency]"
/>
<div>
Frequency: {{ frequency }}Hz
<br>Gain Value (dB): {{ gainValuesDB[frequency] }}
<br>Normalized: {{ normalizedVolumes[frequency] }}
<br>Volume: {{ currentVolumes[frequency] }}
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, onUnmounted, reactive } from 'vue'
import AudioFileSelector from '../AudioFileSelector.vue'
import AudioTagWebAudio from './AudioTagWebAudio.vue'
import RNBOControlValue from './tests/ControlValues/RNBOControlValue.vue'
import { useAudioStore } from '~/stores/audio'
export default defineComponent({
name: 'GainController',
components: {
RNBOControlValue,
AudioTagWebAudio,
AudioFileSelector
},
setup () {
const logger = useNuxtApp().$logger
logger.info('GainController setup')
const audioStore = useAudioStore()
logger.info('Got audioStore', audioStore)
const audioSrc = ref(window.location.origin + '/sounds/lagoon.ogg')
const rampTime = ref(25000)
logger.info('Set rampTime', 25000)
const frequencies = ref([63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000])
logger.info('Set frequencies', { frequencies })
const error = ref<string | null>(null)
const gainValuesDB = reactive<Record<number, number>>({})
const currentVolumes = reactive<Record<number, number>>({})
const normalizedVolumes = reactive<Record<number, number>>({})
const audioElements = reactive<Record<number, InstanceType<typeof AudioTagWebAudio>>>({})
const rampIntervals: Record<number, ReturnType<typeof setInterval> | null> = {}
const onFileSelected = (file: string) => {
const fullPath = new URL(file, window.location.origin).toString()
audioSrc.value = fullPath
logger.info('User hat ein File ausgewählt ' + file)
useNuxtApp().$logger.log({ audioElements })
setTimeout(() => {
audioStore.setPlaying(true)
}, 250)
}
const calculateNormalizedVolume = (db: number): number => {
const minDB = -12
const maxDB = 2
return (db - minDB) / (maxDB - minDB)
}
const rampVolume = (frequency: number, targetVolume: number, duration: number) => {
const startVolume = currentVolumes[frequency] || 1
const startTime = Date.now()
const endTime = startTime + duration
if (rampIntervals[frequency]) {
clearInterval(rampIntervals[frequency]!)
}
rampIntervals[frequency] = setInterval(() => {
const now = Date.now()
if (now >= endTime) {
currentVolumes[frequency] = targetVolume
clearInterval(rampIntervals[frequency]!)
rampIntervals[frequency] = null
return
}
const progress = (now - startTime) / duration
currentVolumes[frequency] = startVolume + (targetVolume - startVolume) * progress
}, 50)
}
const handleValueChange = (frequency: number, value: number) => {
logger.info('Change for ' + frequency + ' to ' + value)
if (!isNaN(value)) {
gainValuesDB[frequency] = value
normalizedVolumes[frequency] = calculateNormalizedVolume(value)
rampVolume(frequency, normalizedVolumes[frequency], rampTime.value)
} else {
useNuxtApp().$logger.log('value is not NaN', { value })
}
}
onUnmounted(() => {
Object.values(rampIntervals).forEach((interval) => {
if (interval) { clearInterval(interval) }
})
})
return {
frequencies,
gainValuesDB,
currentVolumes,
normalizedVolumes,
audioElements,
handleValueChange,
error,
onFileSelected,
audioSrc,
rampTime
}
}
})
</script>
<style scoped>
.error-message {
color: red;
font-weight: bold;
padding: 10px;
border: 1px solid red;
border-radius: 5px;
margin-bottom: 10px;
}
.ramp-time-control {
margin-top: 20px;
}
.ramp-time-control input {
width: 300px;
margin: 0 10px;
}
.frequency-control {
margin-bottom: 20px;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px;
}
</style>