In my example, I have a mp3 that a user has uploaded to a webpage. I create a visual waveform on the page and have all the default controls coded and working (play, pause, stop, scrubbing, etc...)
What I'm trying to accomplish is the scrubbing effect that you hear in After Effects or Animate. When I scrub the audio, I want to hear the tones of the audio at the exact place where the scrubber is.
I don't even know where to begin on this? I looked into AudioContext
and I think my answer is in there somewhere but I'm not sure. I've thought about having a "shadow audio tag" that I set the same src to and then calling play()
and pausing it pretty soon after but that seems like a mess; especially with an aggressive scrubber. Any direction would be appreciated!
Yes your answer is in the AudioContext API (all async and slow MediaElements are not made for this). Specifically, what you want is the AudioBufferSourceNode interface, which is able to play audio instantly.
All you need to do is to fetch your audio file as an ArrayBuffer, ask an AudioContext to decode the audio PCM data from this buffer and create an AudioBuffer from it.
That was the heavy part, but it's done only once.
Then you just need to create a very-light AudioBufferSourceNode for each "scrub" event.
To do so, you can use the three arguments version of its start(when, offset, duration)
method.
const slider = document.getElementById('slider');
const ctx = new AudioContext();
fetch("https://upload.wikimedia.org/wikipedia/en/d/dc/Strawberry_Fields_Forever_(Beatles_song_-_sample).ogg")
.then( resp => resp.arrayBuffer() )
.then( buf => ctx.decodeAudioData(buf) )
.then( prepareUI )
.catch( console.error );
function prepareUI( audioBuf ) {
let source;
slider.oninput = e => {
if( source ) { source.stop(0); }
source = ctx.createBufferSource();
source.buffer = audioBuf;
source.connect(ctx.destination);
const offset = slider.value * audioBuf.duration;
const duration = 0.1;
source.start(0, offset, duration);
};
slider.disabled = false;
}
input { width: 350px }
<input type="range" id="slider" min="0" max="1" step="0.005" disabled>
And of-course you can also reuse this AudioBuffer for your whole player.