implement a ringbuffer in the audioworklet to optimize runtimes
This commit is contained in:
@ -3,7 +3,11 @@ class OwrxAudioProcessor extends AudioWorkletProcessor {
|
||||
super(options);
|
||||
this.maxLength = options.processorOptions.maxLength;
|
||||
this.reduceToLength = options.processorOptions.reduceToLength;
|
||||
this.audio_buffers = [];
|
||||
// initialize ringbuffer, make sure it aligns with the expected buffer size of 128
|
||||
this.bufferSize = Math.round(sampleRate * this.maxLength / 128) * 128
|
||||
this.audioBuffer = new Float32Array(this.bufferSize);
|
||||
this.inPos = 0;
|
||||
this.outPos = 0;
|
||||
this.port.addEventListener('message', (m) => {
|
||||
if (typeof(m.data) === 'string') {
|
||||
const json = JSON.parse(m.data);
|
||||
@ -11,48 +15,39 @@ class OwrxAudioProcessor extends AudioWorkletProcessor {
|
||||
this.reportBuffers();
|
||||
}
|
||||
} else {
|
||||
this.audio_buffers.push(new Float32Array(m.data));
|
||||
// the ringbuffer size is aligned to the output buffer size, which means that the input buffers might
|
||||
// need to wrap around the end of the ringbuffer, back to the start.
|
||||
// it is better to have this processing here instead of in the time-critical process function.
|
||||
if (this.inPos + m.data.length <= this.bufferSize) {
|
||||
// we have enough space, so just copy data over.
|
||||
this.audioBuffer.set(m.data, this.inPos);
|
||||
} else {
|
||||
// we don't have enough space, so we need to split the data.
|
||||
const remaining = this.bufferSize - this.inPos;
|
||||
this.audioBuffer.set(m.data.subarray(0, remaining), this.inPos);
|
||||
this.audioBuffer.set(m.data.subarray(remaining));
|
||||
}
|
||||
this.inPos = (this.inPos + m.data.length) % this.bufferSize;
|
||||
}
|
||||
});
|
||||
this.port.addEventListener('messageerror', console.error);
|
||||
this.port.start();
|
||||
}
|
||||
process(inputs, outputs, parameters) {
|
||||
//console.time('audio::process');
|
||||
const samples = Math.min(128, this.remaining());
|
||||
outputs[0].forEach((output) => {
|
||||
let total = 0;
|
||||
while (this.audio_buffers.length) {
|
||||
const b = this.audio_buffers.shift();
|
||||
const newLength = total + b.length;
|
||||
const ol = output.length;
|
||||
// not enough space to fit all data, so splice and put back in the queue
|
||||
if (newLength > ol) {
|
||||
const tokeep = b.slice(0, ol - total);
|
||||
output.set(tokeep, total);
|
||||
const tobuffer = b.slice(ol - total, b.length);
|
||||
this.audio_buffers.unshift(tobuffer);
|
||||
break;
|
||||
} else {
|
||||
output.set(b, total);
|
||||
}
|
||||
total = newLength;
|
||||
}
|
||||
output.set(this.audioBuffer.subarray(this.outPos, this.outPos + samples));
|
||||
});
|
||||
//console.timeEnd('audio::process');
|
||||
this.outPos = (this.outPos + samples) % this.bufferSize;
|
||||
return true;
|
||||
}
|
||||
bufferLength() {
|
||||
return this.audio_buffers.map(function(b){ return b.length; }).reduce(function(a, b){ return a + b; }, 0);
|
||||
remaining() {
|
||||
const mod = (this.inPos - this.outPos) % this.bufferSize;
|
||||
if (mod >= 0) return mod;
|
||||
return mod + this.bufferSize;
|
||||
}
|
||||
reportBuffers() {
|
||||
var we_have_more_than = (sec) => {
|
||||
return sec * sampleRate < this.bufferLength();
|
||||
};
|
||||
if (we_have_more_than(this.maxLength)) while (we_have_more_than(this.reduceToLength)) {
|
||||
this.audio_buffers.shift();
|
||||
}
|
||||
|
||||
this.port.postMessage(JSON.stringify({buffersize: this.bufferLength()}));
|
||||
this.port.postMessage(JSON.stringify({buffersize: this.remaining()}));
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user