I encounter a problem with my AudioWorkletProcessor class.
I have a continuing flow of audio data, my client gets this flow, prepare the buffer and send it to my AudioWorkletProcessor.
But when it’s sent the process function is stopped to let the “port.onmessage” run, it’s not a like it’s stopped too long but sufficiently to have a cut in my audio.
here’s my code :
worklet-processor.js :
class AudioProcessor extends AudioWorkletProcessor {
constructor() {
super();
this.queue = [];
this.queueLengthMinload = 750
this.queueLengthPreload = 950
this.port.onmessage = (event) => {
if (event.data) {
this.queue.push(event.data);
console.log(this.queue.length)
}
};
}
process(inputs, outputs, parameters) {
const output = outputs[0];
if (this.queue.length > this.queueLengthMinload) {
let segment = this.queue.shift()
for (let x = 0; x < output.length; ++x) {
if (x < segment.length){
output[x].set(segment[x]);
}
}
}
if(this.queue.length < this.queueLengthPreload){
// this.port.postMessage('need-more-data');
}
return true;
}
}
registerProcessor('audio-processor', AudioProcessor);
main.js
socket.on('audio', chunk => {
if(!oneTime) return;
const uint8Chunk = new Uint8Array(chunk);
if (context === null) {
initializeAudioContext().then(() => {
decodeAndQueueAudio(uint8Chunk);
});
} else {
decodeAndQueueAudio(uint8Chunk);
}
});
async function initializeAudioContext() {
context = new AudioContext();
gainNode = context.createGain();
gainNode.gain.value = volumeRange.value / 10;
gainNode.connect(context.destination);
await context.audioWorklet.addModule('worklet-processor.js');
audioWorkletNode = new AudioWorkletNode(context, 'audio-processor');
audioWorkletNode.connect(gainNode);
audioWorkletNode.port.onmessage = (event) => {
if (event.data === 'need-more-data') {
console.log("waiting data");
sendNextSegment()
}
};
}
function decodeAndQueueAudio(uint8Chunk) {
context.decodeAudioData(uint8Chunk.buffer, buffer => {
const numberOfChannels = buffer.numberOfChannels
let channelData = [];
for (let i = 0; i < numberOfChannels; ++i) {
channelData[i] = buffer.getChannelData(i);
}
const segmentSize = 128;
const channelDataLength = channelData.length
let maxLength = 0;
for (let i = 0; i < channelDataLength; ++i) {
const length = channelData[i].length;
if (length > maxLength) {
maxLength = length;
}
}
for (let i = 0; i < maxLength; i += segmentSize) {
let segment = [];
for (let x = 0; x < channelDataLength; ++x) {
let bound = Math.min(i + segmentSize, maxLength)
segment[x] = channelData[x].subarray(i,bound)
}
audioQueue.push(segment);
}
sendNextSegment()
});
}
function sendNextSegment() {
if (audioQueue.length > 0 && audioWorkletNode) {
let audioData = []
audioData = audioQueue;
for (let i = 0; i < audioData.length; ++i){
audioWorkletNode.port.postMessage(audioData.shift());
}
}
}
I have an idea but I’m not sure if it’s possible : Make a worklet just to handle the queue and another to process with “worklet1.queue”
Have you further hints or docs, is my idea ok if yes : I don’t know how to make this extract work ?
worklet-processor.js
class AudioProcessor extends AudioWorkletProcessor {
constructor() {
super();
}
process(inputs, outputs, parameters) {
const output = outputs[0];
if (queueWorkletNode.queue.length > queueWorkletNode.queueLengthMinload) {
let segment = queueWorkletNode.queue.shift()
for (let x = 0; x < output.length; ++x) {
if (x < segment.length){
output[x].set(segment[x]);
}
}
}
if(queueWorkletNode.queue.length < queueWorkletNode.queueLengthPreload){
//this.port.postMessage('need-more-data');
}
return true;
}
}
registerProcessor('audio-processor', AudioProcessor);
worklet-queue.js
class AudioQueue extends AudioWorkletProcessor {
constructor() {
super();
this.queue = [];
this.queueLengthMinload = 750
this.queueLengthPreload = 950
this.port.onmessage = (event) => {
if (event.data) {
this.queue.push(event.data);
console.log(this.queue.length)
}
};
}
process(inputs, outputs, parameters) {
return true;
}
}
registerProcessor('audio-queue', AudioProcessor);
main.js
socket.on('audio', chunk => {
if(!oneTime) return;
const uint8Chunk = new Uint8Array(chunk);
if (context === null) {
initializeAudioContext().then(() => {
decodeAndQueueAudio(uint8Chunk);
});
} else {
decodeAndQueueAudio(uint8Chunk);
}
});
async function initializeAudioContext() {
context = new AudioContext();
gainNode = context.createGain();
gainNode.gain.value = volumeRange.value / 10;
gainNode.connect(context.destination);
await context.audioWorklet.addModule('worklet-queue.js');
await context.audioWorklet.addModule('worklet-processor.js');
procWorkletNode = new AudioWorkletNode(context, 'audio-processor');
queueWorkletNode = new AudioWorkletNode(context, 'audio-queue');
procWorkletNode.connect(gainNode);
procWorkletNode.port.onmessage = (event) => {
if (event.data === 'need-more-data') {
console.log("waiting data");
sendNextSegment()
}
};
}
function decodeAndQueueAudio(uint8Chunk) {
context.decodeAudioData(uint8Chunk.buffer, buffer => {
const numberOfChannels = buffer.numberOfChannels
let channelData = [];
for (let i = 0; i < numberOfChannels; ++i) {
channelData[i] = buffer.getChannelData(i);
}
const segmentSize = 128;
const channelDataLength = channelData.length
let maxLength = 0;
for (let i = 0; i < channelDataLength; ++i) {
const length = channelData[i].length;
if (length > maxLength) {
maxLength = length;
}
}
for (let i = 0; i < maxLength; i += segmentSize) {
let segment = [];
for (let x = 0; x < channelDataLength; ++x) {
let bound = Math.min(i + segmentSize, maxLength)
segment[x] = channelData[x].subarray(i,bound)
}
audioQueue.push(segment);
}
sendNextSegment()
});
}
function sendNextSegment() {
if (audioQueue.length > 0 && queueWorkletNode) {
let audioData = []
audioData = audioQueue;
for (let i = 0; i < audioData.length; ++i){
queueWorkletNode.port.postMessage(audioData.shift());
}
}
}
ludovic araujo is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.