Javascript Web音频API从暂停恢复

Javascript Web音频API从暂停恢复,javascript,html,google-chrome,html5-audio,web-audio-api,Javascript,Html,Google Chrome,Html5 Audio,Web Audio Api,我经常读到,无法使用暂停/恢复音频文件。 但现在我看到了一个他们实际上可以暂停并继续的地方。我试图弄清楚他们是怎么做的。我认为可能source.looping=false是关键,但事实并非如此。 现在,我的音频总是从一开始就重新播放 这是我当前的代码 var context=new(window.AudioContext | | window.webkitadiocontext)(); 函数AudioPlayer(){ this.source=context.createBufferSource

我经常读到,无法使用暂停/恢复音频文件。
但现在我看到了一个他们实际上可以暂停并继续的地方。我试图弄清楚他们是怎么做的。我认为可能
source.looping=false
是关键,但事实并非如此。
现在,我的音频总是从一开始就重新播放

这是我当前的代码

var context=new(window.AudioContext | | window.webkitadiocontext)();
函数AudioPlayer(){
this.source=context.createBufferSource();
this.analyzer=context.createAnalyzer();
this.stopped=true;
}
AudioPlayer.prototype.setBuffer=函数(缓冲区){
this.source.buffer=缓冲区;
this.source.looping=false;
};
AudioPlayer.prototype.play=函数(){
本源连接(本分析仪);
this.analyzer.connect(context.destination);
此.source.noteOn(0);
this.stopped=false;
};
AudioPlayer.prototype.stop=函数(){
这个.analyzer.disconnect();
this.source.disconnect();
this.stopped=true;
};

有人知道该怎么做才能让它工作吗?

如果不花时间检查示例的源代码,我会说您希望使用AudioBufferSourceNode()的noteGrainOn方法

只需记录调用noteOff时进入缓冲区的距离,然后在新的AudioBufferSourceNode上恢复时从那里执行noteGrainOn

这有意义吗

编辑: 有关更新的API调用,请参见下面的注释


编辑2019年2月:有关更新的API调用,请参见MDN

Oskar的回答和ayke的评论非常有用,但我遗漏了一个代码示例。所以我写了一个:我希望它能有所帮助

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

WebAudioAPI中缺少内置的暂停功能对我来说似乎是一个重大疏忽。将来可能会使用计划中的MediaElementSource实现这一点,这将允许您将一个元素(支持暂停)连接到Web音频。目前,大多数解决方法似乎都是基于记忆播放时间(如imbrizi的回答中所述)。这种解决方法在循环声音(实现是否循环无间隙?)和允许动态更改声音播放速率(两者都会影响计时)时存在问题。另一个同样具有黑客风格且技术上不正确但更简单的解决方法是:

source.playbackRate = paused?0.0000001:1;

不幸的是,0不是playbackRate的有效值(实际上会暂停声音)。然而,出于许多实际目的,一些非常低的值(如0.000001)足够接近,并且不会产生任何声音输出。

更新:这仅对Chrome有效。Firefox(v29)尚未实现MediaElementAudioSourceNode.mediaElement属性

假设您已经拥有AudioContext引用和媒体源(例如通过
AudioContext.createMediaElementSource()
方法调用),您可以在源上调用
MediaElement.play()
MediaElement.pause()
,例如

source.mediaElement.pause();
source.mediaElement.play();
不需要黑客和变通方法,它是受支持的。 如果使用
标记作为源代码,则不应直接在JavaScript中的音频元素上调用pause,否则将停止播放

在当前浏览器(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';
    });  
  }
}

(从中修改示例代码)

实际上,web音频API可以为您完成暂停和播放任务。它知道音频上下文的当前状态(正在运行或已挂起),因此您可以通过以下简单方式执行此操作:

susresBtn.onclick=function(){
如果(audioCtx.state==='running'){
audioCtx.suspend()
}否则,如果(audioCtx.state==='suspended'){
audioCtx.resume()
}

}
2017年,使用ctx.currentTime可以很好地跟踪歌曲中的点。下面的代码使用一个按钮(songStartPause)在播放和暂停按钮之间切换。为了简单起见,我使用了全局变量。变量musicStartPoint记录您在歌曲中的时间。音乐api以秒为单位记录时间

将初始musicStartPoint设置为0(歌曲的开头)

使用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); 
}
最后,play函数告诉歌曲从指定的musicStartPoint开始(并立即播放),还设置了songOnTime变量

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

*旁注:我知道在点击功能中设置songOnTime可能看起来更干净,但我认为获取尽可能接近src.start的时间代码是有意义的,就像我们获取尽可能接近src.stop的暂停时间一样。

我没有关注完整的讨论,但我很快就会了解。我只是浏览了一下哈尔的演示,想了解一下。对于那些现在确实喜欢我的人,我想告诉他们 1-如何使此代码立即工作。 2-从该代码获得暂停/播放的技巧

1:将noteOn(xx)替换为start(xx),并将任何有效url放入sound.load()。我想这就是我所做的一切。您将在控制台中得到一些非常有指导性的错误。跟着他们。或者不可以:有时候你可以忽略它们,它现在可以工作了:它与某些函数中的-webkit前缀相关。给出了新的。 2:在某个时候,当它工作时,您可能需要暂停声音。 它会起作用的。但是,大家都知道,对比赛施加新的压力会导致一个错误。因此,此.play()中错误的源代码\uu.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);
    })();
  }
它可以工作:你可以使用播放/暂停。
我想说的是,这真是难以置信。遵循这些简单的步骤,这是值得的

对于chrome fix,每次要播放声音时,请将其设置为:

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

在示例中,他没有使用
noteGrainOn()
(但仍然不知道他是如何做到的)。但不管怎样,这就像一个符咒,s
if(audioCtx.state === 'suspended') {
    audioCtx.resume().then(function() {
      audio.play();
   });  
}else{
     audio.play();
}