Web Audio API - несколько синхронизированных треков - остановка предыдущего трека при запуске нового трека - PullRequest
0 голосов
/ 21 сентября 2018

Я пытаюсь имитировать демонстрацию мультитрека Web Audio API из веб-документа «Mozilla Web Audio API для игр».

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

Единственное предупреждение, которое у меня есть, заключается в том, чтоЯ хочу, чтобы предыдущий трек остановился, когда щелкнет новый трек (вместо того, чтобы проигрывать многослойно друг над другом).

Например, нажмите барабаны, барабаны начнут играть, затем нажмите гитару,барабаны останавливаются, и гитара начинает играть именно там, где барабаны прекратились.

Есть идеи?Есть ли лучшие инструменты / библиотеки для обработки веб-аудио?

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 голосов
/ 22 сентября 2018

Разобрался с помощью @ 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/

0 голосов
/ 21 сентября 2018

Просто сохраните 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>
...