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调用,请参阅下面的注释。

+0

那么他在示例中没有使用'noteGrainOn()'(尽管如此,仍然无法弄清楚他是如何做到的)。但无论如何,这是一种魅力,所以我将把它作为一个被接受的答案。谢谢:) – 2012-07-16 14:59:49

+1

noteGrainOn()现在已经在spec中重命名了,就像noteOn() - 都是现在的play(),有或没有grain偏移参数。尽管如此,还没有看到这种支持。 – LeeGee 2012-10-03 12:53:50

+1

它实际上是'开始'(有或没有参数'开始(howSoonRelativeToCurrentTime,fromSecondInBuffer,forDuration)'),它似乎在铬金丝雀工作 – 2013-01-27 10:12:43

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)足够接近,并且不会产生任何声音输出。

+0

+10为快速入侵:-) – Sam 2014-05-25 16:50:11

+0

这不是一个好主意,因为您将阻止浏览器清除任何未使用的音频缓冲区。使用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。