When playing an audio file on webpage using the <audio>
tag is there a way to dynamically speed up the playback when there is a word gap or pause in the audio and then resume it back to 1x (normal) speed?
Adapting the example from the linked question:
const formatDecibel = val => {
val = 10 * Math.log(val) * Math.LOG10E;
if (val === -Infinity)
return "−∞";
if (val < 0)
return `−${(-val).toFixed(1)}`;
};
document.addEventListener('DOMContentLoaded', function () {
const audio = document.getElementById('audio');
// 2048 sample buffer, 1 channel in, 1 channel out
const meter = document.getElementById('meter');
const labelFast = document.getElementById('label-fast');
const labelLoud = document.getElementById('label-loud');
const chkSkip = document.getElementById('chk-skip');
const handler = () => {
const ctx = new AudioContext();
const processor = ctx.createScriptProcessor(2048, 1, 1);
processor.onaudioprocess = function (evt) {
const input = evt.inputBuffer.getChannelData(0);
const rms = Math.sqrt(
input.map(val => val * val).reduce((a, b) => a + b, 0)
/ input.length
);
if (chkSkip.checked) {
if (rms < 0.002) {
audio.playbackRate = 4;
} else if (rms >= 0.004) {
audio.playbackRate = 1;
}
} else {
audio.playbackRate = 1;
}
meter.value = rms;
labelLoud.textContent =
`${formatDecibel(rms)}\xa0dB FS (LPCM RMS ${rms.toFixed(5)})`;
labelFast.textContent =
`${audio.playbackRate}×`;
};
const source = ctx.createMediaElementSource(audio);
source.connect(ctx.destination);
source.connect(processor);
processor.connect(ctx.destination);
};
audio.addEventListener('play', handler, { once: true });
}, { once: true });
<p> <audio crossorigin='anonymous' id='audio' controls
src='https://upload.wikimedia.org/wikipedia/commons/0/08/Split_infinitive.ogg' style='width: 100%'></audio>
<p> <label><input id='chk-skip' type='checkbox' checked> Skip silence</label>
<p> Loudness: <meter id='meter' style='width: 15em;'></meter> <span id='label-loud' style="display: inline-block; width: 20em; text-align: right;">−∞ dB FS</span>
<p> Speed: <span id='label-fast'>1×</span>
Drawback: this only seems to work smoothly in Blink- and WebKit-based browsers (with same-origin policy occasionally interfering). Firefox plays the audio and detects silence fine, but the playback speed setting seems to have no effect. Seeking to the end of the silent segment may work better.
Also, I used a deprecated API here for the sake of simplicity; the preferred way to implement filters like the silence detector is with a Worker thread. You might want to check that out.