Web Audio API - multiple synchronized tracks - stopping previous track when new track starts

笑着哭i 提交于 2019-12-01 23:03:02

Simply store the BufferSources somewhere in the outer scope and then call their stop() method.

I took the liberty to rewrite a bit your loading logic, you shouldn't create a new request every time you start a new track, in that case you loose the main advantages of AudioBuffers against Audio element: they're truly fast to instantiate.

var active_source = null;

function stopActiveSource() {
  if (active_source) {
    active_source.onended = null; // manual stop, no event
    active_source.stop(0);
  }
}
// instead of requesting a new ArrayBuffer every time
// store them in a dictionnary
var buffers = {};
var context = new(window.AudioContext || window.webkitAudioContext)();

function playTrack(url) {
  // get fom our dictionnary
  var buffer = buffers[url];
  // stop the active one if any
  stopActiveSource();
  // create a new BufferSource
  var source = context.createBufferSource();
  // it is now the active one
  active_source = source;
  source.onended = function() {
    active_source = null;
  };

  source.buffer = buffer;
  source.connect(context.destination);

  source.start(0);
}

// start by getting all AudioBuffers
var tracks = document.getElementsByClassName('track');

for (var i = 0, len = tracks.length; i < len; i++) {
  tracks[i].addEventListener('click', function(e) {
    playTrack(this.href);
    e.preventDefault();
  });
  getBuffer(tracks[i].href);
}


function getBuffer(url) {
  var request = new XMLHttpRequest();
  request.open('GET', url, true);
  request.responseType = 'arraybuffer';
  request.onload = function(evt) {
    context.decodeAudioData(request.response, store);
  };
  request.send();

  function store(buffer) {
    buffers[url] = buffer;
  }
}
<base href="https://dl.dropboxusercontent.com/s/">
<ul>
  <li><a class="track" href="kbgd2jm7ezk3u3x/hihat.mp3">HiHat</a></li>
  <li><a class="track" href="h2j6vm17r07jf03/snare.mp3">Snare</a></li>
  <li><a class="track" href="1cdwpm3gca9mlo0/kick.mp3">Kick</a></li>
  <li><a class="track" href="h8pvqqol3ovyle8/tom.mp3">Tom</a></li>
</ul>

Figured it out with help from @Kaiido

Example with both synchronization and starting a new track where a previous track stops:

<ul>
  <li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-leadguitar.mp3">Lead Guitar</a></li>
  <li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-drums.mp3">Drums</a></li>
  <li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-bassguitar.mp3">Bass Guitar</a></li>
  <li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-horns.mp3">Horns</a></li>
  <li><a class="track" href="http://jPlayer.org/audio/mp3/gbreggae-clav.mp3">Clavi</a></li>
</ul>

let active_source = null;
let buffers = {};
const context = new(window.AudioContext || window.webkitAudioContext)();
let offset = 0;
const tempo = 3.074074076;
const tracks = document.getElementsByClassName('track');

function playTrack(url) {
  let buffer = buffers[url];
  let source = context.createBufferSource();

  source.buffer = buffer;
  source.connect(context.destination);
  source.loop = true;

  if (offset == 0) {
    source.start();
    offset = context.currentTime;
    active_source = source;
  } else {
    let relativeTime = context.currentTime - offset;
    let beats = relativeTime / tempo;
    let remainder = beats - Math.floor(beats);
    let delay = tempo - (remainder*tempo);
    let when = context.currentTime+delay;

    stopActiveSource(when);
    source.start(context.currentTime+delay,relativeTime+delay);
    active_source = source;
    source.onended = function() {
      active_source = null;
    };
  }
}

for (var i = 0, len = tracks.length; i < len; i++) {
  tracks[i].addEventListener('click', function(e) {
    playTrack(this.href);
    e.preventDefault();
  });
  getBuffer(tracks[i].href);
}

function getBuffer(url) {
  const request = new XMLHttpRequest();
  request.open('GET', url, true);
  request.responseType = 'arraybuffer';
  request.onload = function(evt) {
    context.decodeAudioData(request.response, store);
  };
  request.send();

  function store(buffer) {
    buffers[url] = buffer;
  }
}

function stopActiveSource(when) {
  if (active_source) {
    active_source.onended = null;
    active_source.stop(when);
  }
}

http://jsfiddle.net/mdq2c1wv/1/

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!