Web Audio API resume from pause

24,189

Solution 1

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)

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.

Did that make sense?

EDIT: See comments below for updated API calls.

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

Solution 2

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.

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

Solution 3

In current browsers (Chrome 43, Firefox 40) there are now 'suspend' and 'resume' methods available for 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)

Solution 4

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:

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

I hope this can help.

Solution 5

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

if(audioCtx.state === 'suspended') {
    audioCtx.resume().then(function() {
      audio.play();
   });  
}else{
     audio.play();
}
Share:
24,189
dan-lee
Author by

dan-lee

Updated on May 23, 2020

Comments

  • dan-lee
    dan-lee about 4 years

    I often read that it's not possible to pause/resume audio files with the Web Audio API.
    But now I saw a example where 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.

    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?

  • dan-lee
    dan-lee almost 12 years
    Well he doesn't use noteGrainOn() in the example (still can't figure out how he does it, though). But anyway, this works like a charm, so I will take this as an accepted answer. Thank you :)
  • Lee Goddard
    Lee Goddard over 11 years
    noteGrainOn() has now been renamed in the spec, as has noteOn() -- both are now play(), with or without a grain offset argument. Haven't seen this supported yet, though.
  • Misha Reyzlin
    Misha Reyzlin over 11 years
    it's actually start (with or without arguments start(howSoonRelativeToCurrentTime, fromSecondInBuffer, forDuration)) and it seems to work in Chrome Canary
  • ayke
    ayke over 11 years
    start and stop can only be used only once: w3.org/TR/webaudio/#methodsandparams-AudioBufferSourceNode, but it is possible to make a new AudioBufferSourceNode from the same AudioBuffer and start it at the position where the last was stopped (with an offset).
  • Chris Lowis
    Chris Lowis about 10 years
    This isn't a great idea, as you'll prevent the browser from cleaning up any unused audio buffers. It's a little clunky to work with the API as it is, but it's designed that way for reasons of performance and memory efficiency. A small wrapper library like in the OPs question is a better approach.
  • bryc
    bryc about 5 years
    All of this is deprecated now, so this answer is useless. No noteGrainOn, no noteOn, no play, nothing.
  • Oskar Eriksson
    Oskar Eriksson about 5 years
    As the comments above mentions - this is now contained in the start method.
  • Jespertheend
    Jespertheend over 3 years
    You're better off using ctx.currentTime instead of Date.now(). That way the time gets preserved when suspending and resuming the context. And I think it might even be more accurate as well.
  • B''H Bi'ezras -- Boruch Hashem
    B''H Bi'ezras -- Boruch Hashem over 2 years
    but how do u pause it without stopping it?
  • B''H Bi'ezras -- Boruch Hashem
    B''H Bi'ezras -- Boruch Hashem over 2 years
    is there a way to change the current time, like for making custom audio players?
  • B''H Bi'ezras -- Boruch Hashem
    B''H Bi'ezras -- Boruch Hashem over 2 years
    how do u get the buffer data to a mediaelement?
  • B''H Bi'ezras -- Boruch Hashem
    B''H Bi'ezras -- Boruch Hashem over 2 years
    how do u pause it?