javascript Web Audio API 从暂停状态恢复

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11506180/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-26 13:27:09  来源:igfitidea点击:

Web Audio API resume from pause

javascripthtmlgoogle-chromehtml5-audioweb-audio-api

提问by dan-lee

I often read that it's not possible to pause/resume audio files with the Web Audio API.
But now I saw a examplewhere they actually made it possible to pause and resume it. I tried to figure out what how they did it. I thought maybe source.looping = falseis the key, but it wasn't.
For now my audio is always re-playing from the start.

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

This is my current code

这是我当前的代码

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

Does anybody know what to do, to get it work?

有人知道该怎么做才能让它发挥作用吗?

采纳答案by Oskar Eriksson

Without spending any time checking the source of your example, I'd say you'll want to use the noteGrainOn method of the AudioBufferSourceNode (https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#methodsandparams-AudioBufferSourceNode)

无需花任何时间检查示例的源代码,我想说您会想要使用 AudioBufferSourceNode 的 noteGrainOn 方法(https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/规范.html#methodsandparams-AudioBufferSourceNode)

Just keep track of how far into the buffer you were when you called noteOff, and then do noteGrainOn from there when resuming on a new AudioBufferSourceNode.

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

Did that make sense?

那有意义吗?

EDIT: See comments below for updated API calls.

编辑: 有关更新的 API 调用,请参阅下面的评论。

EDIT 2, 2019: See MDN for updated API calls; https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start

2019 年编辑 2:有关更新的 API 调用,请参阅 MDN;https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start

回答by imbrizi

Oskar's answer and ayke's comment are very helpful, but I was missing a code example. So I wrote one: http://jsfiddle.net/v3syS/2/I hope it helps.

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

回答by schellmax

In current browsers (Chrome 43, Firefox 40) there are now 'suspend' and 'resume' methods available for AudioContext:

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

(modified example code from https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/suspend)

(来自https://developer.mozilla.org/en-US/docs/Web/API/AudioContext/suspend 的修改示例代码)

回答by Carlos Angulo

Actually the web-audio API can do the pause and play task for you. It knows the current state of the audio context (running or suspended), so you can do this in this easy way:

实际上,网络音频 API 可以为您完成暂停和播放任务。它知道音频上下文的当前状态(运行或暂停),因此您可以通过这种简单的方式做到这一点:

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

I hope this can help.

我希望这会有所帮助。

回答by Pan Bouradas

For chrome fix, every time you want to play sound, set it like:

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

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

回答by jonas echterhoff

The lack of a built-in pause functionality in the WebAudio API seems like a major oversight to me. Possibly, in the future it will be possible to do this using the planned MediaElementSource, which will let you hook up an element (which supports pausing) to Web Audio. For now, most workarounds seem to be based on remembering playback time (such as described in imbrizi's answer). Such a workaround has issues when looping sounds (does the implementation loop gapless or not?), and when you allow dynamically change the playbackRate of sounds (as both affect timing). Another, equally hack-ish and technically incorrect, but much simpler workaround you can use is:

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

source.playbackRate = paused?0.0000001:1;

Unfortunately, 0 is not a valid value for playbackRate (which would actually pause the sound). However, for many practical purposes, some very low value, like 0.000001, is close enough, and it won't produce any audible output.

不幸的是,0 不是一个有效的播放速率值(它实际上会暂停声音)。但是,对于许多实际用途,一些非常低的值(例如 0.000001)已经足够接近,并且不会产生任何声音输出。

回答by Kovács Imre

UPDATE: This is only valid for Chrome. Firefox (v29) does not yet implement the MediaElementAudioSourceNode.mediaElementproperty.

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

Assuming that you already have the AudioContext reference and your media source (e.g. via AudioContext.createMediaElementSource()method call), you can call MediaElement.play()and MediaElement.pause()on your source, e.g.

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

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

No need for hacks and workarounds, it's supported. If you are working with an <audio>tag as your source, you should not call pause directly on the audio element in your JavaScript, that will stop playback.

不需要黑客和变通方法,它是受支持的。如果您使用<audio>标签作为源,则不应直接在 JavaScript 中的音频元素上调用 pause,否则会停止播放。

回答by user3597313

In 2017, using ctx.currentTime works well for keeping track of the point in the song. The code below uses one button (songStartPause) that toggles between a play & pause button. I used global variables for simplicity's sake. The variable musicStartPoint keeps track of what time you're at in the song. The music api keeps track of time in seconds.

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

Set your initial musicStartPoint at 0 (beginning of the song)

将您的初始 musicStartPoint 设置为 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
  }
}

Use ctx.currentTime to subtract the time the song ends from when it started, and append this length of time to however far you were in the song initially.

使用 ctx.currentTime 从歌曲开始时减去歌曲结束时间,并将此时间长度附加到您最初在歌曲中的位置。

function setPausePoint() {
  songEndTime = ctx.currentTime;
  musicStartPoint += (songEndTime - songOnTime);
}

Load/play functions.

加载/播放功能。

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

Lastly, the play function tells the song to start at the specified musicStartPoint (and to play it immediately), and also sets the songOnTime variable.

最后, play 函数告诉歌曲从指定的 musicStartPoint 开始(并立即播放),并设置 songOnTime 变量。

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

*Sidenote: I know it might look cleaner to set songOnTime up in the click function, but I figure it makes sense to grab the time code as close as possible to src.start, just like how we grab the pause time as close as possible to src.stop.

*旁注:我知道在 click 函数中设置 songOnTime 看起来更简洁,但我认为获取时间码尽可能接近 src.start 是有意义的,就像我们如何尽可能地获取暂停时间一样到 src.stop。

回答by Artatum

I didn't follow the full discussion, but I will soon. I simply headed over HAL demo to understand. For those who now do like me, I would like to tell 1 - how to make this code working now. 2 - a trick to get pause/play, from this code.

我没有关注完整的讨论,但我很快就会关注。我只是通过 HAL 演示来理解。对于那些现在喜欢我的人,我想告诉 1 - 如何让这段代码现在工作。2 - 从这段代码中获得暂停/播放的技巧。

1 : replace noteOn(xx) with start(xx) and put any valid url in sound.load(). I think it's all I've done. You will get a few errors in the console that are pretty directive. Follow them. Or not : sometimes you can ignore them, it works now : it's related to the -webkit prefix in some function. New ones are given. 2 : at some point, when it works, you may want to pause the sound. It will work. But, as everybody knows, a new pressing on play would raise an error. As a result, the code in this.play() after the faulty source_.start(0) is not executed. I simply enclosed those line in a try/catch :

1:用 start(xx) 替换 noteOn(xx) 并将任何有效的 url 放入 sound.load()。我想这就是我所做的一切。您将在控制台中收到一些非常指令的错误。跟着他们。或者不:有时你可以忽略它们,它现在可以工作:它与某些函数中的 -webkit 前缀有关。给了新的。2:在某些时候,当它起作用时,您可能想要暂停声音。它会起作用。但是,众所周知,新的压迫会引发错误。结果,错误的source_.start(0)之后的this.play()中的代码没有被执行。我只是将这些行包含在 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);
    })();
  }

And it works : you can use play/pause. I would like to mention that this HAL simulationis really incredible. Follow those simple steps, it's worth it !

它有效:您可以使用播放/暂停。我想提一下,这个HAL模拟真的很不可思议。按照这些简单的步骤,这是值得的!