Spaces:
Sleeping
Sleeping
/** | |
An in-place replacement for ScriptProcessorNode using AudioWorklet | |
*/ | |
class RecorderProcessor extends AudioWorkletProcessor { | |
// 0. Determine the buffer size (this is the same as the 1st argument of ScriptProcessor) | |
bufferSize = 2048 | |
// 1. Track the current buffer fill level | |
_bytesWritten = 0 | |
// 2. Create a buffer of fixed size | |
_buffer = new Float32Array(this.bufferSize) | |
constructor() { | |
super() | |
this.initBuffer() | |
} | |
initBuffer() { | |
this._bytesWritten = 0 | |
} | |
isBufferEmpty() { | |
return this._bytesWritten === 0 | |
} | |
isBufferFull() { | |
return this._bytesWritten === this.bufferSize | |
} | |
/** | |
* @param {Float32Array[][]} inputs | |
* @returns {boolean} | |
*/ | |
process(inputs) { | |
// Grabbing the 1st channel similar to ScriptProcessorNode | |
this.append(inputs[0][0]) | |
return true | |
} | |
/** | |
* | |
* @param {Float32Array} channelData | |
*/ | |
append(channelData) { | |
if (this.isBufferFull()) { | |
this.flush() | |
} | |
if (!channelData) return | |
for (let i = 0; i < channelData.length; i++) { | |
this._buffer[this._bytesWritten++] = channelData[i] | |
} | |
} | |
flush() { | |
// trim the buffer if ended prematurely | |
const buffer = this._bytesWritten < this.bufferSize | |
? this._buffer.slice(0, this._bytesWritten) | |
: this._buffer | |
const result = this.downsampleBuffer(buffer, 44100, 16000); | |
this.port.postMessage(result) | |
this.initBuffer() | |
} | |
downsampleBuffer (buffer, sampleRate, outSampleRate) { | |
if (outSampleRate == sampleRate) { | |
return buffer; | |
} | |
if (outSampleRate > sampleRate) { | |
throw 'downsampling rate show be smaller than original sample rate'; | |
} | |
var sampleRateRatio = sampleRate / outSampleRate; | |
var newLength = Math.round(buffer.length / sampleRateRatio); | |
var result = new Int16Array(newLength); | |
var offsetResult = 0; | |
var offsetBuffer = 0; | |
while (offsetResult < result.length) { | |
var nextOffsetBuffer = Math.round((offsetResult + 1) * sampleRateRatio); | |
var accum = 0, | |
count = 0; | |
for (var i = offsetBuffer; i < nextOffsetBuffer && i < buffer.length; i++) { | |
accum += buffer[i]; | |
count++; | |
} | |
result[offsetResult] = Math.min(1, accum / count) * 0x7fff; | |
offsetResult++; | |
offsetBuffer = nextOffsetBuffer; | |
} | |
return result.buffer; | |
}; | |
} | |
registerProcessor("recorder.worklet", RecorderProcessor) |