Web Audio API возобновление из паузы

Я часто читал, что невозможно приостановить/возобновить аудиофайлы с помощью Web Audio API.
Но теперь я увидел ... --8-->пример, где они фактически сделали возможным приостановить и возобновить его. Я пытался понять, как они это сделали. Я подумал, может быть ... --1--> - это ключ, но это не так.
На данный момент мой звук всегда воспроизводится заново с самого начала.

Это мой текущий код

var context = new (window.AudioContext || window.webkitAudioContext)();

function AudioPlayer() {
  this.source = context.createBufferSource();
  this.analyser = context.createAnalyser();
  this.stopped = true;
}

AudioPlayer.prototype.setBuffer = function(buffer) {
  this.source.buffer = buffer;
  this.source.looping = false;
};

AudioPlayer.prototype.play = function() {
  this.source.connect(this.analyser);
  this.analyser.connect(context.destination);

  this.source.noteOn(0);
  this.stopped = false;
};

AudioPlayer.prototype.stop = function() {
  this.analyser.disconnect();
  this.source.disconnect();
  this.stopped = true;
};

кто-нибудь знаешь, ЧТО ДЕЛАТЬ, чтобы заставить его работать?

8 ответов


не тратя времени на проверку источника вашего примера, я бы сказал, что вы захотите использовать метод noteGrainOn AudioBufferSourceNode (https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#methodsandparams-AudioBufferSourceNode)

просто следите за тем, как далеко в буфер вы были, когда вы вызвали noteOff, а затем do noteGrainOn оттуда при возобновлении на новом AudioBufferSourceNode.

Это смысл?

изменить: См. комментарии ниже для обновленных вызовов API.


ответ Оскара и комментарий Айка очень полезны, но мне не хватало примера кода. Поэтому я написал одно:2-->http://jsfiddle.net/v3syS/2/ я надеюсь, что это поможет.

var url = 'http://thelab.thingsinjars.com/web-audio-tutorial/hello.mp3';

var ctx = new webkitAudioContext();
var buffer;
var sourceNode;

var startedAt;
var pausedAt;
var paused;

function load(url) {
    var request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.responseType = 'arraybuffer';
    request.onload = function() {
        ctx.decodeAudioData(request.response, onBufferLoad, onBufferError);
    };
    request.send();
};

function play() {
    sourceNode = ctx.createBufferSource();
    sourceNode.connect(ctx.destination);
    sourceNode.buffer = buffer;
    paused = false;

    if (pausedAt) {
        startedAt = Date.now() - pausedAt;
        sourceNode.start(0, pausedAt / 1000);
    }
    else {
        startedAt = Date.now();
        sourceNode.start(0);
    }
};

function stop() {
    sourceNode.stop(0);
    pausedAt = Date.now() - startedAt;
    paused = true;
};

function onBufferLoad(b) {
    buffer = b;
    play();
};

function onBufferError(e) {
    console.log('onBufferError', e);
};

document.getElementById("toggle").onclick = function() {
    if (paused) play();
    else stop();
};

load(url);

в текущих браузерах (Chrome 43, Firefox 40) теперь доступны методы "приостановить" и "возобновить" для AudioContext:

var audioCtx = new AudioContext();
susresBtn.onclick = function() {
  if(audioCtx.state === 'running') {
    audioCtx.suspend().then(function() {
      susresBtn.textContent = 'Resume context';
    });
  } else if(audioCtx.state === 'suspended') {
    audioCtx.resume().then(function() {
      susresBtn.textContent = 'Suspend context';
    });  
  }
}

(измененный пример кода изhttps://developer.mozilla.org/en-US/docs/Web/API/AudioContext/suspend)


на самом деле Web-audio API может выполнить задачу паузы и воспроизведения для вас. Он знает текущее состояние аудио контекста (работает или приостановлено), поэтому вы можете сделать это таким простым способом:

susresBtn.onclick = function() {
  if(audioCtx.state === 'running') {
    audioCtx.suspend()
  } else if(audioCtx.state === 'suspended') {
    audioCtx.resume()  
  }
}

Я надеюсь, что это может помочь.


отсутствие встроенной функции паузы в API WebAudio кажется мне серьезным упущением. Возможно, в будущем это можно будет сделать с помощью запланированного MediaElementSource, который позволит вам подключить элемент (который поддерживает паузу) к веб-аудио. На данный момент большинство обходных путей, похоже, основаны на запоминании времени воспроизведения (например, как описано в ответе имбризи). У такого обходного пути есть проблемы при циклическом звучании (делает ли цикл реализации gapless или нет?), и когда вы динамически разрешить изменить playbackRate звуков (как влияет на сроки). Другой, одинаково хакерский и технически некорректный, но гораздо более простой обходной путь, который вы можете использовать:

source.playbackRate = paused?0.0000001:1;

к сожалению, 0 не является допустимым значением для playbackRate (что фактически приостановит звук). Однако для многих практических целей некоторое очень низкое значение, например 0.000001, достаточно близко, и оно не будет производить никакого звукового выхода.


UPDATE: это действительно только для Chrome. Firefox (v29) еще не реализует MediaElementAudioSourceNode.mediaElement собственность.

предполагая, что у вас уже есть ссылка AudioContext и ваш источник мультимедиа (например, через AudioContext.createMediaElementSource() вызов метода), вы можете позвонить MediaElement.play() и MediaElement.pause()на вашем источнике, например

source.mediaElement.pause();
source.mediaElement.play();

нет необходимости в хаки и обходные пути, он поддерживается. Если вы работаете с <audio> тег в качестве источника, вы не должны вызывать паузу непосредственно на аудио элемент в вашем JavaScript, который остановит воспроизведение.


в 2017 году с помощью ctx.currentTime хорошо работает для отслеживания точки в песне. Приведенный ниже код использует одну кнопку (songStartPause), которая переключает кнопку воспроизведения. Я использовал глобальные переменные для простоты. Переменная musicStartPoint отслеживает, в какое время вы находитесь в песне. Музыкальный api отслеживает время в секундах.

установите начальную точку musicStartPoint в 0 (начало песни)

var ctx = new webkitAudioContext();
var buff, src;
var musicLoaded = false;
var musicStartPoint = 0;
var songOnTime, songEndTime;
var songOn = false;

songStartPause.onclick = function() {

  if(!songOn) {
    if(!musicLoaded) {
      loadAndPlay();
      musicLoaded = true;
    } else {
      play();
    }

    songOn = true;
    songStartPause.innerHTML = "||"  //a fancy Pause symbol

  } else {
    songOn = false;
    src.stop();
    setPausePoint();
    songStartPause.innerHTML = ">"  //a fancy Play symbol
  }
}

используйте ctx.currentTime, чтобы вычесть время окончания песни с момента ее начала и добавьте это время к тому, как далеко вы были в песне изначально.

function setPausePoint() {
  songEndTime = ctx.currentTime;
  musicStartPoint += (songEndTime - songOnTime);
}

функции загрузки / воспроизведения.

function loadAndPlay() {
  var req = new XMLHttpRequest();
  req.open("GET", "//mymusic.com/unity.mp3")
  req.responseType = "arraybuffer";
  req.onload = function() {
    ctx.decodeAudioData(req.response, function(buffer) {
      buff = buffer;
      play();
    })
  }
  req.send();
}

function createBuffer() {
      src = ctx.createBufferSource();
      src.buffer = buff;
    }


function connectNodes() {  
  src.connect(ctx.destination); 
}

наконец, функция воспроизведения сообщает, что песня начинается с указанной musicStartPoint (и воспроизводится немедленно), а также устанавливает переменную songOnTime.

function play(){
  createBuffer() 
  connectNodes();
  songOnTime = ctx.currentTime;
  src.start(0, musicStartPoint);
}

*Sidenote: я знаю, что это может выглядеть чище, чтобы установить songOnTime в функции click, но я считаю, что имеет смысл захватить код времени как можно ближе к src.начните, так же, как мы хватаем время паузы как можно ближе к src.остановка.


Я не следил за полным обсуждением, но скоро буду. Я просто направился к Хэлу демо, чтобы понять. Для тех, кто сейчас любит меня, я хотел бы сказать 1-Как заставить этот код работать сейчас. 2 - трюк, чтобы сделать паузу/воспроизведение, из этого кода.

1: Замените noteOn (xx) на start(xx) и поместите любой допустимый url в звук.нагрузка.)( Думаю, это все, что я сделал. Вы получите несколько ошибок в консоли, что довольно директивы. Следовать за ними. Или нет : иногда вы можете игнорировать их, это работает теперь : это связано с префиксом-webkit в некоторой функции. Даются новые. 2: в какой-то момент, когда он работает, вы можете приостановить звук. Это сработает. Но, как всем известно, новое давление на игру вызовет ошибку. В результате код в этом.play () после неисправного источника.start (0) не выполняется. Я просто заключил эти строки в try / catch:

      this.play = function() {
    analyser_ = context_.createAnalyser();

    // Connect the processing graph: source -> analyser -> destination
    source_.connect(analyser_);
    analyser_.connect(context_.destination);
  try{
    source_.start(0);
  }
  catch(e){
    this.playing = true;

    (function callback(time) {
      processAudio_(time);
      reqId_ = window.webkitRequestAnimationFrame(callback);
    })();
  }

и это работает : вы можете использовать play / pause. Я хотел бы отметить, что это моделирование HAL это действительно невероятно. Следуйте этим простым шагам, это того стоит !