225 lines
7.8 KiB
Vue
225 lines
7.8 KiB
Vue
|
<template>
|
||
|
<div>
|
||
|
<button @click="startAudio">Start</button>
|
||
|
<button @click="stopAudio">Stop</button>
|
||
|
<div>{{ level }}</div>
|
||
|
Masking Gain : <input id="gain-control" v-model="maxGain1000" type="range" min="0" max="1" step="0.01">
|
||
|
Harmonic Gain : <input id="gain-control" v-model="harmonicGain" type="range" min="0" max="1" step="0.01">
|
||
|
</div>
|
||
|
</template>
|
||
|
|
||
|
<script>
|
||
|
export default {
|
||
|
data() {
|
||
|
return {
|
||
|
audioContext: null,
|
||
|
analyserNode: null,
|
||
|
bufferSize: 4096,
|
||
|
snapshotInterval: 125,
|
||
|
snapshotDuration: 5512.5,
|
||
|
level: 0,
|
||
|
snapshots: [],
|
||
|
intervalId: null,
|
||
|
bandPass1:null,
|
||
|
Gain1000:null,
|
||
|
SoundNode:null,
|
||
|
source100:null,
|
||
|
maxGain1000:0.5,
|
||
|
lastGainValue:0,
|
||
|
maskingGain1000:0,
|
||
|
harmonicGain:1,
|
||
|
maskGainControl:0,
|
||
|
};
|
||
|
},
|
||
|
mounted() {
|
||
|
this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
|
||
|
this.source100=this.audioContext.createBufferSource()
|
||
|
this.maskGainControl=this.audioContext.createGain();
|
||
|
fetch('/masking/MaskingNoise_1000.aac')
|
||
|
.then(response => response.arrayBuffer())
|
||
|
.then(arrayBuffer => this.audioContext.decodeAudioData(arrayBuffer))
|
||
|
.then(audioBuffer => {
|
||
|
// Set buffer on AudioBufferSourceNode
|
||
|
this.source100.buffer = audioBuffer;
|
||
|
this.source100.loop = true;
|
||
|
// Connect nodes
|
||
|
// this.source100.connect(compressorNode);
|
||
|
this.source100.connect(this.maskGainControl);
|
||
|
// this.Gain1000.connect(this.compressorNode);
|
||
|
this.maskGainControl.connect(this.audioContext.destination);
|
||
|
// this.Gain1000.gain.setValueAtTime(1,this.audioCtx.currentTime);
|
||
|
// Play source node
|
||
|
|
||
|
});
|
||
|
this.SoundNode=this.audioContext.createBufferSource();
|
||
|
this.harmonicGainNode=this.audioContext.createGain();
|
||
|
fetch('/sounds/Forest.aac')
|
||
|
.then(response => response.arrayBuffer())
|
||
|
.then(arrayBuffer => this.audioContext.decodeAudioData(arrayBuffer))
|
||
|
.then(audioBuffer => {
|
||
|
// Set buffer on AudioBufferSourceNode
|
||
|
this.SoundNode.buffer = audioBuffer;
|
||
|
this.SoundNode.loop = true;
|
||
|
// compressorNode.attack.value = .25;
|
||
|
// this.SoundNode.connect(compressorNode);
|
||
|
// this.harmonicGainNode.gain.value=this.harmonicGain;
|
||
|
this.currentDestination = this.audioContext.destination;
|
||
|
this.SoundNode.connect(this.harmonicGainNode);
|
||
|
this.harmonicGainNode.connect(this.currentDestination);
|
||
|
// Play source node
|
||
|
|
||
|
//1102500
|
||
|
|
||
|
});
|
||
|
},
|
||
|
methods: {
|
||
|
startAudio() {
|
||
|
// Create the AudioContext
|
||
|
|
||
|
|
||
|
// Create the AnalyserNode
|
||
|
this.analyserNode = this.audioContext.createAnalyser();
|
||
|
this.analyserNode.fftSize = this.bufferSize;
|
||
|
this.analyserNode.minDecibels = -100;
|
||
|
this.analyserNode.maxDecibels = -1;
|
||
|
this.analyserNode.smoothingTimeConstant = 0.95;
|
||
|
this.bandPass1=this.audioContext.createBiquadFilter();
|
||
|
this.bandPass1.type='bandpass'
|
||
|
this.bandPass1.frequency.value=63;
|
||
|
this.bandPass1.Q.value=1.41;
|
||
|
|
||
|
// Connect the microphone input to the AnalyserNode
|
||
|
navigator.mediaDevices.getUserMedia({ audio: true })
|
||
|
.then((stream) => {
|
||
|
this.Gain1000=this.audioContext.createGain();
|
||
|
this.Gain1000.gain.value = Math.pow(10, (-39 / 20));
|
||
|
const source = this.audioContext.createMediaStreamSource(stream);
|
||
|
source.connect(this.Gain1000);
|
||
|
this.bandPass1.connect(this.bandPass1);
|
||
|
source.connect(this.analyserNode)
|
||
|
this.source100.start();
|
||
|
this.SoundNode.start();
|
||
|
})
|
||
|
.catch((error) => {
|
||
|
console.error('Error accessing microphone:', error);
|
||
|
});
|
||
|
|
||
|
// Start capturing snapshots at the specified interval
|
||
|
this.intervalId = setInterval(() => {
|
||
|
this.captureSnapshot();
|
||
|
}, this.snapshotInterval);
|
||
|
},
|
||
|
stopAudio() {
|
||
|
// Stop capturing snapshots and clear the interval
|
||
|
clearInterval(this.intervalId);
|
||
|
|
||
|
// Disconnect the audio nodes
|
||
|
this.analyserNode.disconnect();
|
||
|
this.audioContext.close();
|
||
|
|
||
|
// Reset the data
|
||
|
this.snapshots = [];
|
||
|
this.level = 0;
|
||
|
},
|
||
|
captureSnapshot() {
|
||
|
const bufferLength = this.analyserNode.frequencyBinCount;
|
||
|
const frequencyData = new Uint8Array(bufferLength);
|
||
|
this.analyserNode.getByteFrequencyData(frequencyData);
|
||
|
|
||
|
const snapshot = {
|
||
|
data: Array.from(frequencyData),
|
||
|
timestamp: this.audioContext.currentTime,
|
||
|
};
|
||
|
|
||
|
this.snapshots.push(snapshot);
|
||
|
|
||
|
// Remove old snapshots that are older than the snapshot duration
|
||
|
const oldestTimestamp = snapshot.timestamp - this.snapshotDuration / 1000;
|
||
|
while (this.snapshots.length > 0 && this.snapshots[0].timestamp < oldestTimestamp) {
|
||
|
this.snapshots.shift();
|
||
|
this.calculateLevel();
|
||
|
}
|
||
|
this.harmonicGainNode.gain.setValueAtTime(this.harmonicGain,this.audioContext.currentTime);
|
||
|
// Calculate the integrated level value for the bandpass filter
|
||
|
|
||
|
},
|
||
|
calculateLevel() {
|
||
|
// console.log(this.snapshots);
|
||
|
const filteredSnapshots = this.snapshots.filter((snapshot) => {
|
||
|
return (
|
||
|
snapshot.data[63] >= 0 &&
|
||
|
snapshot.data[63] <= 255
|
||
|
);
|
||
|
});
|
||
|
// console.log(this.snapshots);
|
||
|
|
||
|
const levelValues = filteredSnapshots.map((snapshot) => {
|
||
|
return snapshot.data[63];
|
||
|
});
|
||
|
|
||
|
// Convert level values to RMS
|
||
|
const squaredValues = levelValues.map((value) => {
|
||
|
return value ** 2;
|
||
|
});
|
||
|
|
||
|
console.log(levelValues);
|
||
|
const sumOfSquares = squaredValues.reduce((acc, value) => acc + value, 0);
|
||
|
const meanOfSquares = sumOfSquares / squaredValues.length;
|
||
|
const rms = Math.sqrt(meanOfSquares);
|
||
|
|
||
|
const dbfsValues = levelValues.map((value) => {
|
||
|
const rmsValue = value / 255; // Normalize to 0-1 range
|
||
|
const dbfsValue = 20 * Math.log10(rmsValue);
|
||
|
return dbfsValue;
|
||
|
});
|
||
|
console.log(dbfsValues);
|
||
|
|
||
|
// Sort the RMS values in ascending order
|
||
|
const sortedData = dbfsValues.sort((a, b) => a - b);
|
||
|
// console.log(sortedData);
|
||
|
|
||
|
// Calculate the 10th percentile value
|
||
|
const percentile10 = this.calculatePercentile(sortedData, 10);
|
||
|
|
||
|
// Calculate the 90th percentile value
|
||
|
const percentile90 = this.calculatePercentile(sortedData, 90);
|
||
|
const dbfs = 20 * Math.log10(rms / 255);
|
||
|
console.log(rms);
|
||
|
this.level = rms;
|
||
|
this.percentile10 = percentile10;
|
||
|
this.percentile90 = percentile90;
|
||
|
console.log('!0th',this.percentile10);
|
||
|
console.log('90th',this.percentile90);
|
||
|
console.log("Percentile:",this.percentile90-this.percentile10);
|
||
|
const percentile=this.percentile90-this.percentile10;
|
||
|
if (!isFinite(percentile)){
|
||
|
console.log('ok Inf');
|
||
|
return;
|
||
|
}
|
||
|
const rampTime = 2;
|
||
|
const startTime = this.audioContext.currentTime;
|
||
|
// Map the difference to the desired range of GainValues
|
||
|
const minDifference = 0; // Minimum difference value
|
||
|
const maxDifference = 10; // Maximum difference value
|
||
|
const minGainValue = 0.1; // Minimum GainValue
|
||
|
const maxGainValue = this.maxGain1000; // Maximum GainValue
|
||
|
|
||
|
// Map the difference to the GainValue range
|
||
|
const mappedGainValue = ((percentile - minDifference) / (maxDifference - minDifference)) *
|
||
|
(maxGainValue - minGainValue) + minGainValue;
|
||
|
console.log('Maped Gain',mappedGainValue);
|
||
|
|
||
|
this.maskGainControl.gain.setTargetAtTime(mappedGainValue,this.audioContext.currentTime,6);
|
||
|
|
||
|
|
||
|
},
|
||
|
|
||
|
|
||
|
calculatePercentile(sortedData, percentile) {
|
||
|
const index = Math.ceil((percentile / 100) * sortedData.length);
|
||
|
return sortedData[index - 1];
|
||
|
}
|
||
|
},
|
||
|
};
|
||
|
</script>
|