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

前端 未结 2 2009
闹比i
闹比i 2021-01-21 22:33

I\'m trying to mimic the Web Audio API multitrack demo from the \'Mozilla Web Audio API for games\' Web doc.

https://developer.mozilla.org/en-US/docs/Games/Techniques/Au

相关标签:
2条回答
  • 2021-01-21 22:52

    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>

    0 讨论(0)
  • 2021-01-21 22:55

    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/

    0 讨论(0)
提交回复
热议问题