Note: This site is currently "Under construction". I'm migrating to a new version of my site building software. Lots of things are in a state of disrepair as a result (for example, footnote links aren't working). It's all part of the process of building in public. Most things should still be readable though.

Play Multiple Tracks With The Web Audio API

NOTE: See also: id: 2ugi4q3u

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

Code

<!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>

Code

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);

Code

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);

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

References

  • - TODO: Splitter.fm note

    - TODO: Web Audio Note

    - TODO: Multitrack example