Home
Head's Up: I'm in the middle of upgrading my site. Most things are in place, but there are something missing and/or broken including image alt text. Please bear with me while I'm getting things fixed.

Play Multiple Tracks With The Web Audio API

NOTE : See also : id : 2ugi4q3u

I'm building an audio stem player like splitter.fm 1 using the Web Audio API 2 . The first step was building a prototype the plays multiple tracks in sync with each other. I did that by refining an example 3 from MDN.

HTML

The HTML page doesn't have much to it. It calls the javascript and provides a start button that can be used if the browser prevents autoplay.

html
<!DOCTYPE html>
<html lang="en">

<head>
  <title>Stem Player Test</title>
  <script src="player.js"></script>
</head>
<body>
  <button id="startButton">Start</button>
</body>
</html>

JavaScript Version 2

The first version is further below. Leaving it there for reference. This is the second version. Note that there's nothing here to make sure all the files are downloaded. The audio could start before they call come down, but they will sync up as soon as they are loaded.

javascript
let audioCtx = null;

const tracks = [
  { id: "gracie", url: "audio/gracie.mp3" },
  { id: "paino", url: "audio/plucks.mp3" }
];

const init = () => {
  console.log("init");
  window.startButton.addEventListener("click", play);
  play();
}

async function play() {
  let offset = 0;
  console.log("playing")
  audioCtx = new AudioContext();
  tracks.forEach(async (track) => {
    const response = await fetch(track.url);
    const arrayBuffer = await response.arrayBuffer();
    track.track = await audioCtx.decodeAudioData(arrayBuffer);
    track.source = new AudioBufferSourceNode(audioCtx, {
      buffer: track.track,
    });
    track.source.connect(audioCtx.destination);
    if (offset == 0) {
      track.source.start();
      offset = audioCtx.currentTime;
    } else {
      track.source.start(0, audioCtx.currentTime - offset);
    }
    console.log(track)
  })
}

document.addEventListener("DOMContentLoaded", init);

JavaScript Version 1

The JavaScript does the heavy lifting with the Web Audio API. It expects two tracks at [TODO: Code shorthand span ] and [TODO: Code shorthand span ] . The primary function of this example is to show multiple tracks. I also added a panner module into the audio chain since that's what I need for what I'm doing.

javascript
let audioCtx = null;

const init = () => {
  console.log("init");
  window.startButton.addEventListener("click", play);
  play();
}

const play = () => {
  audioCtx = new AudioContext();
  loadFile("audio/gracie.mp3").then((track) => {
    playTrack(track);
  })
  loadFile("audio/piano.mp3").then((track) => {
    playTrack(track);
  })
}

async function loadFile(filepath) {
  const response = await fetch(filepath);
  const arrayBuffer = await response.arrayBuffer();
  const track = await audioCtx.decodeAudioData(arrayBuffer);
  return track;
}

let offset = 0;
function playTrack(audioBuffer) {
  const trackSource = new AudioBufferSourceNode(audioCtx, {
    buffer: audioBuffer,
  });
  const pannerOptions = { pan: 0 };
  const panner = new StereoPannerNode(audioCtx, pannerOptions);
  trackSource.connect(panner).connect(audioCtx.destination);
  if (offset == 0) {
    trackSource.start();
    offset = audioCtx.currentTime;
  } else {
    trackSource.start(0, audioCtx.currentTime - offset);
  }
  return trackSource;
}

document.addEventListener("DOMContentLoaded", init);

[] TODO : Splitter.fm note

[] TODO : Web Audio Note

[] TODO : Multitrack example

Play multiple tracks and stems in sync with the Web Audio API