Web音频API从暂停恢复
我经常读到,无法使用Web Audio API暂停/恢复音频文件。
但现在我看到一个example他们实际上可以暂停和恢复它。我试图弄清楚他们是怎么做到的。我想也许source.looping = false
是关键,但事实并非如此。
现在我的音频始终从头播放。Web音频API从暂停恢复
这是我当前的代码
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;
};
有谁知道做什么,得到它的工作?
无需花费任何时间检查你的榜样的来源,我说你要使用AudioBufferSourceNode的noteGrainOn方法(https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#methodsandparams-AudioBufferSourceNode)
只是跟踪你是如何在缓冲区中,当你打电话给noteOff,然后在新的AudioBufferSourceNode上恢复时从那里做noteGrainOn。
这有道理吗?
编辑: 有关更新的API调用,请参阅下面的注释。
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);
在WebAudio API中缺少内置的暂停功能似乎是对我的一个重大疏漏。可能,将来可以使用计划的MediaElementSource来完成此操作,它可以让您将一个元素(支持暂停)连接到Web Audio。目前,大多数解决方法似乎基于记忆播放时间(如imbrizi的答案中所述)。这样的解决方法在循环声音时会遇到问题(执行循环是否无间隙?),以及何时允许动态更改声音的播放速率(因为两者都会影响定时)。另外,你可以使用,同样劈十岁上下,技术上不正确的,但更简单的解决方法是:
source.playbackRate = paused?0.0000001:1;
遗憾的是,0不是playbackRate有效值(这实际上将暂停的声音)。但是,对于许多实际用途,某些非常低的值(如0.000001)足够接近,并且不会产生任何声音输出。
+10为快速入侵:-) – Sam 2014-05-25 16:50:11
这不是一个好主意,因为您将阻止浏览器清除任何未使用的音频缓冲区。使用API实际上有点笨拙,但是它出于性能和内存效率的原因而设计。 OPs问题中的小包装库是更好的方法。 – 2014-06-14 11:43:33
更新:这只适用于Chrome。 Firefox(v29)尚未实现MediaElementAudioSourceNode.mediaElement
属性。
假设你已经有了AudioContext参考和媒体来源(例如通过AudioContext.createMediaElementSource()
方法调用),您可以将您的来源,例如在呼叫MediaElement.play()
和MediaElement.pause()
source.mediaElement.pause();
source.mediaElement.play();
不需要黑客和变通办法,它是受支持的。 如果您使用<audio>
标记作为源,则不应直接在JavaScript中的音频元素上调用暂停,以停止播放。
在当前的浏览器(浏览器43,火狐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代码)
实际上在网络 - 音频API可以为您暂停并播放任务。它知道音频方面的当前状态(运行或暂停),这样你就可以在这个简单的方法做到这一点:
susresBtn.onclick = function() {
if(audioCtx.state === 'running') {
audioCtx.suspend()
} else if(audioCtx.state === 'suspended') {
audioCtx.resume()
}
}
我希望这可以帮助。
2017年,使用ctx.currentTime非常适合追踪歌曲中的点。下面的代码使用一个按钮(songStartPause),它在播放&暂停按钮之间切换。为了简单起见,我使用了全局变量。变量musicStartPoint记录您在歌曲中的时间。音乐api以秒为单位记录时间。
为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设置你的初始musicStartPoint向然而到目前为止你减去这首歌从什么时候开始结束时间,并追加这段时间的长度最初在歌曲中。
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);
}
*旁注:我知道这可能看起来更清洁的点击功能设置songOnTime了,但我想这是有道理抢时间码尽可能接近到src.start,就像我们如何抢暂停时间尽可能靠近src.stop。
那么他在示例中没有使用'noteGrainOn()'(尽管如此,仍然无法弄清楚他是如何做到的)。但无论如何,这是一种魅力,所以我将把它作为一个被接受的答案。谢谢:) – 2012-07-16 14:59:49
noteGrainOn()现在已经在spec中重命名了,就像noteOn() - 都是现在的play(),有或没有grain偏移参数。尽管如此,还没有看到这种支持。 – LeeGee 2012-10-03 12:53:50
它实际上是'开始'(有或没有参数'开始(howSoonRelativeToCurrentTime,fromSecondInBuffer,forDuration)'),它似乎在铬金丝雀工作 – 2013-01-27 10:12:43