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:
<!DOCTYPE html>
<head>
<meta charset='utf-8'>
<title>Henry Louis Gates</title>
<body>
<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>
<script type="text/javascript">
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 = function () {
// we only need to do this once
this.removeEventListener('play', 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.006) {
audio.playbackRate = 4;
} else if (rms >= 0.004) {
audio.playbackRate = 1;
}
}
function formatDecibel(val) {
val = 10 * Math.log(val) / Math.log(10);
if (val === -Infinity)
return '−∞';
if (val < 0)
return '−' + (-val).toFixed(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, false);
}, false);
</script>
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.