Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Web Audio API and real current time when playing an audio file

I am having problems when I want to know the current time of a file playing using the Web Audio API. My code plays the file nicely and the current time returned by the getCurrentTime() function is accurate enough when it comes to short files which load fast.

But when I try to load big files, sometimes the current time returned by the getCurrentTime() function is accurate and sometimes not. Sometimes, after waiting for example for 20 seconds to hear the file playing, when it starts playing it says that the current time is about 20 seconds (which is not true because it is just playing the beginning of the file). It happens with any audio format (OGG, MP3, WAV...) but only sometimes.

I am using a slow system (Asus EEE PC 901 with an Intel Atom 1.60 Ghz and 2 GB RAM with Windows XP Home Edition and SP3) and Firefox 41.0.1.

I am not sure, but it seems that the source.start() method starts playing the sound way too late, so the line after calling that method, where I set the value for the startTime variable, is not the real starting time.

Here is the code (simplified):

var context, buffer, startTime, source;
var stopped = true;
function load(file, startAt)
{
    //Here creates the AudioContext and loads the file through XHR (AJAX) and gets the buffer. All works fine.
    //When it gots the buffer through XHR (AJAX) and all is fine, it calls play(startAt) function immediately.
    //Note: normally, startAt is 0.
}

function play(startAt)
{
    source = context.createBufferSource(); //Context created before.
    source.buffer = buffer; //Buffer got before from XHR (AJAX).

    //Creates a gain node to be able to set the volume later:
    var gainNode = context.createGain();
    source.connect(gainNode);
    gainNode.connect(context.destination);

    //Plays the sound:
    source.loop = false;
    source.start(startAt, 0, buffer.duration - 3); //I don't want the last 3 seconds.

    //Stores the start time (useful for pause/resume):
    startTime = context.currentTime - startAt; //Here I store the startTime but maybe the file has still not begun to play (should it be just startTime = context.currentTime?).
    stopped = false;
}

function stop()
{
    source.stop(0);
    stopped = true;
}

function getCurrentTime()
{
    return (stopped) ? 0 : context.currentTime - startTime;
}

How can I detect when exactly the source.start() method starts playing the file? So I can set the startTime variable value just at that moment, and never before.

Thank you very much in advance. I would really appreciate any kind of help.

like image 484
jalbam Avatar asked Oct 08 '15 04:10

jalbam


2 Answers

From MDN (https://developer.mozilla.org/en-US/docs/Web/API/AudioBufferSourceNode/start), about the first parameter of the start() function:

when (Optional)

The time, in seconds, at which the sound should begin to play, in the same time coordinate system used by the AudioContext. If when is less than (AudioContext.currentTime, or if it's 0, the sound begins to play at once. The default value is 0.

There is no evident issue with your code (although there is no example a call to play()): if you call play(0) or play(context.currentTime + someDelayInSeconds), start() should behave as expected. Unfortunately here the issue is that AudioBufferSource is not meant for big files. Again from the MDN doc of AudioBuffer (https://developer.mozilla.org/en-US/docs/Web/API/AudioBuffer):

Objects of these types are designed to hold small audio snippets, typically less than 45 s.

I suspect that for big something doesn't work very well with the "sound begins play at once" assumption (I also experienced it, although 20 seconds seems way too much...). Unfortunately there is no way to get the exact start time of AudioBufferSource in WebAudio yet.

If you don't have any real reason to load this big file with AudioBufferSource, I suggest you use a MediaElementSourceNode (https://developer.mozilla.org/en-US/docs/Web/API/MediaElementAudioSourceNode): as you can see from the example on the linked doc, it allows you to plug a simple HTML5 Audio element into the AudioContext. You then can have all usual control over the element itself, i.e. you also have access to the audioElement.currentTime property, which tells you the current playout time (in this case of the file itself, which is what you need). Additionally, you don't have to handle loading of the file in memory and could start playing as soon as some data is available.

like image 118
Stefano Bider Avatar answered Nov 19 '22 13:11

Stefano Bider


context.currentTime starts counting the second you create the context object. That means if it takes 20 seconds for your audio to load, context.currentTime == 20.

To account for this delay, you can set a simple timer from the time that you create the context to the time that audio loading completes.

var context; //Create your context here
var audioLoadStart = new Date();
//Do audio load
var audioLoadOffset = (new Date() - audioLoadStart) / 1000;
currentTime = context.currentTime - audioLoadOffset - startTime;
like image 2
Jack Guy Avatar answered Nov 19 '22 13:11

Jack Guy