Веб-аудио API возобновляется с паузы

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

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

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

Кто-нибудь знает, что делать, чтобы заставить его работать?

Ответ 1

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

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

Это имело смысл?

РЕДАКТИРОВАТЬ: см. Комментарии ниже для обновленных вызовов API.

РЕДАКТИРОВАНИЕ 2, 2019: см. MDN для обновленных вызовов API; https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start

Ответ 2

Ответ Oskar и комментарий ayke очень полезны, но мне не хватает кода. Поэтому я написал один: 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);

Ответ 3

В текущих браузерах (Chrome 43, Firefox 40) теперь доступны методы "suspend" и "resume" для 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)

Ответ 4

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

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

Ответ 5

Чтобы исправить хром, каждый раз, когда вы хотите воспроизвести звук, установите его следующим образом:

if(audioCtx.state === 'suspended') {
    audioCtx.resume().then(function() {
      audio.play();
   });  
}else{
     audio.play();
}

Ответ 6

Отсутствие встроенной функции паузы в API WebAudio кажется для меня главным оплотом. Возможно, в будущем это можно будет сделать с помощью запланированного MediaElementSource, который позволит вам подключить элемент (который поддерживает приостановку) к Web Audio. На данный момент большинство обходных решений, похоже, основано на запоминании времени воспроизведения (например, описанного в ответе imbrizi). Такое обходное решение имеет проблемы при циклическом звуке (делает ли цикл реализации безразличным или нет?), И когда вы разрешаете динамически изменять воспроизведениеRate звуков (так как оба влияют на время). Другой, одинаково хакерский и технически неправильный, но гораздо более простой способ обхода, который вы можете использовать:

source.playbackRate = paused?0.0000001:1;

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

Ответ 7

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

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

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

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

Ответ 8

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

Установите начальную музыку в форматеStartPoint в 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 в функции щелчка, но я считаю, что имеет смысл захватить временный код как можно ближе к src.start, точно так же, как мы захватим время паузы как как можно ближе к src.stop.

Ответ 9

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

1: замените noteOn (xx) на start (xx) и поместите любой допустимый URL в sound.load(). Я думаю, что все, что я сделал. Вы получите несколько ошибок в консоли, которые довольно директивны. Следуйте за ними. Или нет: иногда вы можете игнорировать их, это работает сейчас: это связано с префиксом -webkit в некоторой функции. Новые даны. 2: в какой-то момент, когда это работает, вы можете приостановить звук. Это будет работать. Но, как все знают, новое нажатие на игру вызовет ошибку. В результате код в this.play() после неисправного source_.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);
    })();
  }

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