How to using await keyword or wait the promise in the AudioWorkletProcessor.process function?
I have some tasks such as running machine learning model inference and retrieve the results that requires async function, but I can't use it in the process function of the AudioWorkletProcessor.
Here is a simple AudioWorkletProcessor test page (host it in a web server instead of open it directly, otherwise the javascript blob will be blocked due to the security policy).
<!doctype html>
<html>
<head>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
</head>
<div class="p-3 mb-2 bg-slate-100 w-1/3 rounded text-sm">
<input id="volume-meter" class="w-full bg-white"
type="range" min="0" max="100" value="0" step="1" disabled>
</div>
<button id="button-start" disabled>START</button>
<script>
const audioContext = new AudioContext();
// the code in "volume-meter-processor.js" since error happens when addModule directly
let process_code = `
const SMOOTHING_FACTOR = 0.8;
const FRAME_PER_SECOND = 60;
const FRAME_INTERVAL = 1 / FRAME_PER_SECOND;
class VolumeMeter extends AudioWorkletProcessor {
constructor() {
super();
this._lastUpdate = currentTime;
this._volume = 0;
}
calculateRMS(inputChannelData) {
let sum = 0;
for (let i = 0; i < inputChannelData.length; i++) {
sum += inputChannelData[i] * inputChannelData[i];
}
let rms = Math.sqrt(sum / inputChannelData.length);
this._volume = Math.max(rms, this._volume * SMOOTHING_FACTOR);
}
test_async = async function(a,b){
return a+b;
}
process(inputs, outputs) {
const inputChannelData = inputs[0][0];
if (currentTime - this._lastUpdate > FRAME_INTERVAL) {
this.calculateRMS(inputChannelData);
this.port.postMessage(this._volume);
console.log(inputs)
this._lastUpdate = currentTime;
}
let c = await this.test_async(3,5);
return true;
}
}
registerProcessor("volume-meter", VolumeMeter);
`;
const startAudio = async (context, meterElement) => {
let blob = new Blob([process_code], {type: 'application/javascript'});
//await context.audioWorklet.addModule('volume-meter-processor.js');
await context.audioWorklet.addModule(URL.createObjectURL(blob));
const mediaStream = await navigator.mediaDevices.getUserMedia({audio: true});
const micNode = context.createMediaStreamSource(mediaStream);
const volumeMeterNode = new AudioWorkletNode(context, 'volume-meter');
volumeMeterNode.port.onmessage = ({data}) => {
meterElement.value = data * 500;
};
micNode.connect(volumeMeterNode).connect(context.destination);
};
// A simplem onLoad handler. It also handles user gesture to unlock the audio
// playback.
window.addEventListener('load', async () => {
const buttonEl = document.getElementById('button-start');
const meterEl = document.getElementById('volume-meter');
buttonEl.disabled = false;
meterEl.disabled = false;
buttonEl.addEventListener('click', async () => {
await startAudio(audioContext, meterEl);
audioContext.resume();
buttonEl.disabled = true;
buttonEl.textContent = 'Playing...';
}, false);
});
</script>
</html>
I want to use await
keyword and wait the result in the process function.
This is my attemps, but non of these works.
process(inputs, outputs) {
const inputChannelData = inputs[0][0];
if (currentTime - this._lastUpdate > FRAME_INTERVAL) {
this.calculateRMS(inputChannelData);
this.port.postMessage(this._volume);
console.log(inputs)
this._lastUpdate = currentTime;
}
let c = await this.test_async(3,5);
return true;
}
This attemp failed with error: SyntaxError: Unexpected reserved word
async process(inputs, outputs) {
const inputChannelData = inputs[0][0];
if (currentTime - this._lastUpdate > FRAME_INTERVAL) {
this.calculateRMS(inputChannelData);
this.port.postMessage(this._volume);
console.log(inputs)
this._lastUpdate = currentTime;
}
let c = await this.test_async(3,5);
return true;
}
The webpage simply stuck and not process any microphone input
process(inputs, outputs) {
const inputChannelData = inputs[0][0];
if (currentTime - this._lastUpdate > FRAME_INTERVAL) {
this.calculateRMS(inputChannelData);
this.port.postMessage(this._volume);
console.log(inputs)
this._lastUpdate = currentTime;
}
let c = 0;
this.test_async(3,5).then(value => {
c = value;
})
while (c === 0){
//This while loop consumes a lot of CPU!!!
console.log("waiting promise")
}
console.log("promise done, c=", c)
return true;
}
Attemp 3 fails due to the test_async are never been called.
How do I use the async function in the AudioWorkletProcessor.process function?
Simply can't.
This document https://developer.mozilla.org/en-US/docs/Web/API/AudioWorkletProcessor/process indicates the AudioWorkletProcessor.process() method is called synchronously from the audio rendering thread so that it is not possible to wait promise from another async function in the AudioWorkletProcessor.process() method.