Web Audio API-多个同步轨道-新轨道开始时停止上一个轨道

时间:2018-09-21 03:04:25

标签: javascript api html5-audio web-audio-api

我正在尝试模仿“ Mozilla Web游戏音频API”网络文档中的Web Audio API多轨演示。

https://developer.mozilla.org/en-US/docs/Games/Techniques/Audio_for_Web_Games#Web_Audio_API_for_games

我唯一需要注意的是,单击新曲目后,我希望上一曲目停止播放(而不是彼此叠加播放)。

一个例子是,单击鼓,鼓开始演奏,然后单击吉他,鼓停止,吉他从鼓停止处开始播放。

有什么想法吗?是否有更好的工具/库来处理Web音频?

http://jsfiddle.net/c87z11jj/1/

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

    window.AudioContext = window.AudioContext || window.webkitAudioContext;

    var offset = 0;
    var context = new AudioContext();

    function playTrack(url) {
      var request = new XMLHttpRequest();
      request.open('GET', url, true);
      request.responseType = 'arraybuffer';

      var audiobuffer;

      // Decode asynchronously
      request.onload = function() {

        if (request.status == 200) {

          context.decodeAudioData(request.response, function(buffer) {
            var source = context.createBufferSource();
            source.buffer = buffer;
            source.connect(context.destination);
            console.log('context.currentTime '+context.currentTime);

            if (offset == 0) {
              source.start();
              offset = context.currentTime;
            } else {
              source.start(0,context.currentTime - offset);
            }

          }, function(e) {
            console.log('Error decoding audio data:' + e);
          });
        } else {


     console.log('Audio didn\'t load successfully; error code:' + request.statusText);
    }
  }
  request.send();
}

var tracks = document.getElementsByClassName('track');

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

2 个答案:

答案 0 :(得分:2)

只需将BufferSources存储在外部作用域中的某个位置,然后调用其stop()方法。

我自由地重写了一些加载逻辑,您不应该在每次开始新曲目时都创建一个新请求,那样的话,您就失去了AudioBuffers针对Audio元素的主要优势:它们确实非常快实例化。

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>

答案 1 :(得分:0)

在@Kaiido的帮助下找到了答案

同时同步并在上一个曲目停止的地方开始新曲目的示例:

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