Bug 70061

Summary: Feature request: a way to schedule callbacks without relying on setTimeout
Product: WebKit Reporter: Boris Smus <smus>
Component: Web AudioAssignee: Nobody <webkit-unassigned>
Status: UNCONFIRMED ---    
Severity: Normal CC: agoode, crogers, fred, jamesr, simonjam, steve_sims7, tomas
Priority: P2    
Version: 528+ (Nightly build)   
Hardware: Unspecified   
OS: Unspecified   

Description Boris Smus 2011-10-13 15:04:21 PDT
To do things like scheduling playback of multiple tracks into the future, currently need to rely on setTimeout calls, as in the following example:

  var duration = bufferNowPlaying.duration;
  ctx.timer = setTimeout(function() {
    playNextSong(bufferToPlayNext);
  }, duration * 1000);

setTimeout is not precise, especially if dealing with a page that isn't in the foreground. Since Web Audio provides an accurate timing engine, we should use that instead!

For example, we could have a method scheduleCallbackAtTime(time, function) on the AudioContext, that would take a time (in ms) and a callback function to call at that precise time.
Comment 1 Chris Rogers 2011-11-05 13:13:52 PDT
I would suggest naming it "callbackAtTime" and change the order of the arguments to be similar to setTimeout():

callbackAtTime(callback, time)

So an example would look like:

audioContext.callbackAtTime(myCallback, 5);
Comment 2 Boris Smus 2011-11-07 12:47:41 PST
Sounds great.
Comment 3 steve_sims7 2011-11-16 13:49:25 PST
I'd suggest that just like setTimeout it should return back a timer reference that can be used to cancel the callback.

This could be useful in the event that one has pending callbacks and the user performs an action that causes the audio playback to get paused.
Comment 4 Ojan Vafai 2012-11-02 14:39:08 PDT
What's to stop people using this the same way they do setTimeout and eating all your cpu/battery with background tabs? I guess we expect people to mainly use it for audio, so we don't want to throttle it anyways?

I suppose if we end up giving some UI to disable audio for a tab or, if the system volume is muted, we could throttle this API the way we do setTimeout for background tabs. That'd be kind of neat actually. Would be great if we could do this from the initial launch of the API to prevent people abusing this as a generic timer API.

Bikeshed nit: callbackAtTime reads to me as something that would take a time (e.g. since the epoch) to callback at. How about just calling it setTimeout?

audioContext.setTimeout(callback, delay);

Do we want the delay to be in milliseconds or something more fine-grained? In other APIs the way we dealt with more fine grained was to have it still be milliseconds, but allow decimal values.
Comment 5 Chris Rogers 2012-11-02 14:45:53 PDT
(In reply to comment #4)
> What's to stop people using this the same way they do setTimeout and eating all your cpu/battery with background tabs? I guess we expect people to mainly use it for audio, so we don't want to throttle it anyways?

One of the most interesting reasons for having this is so that there is no throttling.

> 
> I suppose if we end up giving some UI to disable audio for a tab or, if the system volume is muted, we could throttle this API the way we do setTimeout for background tabs. That'd be kind of neat actually. Would be great if we could do this from the initial launch of the API to prevent people abusing this as a generic timer API.

I don't know if there's any reasonable way to prevent people from abusing it.

> 
> Bikeshed nit: callbackAtTime reads to me as something that would take a time (e.g. since the epoch) to callback at. How about just calling it setTimeout?

But it *is* a time in the time-coordinate system of the AudioContext.  If you look carefully at the Web Audio API specification, then you can see that these times are used extensively.  For example, there are AudioParam methods called "setValueAtTime()", etc.

> 
> audioContext.setTimeout(callback, delay);
> 
> Do we want the delay to be in milliseconds or something more fine-grained? In other APIs the way we dealt with more fine grained was to have it still be milliseconds, but allow decimal values.

We already have a time-system that's well established in the Web Audio API, so we know that this value is in terms of AudioContext.currentTime (which is in double-precision seconds)
Comment 6 Ojan Vafai 2012-11-02 14:51:43 PDT
(In reply to comment #5)
> (In reply to comment #4)
> > I suppose if we end up giving some UI to disable audio for a tab or, if the system volume is muted, we could throttle this API the way we do setTimeout for background tabs. That'd be kind of neat actually. Would be great if we could do this from the initial launch of the API to prevent people abusing this as a generic timer API.
> 
> I don't know if there's any reasonable way to prevent people from abusing it.

You don't like the idea of throttling when audio is muted? Specifically, I was thinking this could work more like requestAnimationFrame. It gives the callback an argument of the actual time so that it can know if some callbacks got skipped and catch up appropriately.

It's admittedly a less developer-friendly API, but the benefits of not using your CPU for audio the user isn't hearing seem possibly worth it.

I'm not wed to this. Just thinking out loud.

> > Bikeshed nit: callbackAtTime reads to me as something that would take a time (e.g. since the epoch) to callback at. How about just calling it setTimeout?
> 
> But it *is* a time in the time-coordinate system of the AudioContext.  If you look carefully at the Web Audio API specification, then you can see that these times are used extensively.  For example, there are AudioParam methods called "setValueAtTime()", etc.

Oh, I see. callbackAtTime makes more sense then.
Comment 7 Chris Rogers 2012-11-02 15:08:22 PDT
(In reply to comment #6)
> (In reply to comment #5)
> > (In reply to comment #4)
> > > I suppose if we end up giving some UI to disable audio for a tab or, if the system volume is muted, we could throttle this API the way we do setTimeout for background tabs. That'd be kind of neat actually. Would be great if we could do this from the initial launch of the API to prevent people abusing this as a generic timer API.
> > 
> > I don't know if there's any reasonable way to prevent people from abusing it.
> 
> You don't like the idea of throttling when audio is muted? Specifically, I was thinking this could work more like requestAnimationFrame. It gives the callback an argument of the actual time so that it can know if some callbacks got skipped and catch up appropriately.
> 
> It's admittedly a less developer-friendly API, but the benefits of not using your CPU for audio the user isn't hearing seem possibly worth it.
> 
> I'm not wed to this. Just thinking out loud.

The developer can always query AudioContext.currentTime to see what the actual time is (as compared with the requested callback time), so that would come for free.

I'm not sure what you mean by "audio is muted".  I suppose if a user agent has a UI for muting a tab, then throttling might make sense.  But that shouldn't be the normal case.


> 
> > > Bikeshed nit: callbackAtTime reads to me as something that would take a time (e.g. since the epoch) to callback at. How about just calling it setTimeout?
> > 
> > But it *is* a time in the time-coordinate system of the AudioContext.  If you look carefully at the Web Audio API specification, then you can see that these times are used extensively.  For example, there are AudioParam methods called "setValueAtTime()", etc.
> 
> Oh, I see. callbackAtTime makes more sense then.
Comment 8 Ojan Vafai 2012-11-02 16:47:59 PDT
(In reply to comment #7)
> I'm not sure what you mean by "audio is muted".  I suppose if a user agent has a UI for muting a tab, then throttling might make sense.  But that shouldn't be the normal case.

I know we've talked about adding UI to see which tab is making sound and mute it to Chrome. Also, if you mute the audio at the OS level. I assume we can detect that.

If I have http://static.echonest.com/InfiniteGangnamStyle/ loaded in a background tab and I have sound muted, I don't see why it should use CPU generating audio that I can't hear.