mindboost-rnbo-test-project/components/experiments/tests/ControlValues/RNBOControlValue.vue

317 lines
9.6 KiB
Vue

<!--
RNBOControlValue ist eine Komponente zur Steuerung und Überwachung von Audio-Parametern,
insbesondere für die Kontrolle von Frequenzbändern. Sie ermöglicht das Testen von
Audio-Geräten, die Anpassung von Parametern und die Anzeige von Kontrollwerten in Echtzeit.
-->
<template>
<div v-if="true">
<h2>Control Values Device Test -- {{ centerFrequency }}Hz</h2>
<button @click="testControlValuesDevice">Test Control Values Device</button>
<p v-if="testResult">{{ testResult }}</p>
<div class="microphone-info">
<h3>Current Microphone:</h3>
<p>{{ currentMicrophone || 'No microphone selected' }}</p>
</div>
<table v-if="parameters.length > 0" class="parameter-table">
<thead>
<tr>
<th>Parameter Name</th>
<th>Value</th>
<th>Action</th>
</tr>
</thead>
<tbody>
<tr v-for="param in parameters" :key="param.name">
<td>{{ param.name }}</td>
<td>
<input
:value="param.value"
type="number"
@input="updateParameter(param.name, $event.target?.value)"
>
</td>
<td>
<input
:value="param.value"
type="range"
:min="param.min"
:max="param.max"
:step="param.step"
@input="updateParameter(param.name, $event.target?.value)"
>
</td>
</tr>
</tbody>
</table>
<!-- Neue Keynote für den Outlet-Wert -->
<div class="outlet-keynote">
<h3>Control Value</h3>
<div class="outlet-value">{{ formatValue(outletValue) }}</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent } from 'vue'
import { mapActions, mapState } from 'pinia'
import { useDevicesStore } from '~/stores/device'
import { useMicStore } from '~/stores/microphone'
import { useAudioStore } from '~/stores/audio'
interface ParameterData {
name: string;
paramId: string;
value: number;
min: number;
max: number;
step: number;
}
const minSamples = 48
const maxSamples = 1920000
export default defineComponent({
name: 'RNBOControlValue',
props: {
centerFrequency: {
type: Number,
required: true,
validator: (value: number) => [63, 125, 250, 500, 1000, 1500, 2000, 4000, 8000, 16000, 150].includes(value)
},
qFactor: {
type: Number,
default: 1.414,
validator: (value: number) => value > 0.1 && value < 1.5
},
attack: {
type: Number,
default: 120000,
validator: (value:number) => value >= minSamples && value <= maxSamples
},
release: {
type: Number,
default: 144000,
validator: (value:number) => value >= minSamples && value <= maxSamples
},
status: {
type: Boolean,
default: false
}
},
emits: ['control-value-change'],
data () {
return {
testResult: '',
parameters: [] as ParameterData[],
device: null as any,
outletValue: 0,
currentMicrophone: ''
}
},
computed: {
...mapState(useMicStore, ['microphone'])
},
watch: {
release (value) {
this.updateParameter('release', '' + value)
},
attack (value) {
if (!isNaN(value)) {
this.updateParameter('attack', '' + value)
}
},
status (newStatus) {
const microphoneStore = useMicStore()
// status is bind to playing status in audio store
if (newStatus) {
microphoneStore.attachMicrophone()
this.testControlValuesDevice()
} else {
microphoneStore.detachMicrophone()
}
}
},
mounted () {
this.updateParameter('centerFrequency', '' + this.centerFrequency)
this.updateParameter('release', '' + this.release)
this.updateParameter('attack', '' + this.attack)
this.updateParameter('qFactor', String(this.qFactor))
},
methods: {
async getCurrentMicrophone () {
const micStore = useMicStore()
try {
const micro = await micStore.getMicrophone()
if (micro?.microphoneNode.label) {
this.currentMicrophone = micro.microphoneNode.label
} else {
this.currentMicrophone = 'No microphone detected'
}
} catch (error) {
useNuxtApp().$logger.error('Error getting microphone:', error)
this.currentMicrophone = 'Error detecting microphone'
}
},
async testControlValuesDevice () {
const deviceStore = useDevicesStore()
const micStore = useMicStore()
const audioStore = useAudioStore()
await audioStore.getContext()
if (!audioStore) { return }
if (audioStore.audioContext?.state !== 'running') {
await audioStore.audioContext?.resume()
}
this.device = await deviceStore.createControlValuesDevice(`testControlValues_${this.centerFrequency}Hz`, this.centerFrequency)
if (!this.device) {
this.testResult = `Failed to create control values device for ${this.centerFrequency}Hz`
// this.$logger.error('Device creation failed')
}
const device = this.device
// this.$logger.info(`Created device for ${this.centerFrequency}Hz`, { device })
const microphone = await micStore.getMicrophone()
try {
const micSource = microphone.microphoneNode
if (micSource && this.device.node && audioStore.audioContext) {
micSource.connect(this.device.node)
await this.updateParameterList()
await new Promise(resolve => setTimeout(resolve, 100))
this.setupOutletListener()
this.testResult = `Control values device for ${this.centerFrequency}Hz created successfully`
} else {
this.testResult = `Failed to connect microphone to device for ${this.centerFrequency}Hz`
// this.$logger.error('Connection failed:', { micSource, deviceNode: this.device.node, audioContext: audioStore.audioContext })
}
} catch (error) {
// this.$logger.error(`Test failed for ${this.centerFrequency}Hz:`, error)
this.testResult = `Test failed for ${this.centerFrequency}Hz: ${error instanceof Error ? error.message : String(error)}`
// this.$logger.error(this.testResult)
}
},
setupOutletListener () {
try {
if (!this.device) {
// this.$logger.warn('Device is not available')
return
}
this.device.messageEvent.subscribe((ev: any) => {
if (ev.tag === 'out1') {
const newValue = this.ensureNumber(ev.payload)
this.outletValue = newValue
if (!(newValue > -13 && newValue < -12.98)) {
this.$emit('control-value-change', { frequency: this.centerFrequency, value: newValue })
}
}
})
} catch (error: unknown) {
// this.$logger.error(`Test failed for ${this.centerFrequency}Hz:`, error)
this.testResult = `Test failed for ${this.centerFrequency}Hz: ${error instanceof Error ? error.message : String(error)}`
}
},
updateParameterList () {
// this.$logger.info('Updating parameter list', this.device)
if (this.device && this.device.parameters) {
const params = this.device.parameters
// this.$logger.info('Parameters:', { params })
this.parameters = Array.from(params.entries()).map(([name, param]: [string, any]) => {
return {
name: param.name,
value: this.ensureNumber(param.value),
min: this.ensureNumber(param.min),
max: this.ensureNumber(param.max),
step: this.ensureNumber(param.step) || 0.01
}
})
// this.$logger.info('Updated parameters:', this.parameters)
} else {
// this.$logger.info('No parameters found')
}
},
updateParameter (name: string, value: string) {
if (this.device && this.device.parameters) {
const numValue = this.ensureNumber(value)
const param = this.parameters.find(p => p.name === name)
if (param) {
param.value = numValue
if (typeof this.device.parameters.set === 'function') {
this.device.parameters.set(name, numValue)
} else if (Array.isArray(this.device.parameters)) {
const deviceParam = this.device.parameters.find((p: any) => p.name === name)
if (deviceParam) {
deviceParam.value = numValue
}
} else if (typeof this.device.parameters === 'object') {
if (this.device.parameters[name]) {
this.device.parameters[name].value = numValue
}
}
this.$forceUpdate()
}
} else {
// Device noch nicht da → update nur in local parameters
const param = this.parameters.find(p => p.name === name)
if (param) {
param.value = this.ensureNumber(value)
} else {
this.parameters.push({ name, value: this.ensureNumber(value) })
}
this.$forceUpdate()
}
},
ensureNumber (value: any): number {
const num = Number(value)
return isNaN(num) ? 0 : num
},
formatValue (value: number): string {
return value.toFixed(2)
}
}
})
</script>
<style scoped>
.parameter-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.parameter-table th, .parameter-table td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
.parameter-table th {
background-color: #f2f2f2;
}
.parameter-table input[type="range"] {
width: 100%;
}
.outlet-keynote {
text-align: center;
margin-top: 30px;
}
.outlet-keynote h3 {
font-size: 1.2em;
margin-bottom: 10px;
}
.outlet-value {
font-size: 2em;
font-weight: bold;
}
</style>