This Web Assembly Ring Buffer can handles an input buffer from specific size and give you an output buffer with any sizes that you want
- Manipulate Input and Output PCM Data
- Dynamic buffer sizes
- Wasm Ring Buffer Queue implemented in C++
- High processment
npm install wasm-ring-buffer
import WasmRingBuffer from 'wasm-ring-buffer';
The current AudioWorkletProccess only process 128 bytes for each; so if you need a buffer with your own size, you need to use a "Ring Buffer" to manipulate it. So this library does it for you. We enqueue the AudioWorkletProccess buffer into a Circular Linked List(FIFO), and dequeue with your own size.
For browser definitions a WebAssembly implementation can not run in the Main Thread, you can use only inside of WebWorkers or AudioWorkletNode
- wasm-ring-buffer
- example
- using-react
- src
node.h
queue.h
ring-buffer.wasmmodule.js
ring-buffer.wasmmodule.wasm
wasm-ring-buffer.cpp
index.js
index.d.ts
const inputAudioContext = new AudioContext({ sampleRate: 8000 });
inputAudioContext.audioWorklet
.addModule('your-worklet-processor.js')
.then(() => {
navigator.mediaDevices
.getUserMedia({ audio: true })
.then(stream => {
const microphone = inputAudioContext.createMediaStreamSource(stream);
const audioWorkletNode = new AudioWorkletNode(
inputAudioContext,
'your-worklet-processor',
{
channelCount : 1,
processorOptions: { //Passing the arguments to processor
bufferSize: 160, //output buffer size
capacity:2046 // max fifo capacity
},
},
);
audioWorkletNode.port.onmessage = ({ data }) => {
console.log('Your own buffer >> ', data); //Receiving data from worklet thread
};
microphone.connect(audioWorkletNode).connect(inputAudioContext.destination);
});
});
import WasmRingBuffer from 'wasm-ring-buffer/index.js';
import { LOG_TABLE } from './constants.js';
class YourWorkletProcessor extends AudioWorkletProcessor {
constructor(options) {
super();
this._bufferSize = options.processorOptions.bufferSize;
this._capacity = options.processorOptions.capacity;
this._ringBuffer = new WasmRingBuffer(this._capacity, this._bufferSize);
}
float32ToInt16(float32array) {
let l = float32array.length;
const buffer = new Int16Array(l);
while (l--) {
buffer[l] = Math.min(1, float32array[l]) * 0x7fff;
}
return buffer;
}
alawEncode(sample) {
let compandedValue;
sample = sample === -32768 ? -32767 : sample;
const sign = (~sample >> 8) & 0x80;
if (!sign) {
sample *= -1;
}
if (sample > 32635) {
sample = 32635;
}
if (sample >= 256) {
const exponent = LOG_TABLE[(sample >> 8) & 0x7f];
const mantissa = (sample >> (exponent + 3)) & 0x0f;
compandedValue = (exponent << 4) | mantissa;
} else {
compandedValue = sample >> 4;
}
return compandedValue ^ (sign ^ 0x55);
}
linearToAlaw(int16array) {
const aLawSamples = new Uint8Array(int16array.length);
for (let i = 0; i < int16array.length; i++) {
aLawSamples[i] = this.alawEncode(int16array[i]);
}
return aLawSamples;
}
process(inputs) {
const input = inputs[0]; // channel 1
const output = new Float32Array(this._bufferSize);
this._ringBuffer.enqueue(input[0]); //storing
while (this._ringBuffer.size() >= this._bufferSize) {
this._ringBuffer.dequeue(output); //retrieving
const int16array = this.float32ToInt16(output);
const payload = this.linearToAlaw(int16array);
const sharedPayload = new Uint8Array(new SharedArrayBuffer(payload.length)); // sharing buffer memory
sharedPayload.set(payload, 0);
this.port.postMessage(sharedPayload); //Sending data to main thread
}
return true;
}
}
registerProcessor(`your-worklet-processor`, YourWorkletProcessor);