Source code

Revision control

Copy as Markdown

Other Tools

(function (window) {
// Measure the RMS of the current audio output from `src` by creating a fresh
// ScriptProcessorNode and capturing exactly one audioprocess callback, so no
// samples from previous processing are included in the result.
async function measureRMSOnceWithFreshScriptProcessor(ac, src) {
function computeRMSFloat(data) {
let sumSq = 0;
for (let i = 0; i < data.length; i++) {
const v = data[i];
sumSq += v * v;
}
return Math.sqrt(sumSq / data.length);
}
// Use a new ScriptProcessorNode for each measurement, so that an event does
// not contain samples from the past.
const sp = ac.createScriptProcessor(2048, 1, 1);
src.connect(sp);
sp.connect(ac.destination);
const { inputBuffer } =
await new Promise(resolve => (sp.onaudioprocess = resolve));
src.disconnect(sp);
sp.disconnect();
sp.onaudioprocess = null;
const input = inputBuffer.getChannelData(0);
const rms = computeRMSFloat(input);
return rms;
}
// Repeatedly measures the RMS level from `src` using a fresh ScriptProcessor
// until the measured value is exactly equal to `threshold`.
// Resolves with the RMS value once the condition is met.
async function waitForRmsEqualToThreshold(ac, src, threshold) {
while (true) {
const rms = await measureRMSOnceWithFreshScriptProcessor(ac, src);
if (rms === threshold) {
return rms;
}
}
}
// Repeatedly measures the RMS level from `src` using a fresh ScriptProcessor
// until the measured value becomes greater than `threshold`.
// Resolves with the RMS value once the signal exceeds the threshold.
async function waitForRmsOverThreshold(ac, src, threshold) {
while (true) {
const rms = await measureRMSOnceWithFreshScriptProcessor(ac, src);
if (rms > threshold) {
return rms;
}
}
}
window.RMSUtils = {
measureRMSOnceWithFreshScriptProcessor,
waitForRmsEqualToThreshold,
waitForRmsOverThreshold,
};
})(window);