Веб-аудио 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 действительно невероятна. Следуйте этим простым шагам, оно того стоит!