This is an archived snapshot of W3C's public bugzilla bug tracker, decommissioned in April 2019. Please see the home page for more details.

Bug 23007 - Unclear semantics of duration param to AudioBufferSourceNode.start()
Summary: Unclear semantics of duration param to AudioBufferSourceNode.start()
Status: CLOSED WONTFIX
Alias: None
Product: AudioWG
Classification: Unclassified
Component: Web Audio API (show other bugs)
Version: unspecified
Hardware: PC All
: P2 normal
Target Milestone: TBD
Assignee: Chris Rogers
QA Contact: public-audio
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2013-08-19 14:38 UTC by Joe Berkovitz / NF
Modified: 2014-10-28 17:17 UTC (History)
2 users (show)

See Also:


Attachments

Description Joe Berkovitz / NF 2013-08-19 14:38:11 UTC
One reading of the optional "duration" parameter AudioBufferSourceNode.start() is that it is a kind of syntactic sugar in which

   node.start(startTime, offset, duration);

behaves the same as:

   node.start(startTime, offset);
   node.stop(startTime + duration, offset);

Another view is that the duration paramter has slightly different semantics from the stop/start delta, although exactly how it differs is unclear. Gecko's interpretation is reflected in this WebKit bug:

  https://bugs.webkit.org/show_bug.cgi?id=111952

which states that the duration parameter is ignored if the buffer is in loop mode.

My feeling is that the syntactic-sugar interpretation (duration is equivalent stop/start delta) is the cleanest, most obvious behavior and was probably the original intention of the parameter, however objectionable the sugar flavoring may be.
Comment 1 Ehsan Akhgari [:ehsan] 2013-08-19 16:07:20 UTC
(In reply to comment #0)
> One reading of the optional "duration" parameter
> AudioBufferSourceNode.start() is that it is a kind of syntactic sugar in
> which
> 
>    node.start(startTime, offset, duration);
> 
> behaves the same as:
> 
>    node.start(startTime, offset);
>    node.stop(startTime + duration, offset);

Hmm, stop only accepts one argument, but yes, these should be the same in the non-looping mode.

> Another view is that the duration paramter has slightly different semantics
> from the stop/start delta, although exactly how it differs is unclear.
> Gecko's interpretation is reflected in this WebKit bug:
> 
>   https://bugs.webkit.org/show_bug.cgi?id=111952

> which states that the duration parameter is ignored if the buffer is in loop
> mode.

FWIW the spec is quite clear here: <https://bugs.webkit.org/show_bug.cgi?id=111952#c2>  It's just that WebKit doesn't implement what the spec requires correctly.

> My feeling is that the syntactic-sugar interpretation (duration is
> equivalent stop/start delta) is the cleanest, most obvious behavior and was
> probably the original intention of the parameter, however objectionable the
> sugar flavoring may be.

Really there is no good way to reconcile the duration argument with the loopStart and loopEnd attributes, as they're multiple ways of specifying the same thing...
Comment 2 Chris Wilson 2013-08-19 17:17:19 UTC
Actually, I don't think the spec *is* very clear here, reconciling loopStart/loopEnd with the duration parameter.

As I see it, loopStart/loopEnd are very oddly specified; the default values (0 and 0) would appear to loop a zero-length portion at the beginning of the buffer.  Oddly, you can set loopEnd without loopStart, but you can't set loopStart without setting loopEnd (despite "zero" seeming like a magic "length of buffer" value).  I think this is a bug, also.

In general, the duration parameter is a useful shorthand, and I don't like parameters that magically take no effect in "some" cases.  I think it is quite possible to reconcile loopStart/loopEnd with the duration parameter, but you're right that it is not reconciled today - in the addition of loopStart/loopEnd, duration seems to have been forgotten for looping cases. 

Perhaps "if duration is specified, this will set actualLoopEnd=max(buffer.length,loopStart+duration)"? (In spirit, at least - haven't worked out the whole section rewording.)
Comment 3 Joe Berkovitz / NF 2013-08-19 18:05:42 UTC
The spec language for "duration" reads:

"The duration parameter describes the duration of the portion (in seconds) to be played. If this parameter is not passed, the duration will be equal to the total duration of the AudioBuffer minus the offset parameter. Thus if neither offset nor duration are specified then the implied duration is the total duration of the AudioBuffer."

Looping is not mentioned anywhere in this paragraph, and "total duration of the AudioBuffer" is a fuzzy concept that might or might not take account of looping.

I do not think that a duration argument should ever force looping to take place. It still seems simplest to me to construe duration as an implicit call to stop(), otherwise we have two competing definitions of "duration", one of which has to have an ugly, fiddly definition with respect to looping.

@ChrisW, the loopStart=loopEnd=0 default does not normally cause any looping, because by default the "loop" attribute of a buffer is false. ChrisR and I had a longish conversation about this and the intent here was to preserve a legacy behavior from before loopStart/loopEnd existed, where simply setting loop=true on a buffer would loop the whole buffer. He wanted the default values in this case to be zero for both loopStart and loopEnd which seemed reasonable.
Comment 4 Chris Wilson 2013-08-19 19:23:15 UTC
(In reply to comment #3)
> Looping is not mentioned anywhere in this paragraph, and "total duration of
> the AudioBuffer" is a fuzzy concept that might or might not take account of
> looping.

Quite true.

> I do not think that a duration argument should ever force looping to take
> place. 

No, I agree.

> It still seems simplest to me to construe duration as an implicit
> call to stop(), otherwise we have two competing definitions of "duration",
> one of which has to have an ugly, fiddly definition with respect to looping.

In other words, "duration" refers to the length of time that the source will be sounding, whether looping or not.  I would agree with that behavior, although I'd point out we'll need to change the definition when omitted (duration cannot be the total duration of the AudioBuffer when omitted *while looping*- it should infinite).

I *think* Ehsan is suggesting that duration should be completely ignored when looping.  I'm less happy with that - I think we either should support duration as a looping shortcut (as Webkit and Blink currently do), or we should use it as an implicit stop() shortcut.

> @ChrisW, the loopStart=loopEnd=0 default does not normally cause any
> looping, because by default the "loop" attribute of a buffer is false.

I know that - I meant "if loop=true, it looks like a zero-length loop segment by default".  I wasn't clear.  :)

> ChrisR and I had a longish conversation about this and the intent here was
> to preserve a legacy behavior from before loopStart/loopEnd existed, where
> simply setting loop=true on a buffer would loop the whole buffer. He wanted
> the default values in this case to be zero for both loopStart and loopEnd
> which seemed reasonable.

Certainly, if you create a new buffersource and set loop=true, defaults should loop the whole buffer.  I'm okay with keeping those defaults (and the magic loopEnd=0 means buffer.length), but I think it's a bit weird that the algorithm defined means you can't set loopStart without setting loopEnd also.  I think that should be fixed.
Comment 5 Ehsan Akhgari [:ehsan] 2013-08-19 22:53:35 UTC
(In reply to comment #4)
> > It still seems simplest to me to construe duration as an implicit
> > call to stop(), otherwise we have two competing definitions of "duration",
> > one of which has to have an ugly, fiddly definition with respect to looping.
> 
> In other words, "duration" refers to the length of time that the source will
> be sounding, whether looping or not.  I would agree with that behavior,
> although I'd point out we'll need to change the definition when omitted
> (duration cannot be the total duration of the AudioBuffer when omitted
> *while looping*- it should infinite).

That would not make sense in the looping case because "If this parameter is not passed, the duration will be equal to the total duration of the AudioBuffer minus the offset parameter.".

> I *think* Ehsan is suggesting that duration should be completely ignored
> when looping.  I'm less happy with that - I think we either should support
> duration as a looping shortcut (as Webkit and Blink currently do), or we
> should use it as an implicit stop() shortcut.

I'm not suggesting that per se, all I'm saying is that is what the spec has required for now.  It's just sad that WebKit/Blink were never fixed according to that.  The prose under 4.10.3 is quite clear on what should happen in the case of looping, and since duration is never mentioned there, it should be ignored.  This is what Gecko implements.

If we were to write the spec today, we should have just dropped the duration argument altogether, as it is redundant.  Also I'd have dropped loopStart/loopEnd and added loopDuration or something, forcing loops to always start at the beginning of the buffer.

> > @ChrisW, the loopStart=loopEnd=0 default does not normally cause any
> > looping, because by default the "loop" attribute of a buffer is false.
> 
> I know that - I meant "if loop=true, it looks like a zero-length loop
> segment by default".  I wasn't clear.  :)
> 
> > ChrisR and I had a longish conversation about this and the intent here was
> > to preserve a legacy behavior from before loopStart/loopEnd existed, where
> > simply setting loop=true on a buffer would loop the whole buffer. He wanted
> > the default values in this case to be zero for both loopStart and loopEnd
> > which seemed reasonable.
> 
> Certainly, if you create a new buffersource and set loop=true, defaults
> should loop the whole buffer.  I'm okay with keeping those defaults (and the
> magic loopEnd=0 means buffer.length), but I think it's a bit weird that the
> algorithm defined means you can't set loopStart without setting loopEnd
> also.  I think that should be fixed.

Do you have any proposals on how to do that while not breaking compatibility with existing content?
Comment 6 Chris Wilson 2013-08-19 23:23:13 UTC
(In reply to comment #5)
> > In other words, "duration" refers to the length of time that the source will
> > be sounding, whether looping or not.  I would agree with that behavior,
> > although I'd point out we'll need to change the definition when omitted
> > (duration cannot be the total duration of the AudioBuffer when omitted
> > *while looping*- it should infinite).
> 
> That would not make sense in the looping case because "If this parameter is
> not passed, the duration will be equal to the total duration of the
> AudioBuffer minus the offset parameter.".

No; in the looping case, the *sound duration* is infinite (i.e. until stop() is called to schedule an end).

I think we're getting confused with the term "duration" because there are two different meanings:  
    1) "duration of playing some sound" - i.e. from start() to stop() schedule points, and  
    2) "loop duration" - the time between the loop begin point and the loop end point.

Right now, Webkit/Blink is treating the duration parameter as #1 when not looping, and as #2 when looping.  Firefox is treating the duration parameter as #1 when not looping, and ignoring it entirely when looping.  (True, Ehsan?)
 
> > I *think* Ehsan is suggesting that duration should be completely ignored
> > when looping.  I'm less happy with that - I think we either should support
> > duration as a looping shortcut (as Webkit and Blink currently do), or we
> > should use it as an implicit stop() shortcut.
> 
> I'm not suggesting that per se, all I'm saying is that is what the spec has
> required for now.  

I don't agree that that is true;  the spec does not say "duration only applies when looping," it just doesn't clearly describe how it interacts with loopStart/loopEnd.
 
> It's just sad that WebKit/Blink were never fixed
> according to that.  The prose under 4.10.3 is quite clear on what should
> happen in the case of looping, and since duration is never mentioned there,
> it should be ignored.  This is what Gecko implements.

The only thing 4.10.3 is clear about is where the loop begin and end points are set in the looping pattern, not how long you continue to play; that is set by stop(), or by the implicit stop() that Joe was referring to that is implied in the non-looping case by the duration parameter.

One could make a case that the Webkit/Blink's behavior is correct - that the duration refers to the loopEnd - though this is not clearly specified, and I think a main argument for it (aside from "that's what Webkit and Blink do already") is that we didn't used to have loopStart/loopEnd, and this was how you set up a loop.  That's (obviously) not a super-strong argument.

I think it makes more sense to consider (and change the spec to clarify, and change current implementations including Blink to reflect) that duration sets up an implicit stop() call, and interacts with looping in the obvious way.  

I think saying the duration parameter is ignored when looping is less intuitive than either of the aforementioned options.

> If we were to write the spec today, we should have just dropped the duration
> argument altogether, as it is redundant.  Also I'd have dropped
> loopStart/loopEnd and added loopDuration or something, forcing loops to
> always start at the beginning of the buffer.

Well, it used to work like that (no loop start), and that's EXACTLY why Webkit/Blink work the way they do.  QED.  :)

However, having an "attack segment" is a very common feature, it's nice to have, and "duration" doesn't need to be redundant; it can be a shortcut for calling stop().

> > Certainly, if you create a new buffersource and set loop=true, defaults
> > should loop the whole buffer.  I'm okay with keeping those defaults (and the
> > magic loopEnd=0 means buffer.length), but I think it's a bit weird that the
> > algorithm defined means you can't set loopStart without setting loopEnd
> > also.  I think that should be fixed.
> 
> Do you have any proposals on how to do that while not breaking compatibility
> with existing content?

I have little compatibility concern about making loops (which are relatively recent, and moderately esoteric) start actually working when loopStart is set but loopEnd is not.  I expect the occurrence of this in the wild is zero, to be frank.
Comment 7 Joe Berkovitz / NF 2013-08-20 11:57:00 UTC
(In reply to comment #6)
> One could make a case that the Webkit/Blink's behavior is correct - that the
> duration refers to the loopEnd - though this is not clearly specified, and I
> think a main argument for it (aside from "that's what Webkit and Blink do
> already") is that we didn't used to have loopStart/loopEnd, and this was how
> you set up a loop.  That's (obviously) not a super-strong argument.
> 
> I think it makes more sense to consider (and change the spec to clarify, and
> change current implementations including Blink to reflect) that duration
> sets up an implicit stop() call, and interacts with looping in the obvious
> way.  

For my part, I feel that Webkit/Blink's behavior (which I was unaware of and have never tested) seems like an over-eager interpretation of "duration".

> > If we were to write the spec today, we should have just dropped the duration
> > argument altogether, as it is redundant.  Also I'd have dropped
> > loopStart/loopEnd and added loopDuration or something, forcing loops to
> > always start at the beginning of the buffer.
> 
> Well, it used to work like that (no loop start), and that's EXACTLY why
> Webkit/Blink work the way they do.  QED.  :)
> 
> However, having an "attack segment" is a very common feature, it's nice to
> have, and "duration" doesn't need to be redundant; it can be a shortcut for
> calling stop().

A loop start point is much more than "nice to have". It is the normal way that modern instrument sample libraries are constituted. Without it, one has to hand-stitch loops together in an arbitrary-length AudioBuffer.

This need is cited in the Use Cases document under "2.5 Music Creation Environment with Sampled Instruments".

> > Do you have any proposals on how to do that while not breaking compatibility
> > with existing content?
> 
> I have little compatibility concern about making loops (which are relatively
> recent, and moderately esoteric) start actually working when loopStart is
> set but loopEnd is not.  I expect the occurrence of this in the wild is
> zero, to be frank.

I share Chris's lack of concern for compatibility here, since a loopEnd == 0 will not produce any looping at all in today's API releases. I propose that loopEnd == 0 have the semantics of indicating that the loop ends at the buffer end. This removes the problem of needing to set both loopStart and loopEnd, if that is a problem.
Comment 8 Ehsan Akhgari [:ehsan] 2013-08-28 17:57:45 UTC
(In reply to comment #6)
> (In reply to comment #5)
> > > In other words, "duration" refers to the length of time that the source will
> > > be sounding, whether looping or not.  I would agree with that behavior,
> > > although I'd point out we'll need to change the definition when omitted
> > > (duration cannot be the total duration of the AudioBuffer when omitted
> > > *while looping*- it should infinite).
> > 
> > That would not make sense in the looping case because "If this parameter is
> > not passed, the duration will be equal to the total duration of the
> > AudioBuffer minus the offset parameter.".
> 
> No; in the looping case, the *sound duration* is infinite (i.e. until stop()
> is called to schedule an end).
> 
> I think we're getting confused with the term "duration" because there are
> two different meanings:  
>     1) "duration of playing some sound" - i.e. from start() to stop()
> schedule points, and  
>     2) "loop duration" - the time between the loop begin point and the loop
> end point.

Yeah, we're mixing up the terminology indeed!  Thanks for the clarification.

> Right now, Webkit/Blink is treating the duration parameter as #1 when not
> looping, and as #2 when looping.  Firefox is treating the duration parameter
> as #1 when not looping, and ignoring it entirely when looping.  (True,
> Ehsan?)

Yes, that's what Gecko does.  The reason that the WebKit/Blink interpretation is wrong IMO is that #2 already _has_ a precise definition, loopEnd-loopStart.

> > > I *think* Ehsan is suggesting that duration should be completely ignored
> > > when looping.  I'm less happy with that - I think we either should support
> > > duration as a looping shortcut (as Webkit and Blink currently do), or we
> > > should use it as an implicit stop() shortcut.
> > 
> > I'm not suggesting that per se, all I'm saying is that is what the spec has
> > required for now.  
> 
> I don't agree that that is true;  the spec does not say "duration only
> applies when looping," it just doesn't clearly describe how it interacts
> with loopStart/loopEnd.

OK, I think I see the source of our disagreement now.  When reading the spec, since #2 is precisely defined, I interpret the duration argument to only reflect as #1.  If I'm understanding your viewpoint correctly (and please correct me if I'm wrong), you're trying to reconcile #2 with loopEnd-loopStart.  My contention is that those two are impossible to reconcile, since they can be different values, and the implementation has to pick one or the other.  Given the prose for section 4.10.3, I believe the spec has clearly specified what the loop duration is.

> > It's just sad that WebKit/Blink were never fixed
> > according to that.  The prose under 4.10.3 is quite clear on what should
> > happen in the case of looping, and since duration is never mentioned there,
> > it should be ignored.  This is what Gecko implements.
> 
> The only thing 4.10.3 is clear about is where the loop begin and end points
> are set in the looping pattern, not how long you continue to play; that is
> set by stop(), or by the implicit stop() that Joe was referring to that is
> implied in the non-looping case by the duration parameter.

Yes, I agree.  The duration in the sense of #1 in the looping case is only determined by when stop() is called.

> One could make a case that the Webkit/Blink's behavior is correct - that the
> duration refers to the loopEnd - though this is not clearly specified, and I
> think a main argument for it (aside from "that's what Webkit and Blink do
> already") is that we didn't used to have loopStart/loopEnd, and this was how
> you set up a loop.  That's (obviously) not a super-strong argument.

Agreed.  :-)

One thing to note here is that I actually have very little interest on which implementation's behavior we end up adopting.  I just think that the WebKit/Blink implementation doesn't make sense as it convolutes the notion of loop duration (which is defined as loopEnd-loopStart with the duration argument passed to start()), and I don't think you can reconcile those two.

> I think it makes more sense to consider (and change the spec to clarify, and
> change current implementations including Blink to reflect) that duration
> sets up an implicit stop() call, and interacts with looping in the obvious
> way.  

You mean both in the looping and non-looping case?

> I think saying the duration parameter is ignored when looping is less
> intuitive than either of the aforementioned options.

Well, the reason I don't think that is a good idea is that in the non-looping case, the duration argument can't (shouldn't?) be longer than the length of the buffer - offset.  However, that clearly makes little sense in the looping case.  Also, note that when processing a start() call, the implementation has no idea whether it's going to be looping or not (yikes!)

> > If we were to write the spec today, we should have just dropped the duration
> > argument altogether, as it is redundant.  Also I'd have dropped
> > loopStart/loopEnd and added loopDuration or something, forcing loops to
> > always start at the beginning of the buffer.
> 
> Well, it used to work like that (no loop start), and that's EXACTLY why
> Webkit/Blink work the way they do.  QED.  :)
> 
> However, having an "attack segment" is a very common feature, it's nice to
> have, and "duration" doesn't need to be redundant; it can be a shortcut for
> calling stop().

See above.  :-)

> > > Certainly, if you create a new buffersource and set loop=true, defaults
> > > should loop the whole buffer.  I'm okay with keeping those defaults (and the
> > > magic loopEnd=0 means buffer.length), but I think it's a bit weird that the
> > > algorithm defined means you can't set loopStart without setting loopEnd
> > > also.  I think that should be fixed.
> > 
> > Do you have any proposals on how to do that while not breaking compatibility
> > with existing content?
> 
> I have little compatibility concern about making loops (which are relatively
> recent, and moderately esoteric) start actually working when loopStart is
> set but loopEnd is not.  I expect the occurrence of this in the wild is
> zero, to be frank.

That's great!  At least it gives us the altitude to fix things.  Now only if we knew what the right fix was!  ;-)
Comment 9 Chris Wilson 2013-08-28 18:33:12 UTC
(In reply to comment #8)
> Yes, that's what Gecko does.  The reason that the WebKit/Blink
> interpretation is wrong IMO is that #2 already _has_ a precise definition,
> loopEnd-loopStart.

See above, "weak argument".  :)  I'm not suggesting we should keep that.

> OK, I think I see the source of our disagreement now.  When reading the
> spec, since #2 is precisely defined, I interpret the duration argument to
> only reflect as #1.  If I'm understanding your viewpoint correctly (and
> please correct me if I'm wrong), you're trying to reconcile #2 with
> loopEnd-loopStart.  My contention is that those two are impossible to
> reconcile, since they can be different values, and the implementation has to
> pick one or the other.  Given the prose for section 4.10.3, I believe the
> spec has clearly specified what the loop duration is.

Not really; I'm not arguing that loop duration should be affected by the 'duration' parameter - just explaining that originally, that was how loop duration was, in fact, specified, which is why it works that way in Blink/Webkit today.

> One thing to note here is that I actually have very little interest on which
> implementation's behavior we end up adopting.  I just think that the
> WebKit/Blink implementation doesn't make sense as it convolutes the notion
> of loop duration (which is defined as loopEnd-loopStart with the duration
> argument passed to start()), and I don't think you can reconcile those two.

Yeah, I think it makes sense to not conflate loop duration and duration.

> > I think it makes more sense to consider (and change the spec to clarify, and
> > change current implementations including Blink to reflect) that duration
> > sets up an implicit stop() call, and interacts with looping in the obvious
> > way.  
> 
> You mean both in the looping and non-looping case?

Yes.  In either case, I think duration sets the length of time that the buffer source will play.

> > I think saying the duration parameter is ignored when looping is less
> > intuitive than either of the aforementioned options.
> 
> Well, the reason I don't think that is a good idea is that in the
> non-looping case, the duration argument can't (shouldn't?) be longer than
> the length of the buffer - offset.  However, that clearly makes little sense
> in the looping case.  Also, note that when processing a start() call, the
> implementation has no idea whether it's going to be looping or not (yikes!)

The duration certainly CAN be longer than the buffer (length-offset) in the non-looping case - it will just be silent after it runs out of bits.  The loop start/end in the looping case let it play longer than that.

> > > > Certainly, if you create a new buffersource and set loop=true, defaults
> > > > should loop the whole buffer.  I'm okay with keeping those defaults (and the
> > > > magic loopEnd=0 means buffer.length), but I think it's a bit weird that the
> > > > algorithm defined means you can't set loopStart without setting loopEnd
> > > > also.  I think that should be fixed.
> > > 
> > > Do you have any proposals on how to do that while not breaking compatibility
> > > with existing content?
> > 
> > I have little compatibility concern about making loops (which are relatively
> > recent, and moderately esoteric) start actually working when loopStart is
> > set but loopEnd is not.  I expect the occurrence of this in the wild is
> > zero, to be frank.
> 
> That's great!  At least it gives us the altitude to fix things.  Now only if
> we knew what the right fix was!  ;-)

Note there are two separate issues here -

A) what "duration" means (if anything) in the looping case, and 

B) the current definition of loopStart/loopEnd states if loopStart is set, but loopEnd is not, the loopStart is ignored.  I think that's goofy, and should be changed.
Comment 10 Ehsan Akhgari [:ehsan] 2013-09-04 15:23:48 UTC
(In reply to comment #9)
> > OK, I think I see the source of our disagreement now.  When reading the
> > spec, since #2 is precisely defined, I interpret the duration argument to
> > only reflect as #1.  If I'm understanding your viewpoint correctly (and
> > please correct me if I'm wrong), you're trying to reconcile #2 with
> > loopEnd-loopStart.  My contention is that those two are impossible to
> > reconcile, since they can be different values, and the implementation has to
> > pick one or the other.  Given the prose for section 4.10.3, I believe the
> > spec has clearly specified what the loop duration is.
> 
> Not really; I'm not arguing that loop duration should be affected by the
> 'duration' parameter - just explaining that originally, that was how loop
> duration was, in fact, specified, which is why it works that way in
> Blink/Webkit today.

OK, so I'll take this as a historical note then.  :-)

> > One thing to note here is that I actually have very little interest on which
> > implementation's behavior we end up adopting.  I just think that the
> > WebKit/Blink implementation doesn't make sense as it convolutes the notion
> > of loop duration (which is defined as loopEnd-loopStart with the duration
> > argument passed to start()), and I don't think you can reconcile those two.
> 
> Yeah, I think it makes sense to not conflate loop duration and duration.

Great!

> > > I think it makes more sense to consider (and change the spec to clarify, and
> > > change current implementations including Blink to reflect) that duration
> > > sets up an implicit stop() call, and interacts with looping in the obvious
> > > way.  
> > 
> > You mean both in the looping and non-looping case?
> 
> Yes.  In either case, I think duration sets the length of time that the
> buffer source will play.

OK, I am not sure if this makes sense at all.  Currently, the spec defines duration as following:

"The duration parameter describes the duration of the portion (in seconds) to be played."

This means that durations longer than the length of the buffer are invalid.  Now, with your definition, they _should_ be valid in the looping case.  The problem is that by the time that the author calls start(), we *don't* know whether we're in the looping case or not, so the implementation will be unable to do the proper check there.  In fact, in a sense we *never* know whether we're looping or not as the web page can keep on toggling the loop attribute on the main thread as the buffer is being played back.  So, I don't think that any proposal which assumes different meanings for the duration argument in the looping/non-looping case is possible to implement.

> > > I think saying the duration parameter is ignored when looping is less
> > > intuitive than either of the aforementioned options.
> > 
> > Well, the reason I don't think that is a good idea is that in the
> > non-looping case, the duration argument can't (shouldn't?) be longer than
> > the length of the buffer - offset.  However, that clearly makes little sense
> > in the looping case.  Also, note that when processing a start() call, the
> > implementation has no idea whether it's going to be looping or not (yikes!)
> 
> The duration certainly CAN be longer than the buffer (length-offset) in the
> non-looping case - it will just be silent after it runs out of bits.  The
> loop start/end in the looping case let it play longer than that.

Consider what should happen when you have a buffer of 1s, and pass in 2s as the duration with loop==false, then call start() and in about 1.5s, you set loop=true.  Should the implementation play the buffer for the first second, then produce silence for half a second, and then jump back somewhere in the middle of the buffer and start playing back?  That sound insane to me, and I'd be quite strongly opposed to that.

> > > > > Certainly, if you create a new buffersource and set loop=true, defaults
> > > > > should loop the whole buffer.  I'm okay with keeping those defaults (and the
> > > > > magic loopEnd=0 means buffer.length), but I think it's a bit weird that the
> > > > > algorithm defined means you can't set loopStart without setting loopEnd
> > > > > also.  I think that should be fixed.
> > > > 
> > > > Do you have any proposals on how to do that while not breaking compatibility
> > > > with existing content?
> > > 
> > > I have little compatibility concern about making loops (which are relatively
> > > recent, and moderately esoteric) start actually working when loopStart is
> > > set but loopEnd is not.  I expect the occurrence of this in the wild is
> > > zero, to be frank.
> > 
> > That's great!  At least it gives us the altitude to fix things.  Now only if
> > we knew what the right fix was!  ;-)
> 
> Note there are two separate issues here -
> 
> A) what "duration" means (if anything) in the looping case, and 

My proposal is that it should be completely ignored in the looping case.  Ideally we should rename it to nonLoopingPortion to clear up all confusions. ;-)

> B) the current definition of loopStart/loopEnd states if loopStart is set,
> but loopEnd is not, the loopStart is ignored.  I think that's goofy, and
> should be changed.

I actually think it makes sense to not accept the cases where loopStart>loopEnd, including the case you mentioned above.  Why do you think that's goofy?
Comment 11 Joe Berkovitz / NF 2013-09-04 16:19:57 UTC
(In reply to comment #10)
> > Yes.  In either case, I think duration sets the length of time that the
> > buffer source will play.
> 
> OK, I am not sure if this makes sense at all.  Currently, the spec defines
> duration as following:
> 
> "The duration parameter describes the duration of the portion (in seconds)
> to be played."
> 
> This means that durations longer than the length of the buffer are invalid. 
> Now, with your definition, they _should_ be valid in the looping case.  The
> problem is that by the time that the author calls start(), we *don't* know
> whether we're in the looping case or not, so the implementation will be
> unable to do the proper check there.  In fact, in a sense we *never* know
> whether we're looping or not as the web page can keep on toggling the loop
> attribute on the main thread as the buffer is being played back.  So, I
> don't think that any proposal which assumes different meanings for the
> duration argument in the looping/non-looping case is possible to implement.

Let's break this into two issues, which I think are somewhat independent.

1. There is a question of whether "the length of time the buffer source will play" makes sense as a definition for duration. I think that this does make sense on its face, because for any length of time in a single call to start(startTime, ..., duration) the user can make an equivalent pair of calls to start(startTime, ...) followed by stop(startTime + duration). This is a completely reasonable definition of "length of time the source will play" and introduces no new concepts beyond those that predated the introduction of the optional duration argument, namely start time and stop time.

2. Ehsan raises a good point about the effect of intervening changes to looping properties of the buffer prior to its playback. This question also exists for start/stop calls *without* duration and I propose that it should be logged as a separate API issue that belongs to the overall data-race family of questions. For that matter, the entire "buffer" property of the node could be changed on the fly, couldn't it?  My belief is that such on-the-fly changes are just as unsafe and unpredictable as changing the sample frames inside a buffer whose other attributes remain stable.
Comment 12 Chris Wilson 2013-09-04 16:56:07 UTC
(In reply to comment #10)
> > Not really; I'm not arguing that loop duration should be affected by the
> > 'duration' parameter - just explaining that originally, that was how loop
> > duration was, in fact, specified, which is why it works that way in
> > Blink/Webkit today.
> 
> OK, so I'll take this as a historical note then.  :-)

Yep, that was the point of the statement.

> > Yes.  In either case, I think duration sets the length of time that the
> > buffer source will play.
> 
> OK, I am not sure if this makes sense at all.  Currently, the spec defines
> duration as following:
> 
> "The duration parameter describes the duration of the portion (in seconds)
> to be played."

I think that wording should be improved.  It sounds to me like it hasn't been touched since prior to the loopStart/loopEnd addition.

> This means that durations longer than the length of the buffer are invalid. 

Invalid?   No - they just issue silence once the buffer has been completely iterated over.  At least, to my knowledge this isn't a throw-an-exception case.

> Now, with your definition, they _should_ be valid in the looping case.  The
> problem is that by the time that the author calls start(), we *don't* know
> whether we're in the looping case or not, so the implementation will be
> unable to do the proper check there.  In fact, in a sense we *never* know
> whether we're looping or not as the web page can keep on toggling the loop
> attribute on the main thread as the buffer is being played back.  So, I
> don't think that any proposal which assumes different meanings for the
> duration argument in the looping/non-looping case is possible to implement.

I think independently of this, the "loop" parameter should have a tighter definition of when it can be changed.  (e.g. when "the buffer is acquired" sounds like a good time to copy that parameter too.)

> > The duration certainly CAN be longer than the buffer (length-offset) in the
> > non-looping case - it will just be silent after it runs out of bits.  The
> > loop start/end in the looping case let it play longer than that.
> 
> Consider what should happen when you have a buffer of 1s, and pass in 2s as
> the duration with loop==false, then call start() and in about 1.5s, you set
> loop=true.  Should the implementation play the buffer for the first second,
> then produce silence for half a second, and then jump back somewhere in the
> middle of the buffer and start playing back?  That sound insane to me, and
> I'd be quite strongly opposed to that.

I would expect the onended would have fired and the memory potentially reclaimed already, so no, I wouldn't expect that.

> > A) what "duration" means (if anything) in the looping case, and 
> 
> My proposal is that it should be completely ignored in the looping case. 
> Ideally we should rename it to nonLoopingPortion to clear up all confusions.

Or rename it "soundPlayingDuration" and make it work as I suggested.  :)

> > B) the current definition of loopStart/loopEnd states if loopStart is set,
> > but loopEnd is not, the loopStart is ignored.  I think that's goofy, and
> > should be changed.
> 
> I actually think it makes sense to not accept the cases where
> loopStart>loopEnd, including the case you mentioned above.  Why do you think
> that's goofy?

Because it makes little to no sense to have a looping buffer that has data AFTER the loopEnd point; if you're looping, and there is an attack section, you would need to set the loopStart, but by default the loopEnd should just be the end of the buffer (because there's no way the looping algorithm is going to get to any buffer data after the loopEnd anyway).  However, that case explicitly doesn't work - you explicitly have to set the bufferEnd to the end of the data.  That's goofy.

Obviously, setting loopStart > loopEnd where loopEnd != the default is fairly nonsensical.
Comment 13 Ehsan Akhgari [:ehsan] 2013-09-04 22:38:11 UTC
(In reply to comment #12)
> > > Yes.  In either case, I think duration sets the length of time that the
> > > buffer source will play.
> > 
> > OK, I am not sure if this makes sense at all.  Currently, the spec defines
> > duration as following:
> > 
> > "The duration parameter describes the duration of the portion (in seconds)
> > to be played."
> 
> I think that wording should be improved.  It sounds to me like it hasn't
> been touched since prior to the loopStart/loopEnd addition.

Sure, when we settle on what the definition should be.  ;-)

> > This means that durations longer than the length of the buffer are invalid. 
> 
> Invalid?   No - they just issue silence once the buffer has been completely
> iterated over.  At least, to my knowledge this isn't a throw-an-exception
> case.

No, I didn't mean that we should throw an exception in that case, I was talking about ignoring such duration arguments, or, IOW, clamping duration down to the length of the buffer - offset.

> > Now, with your definition, they _should_ be valid in the looping case.  The
> > problem is that by the time that the author calls start(), we *don't* know
> > whether we're in the looping case or not, so the implementation will be
> > unable to do the proper check there.  In fact, in a sense we *never* know
> > whether we're looping or not as the web page can keep on toggling the loop
> > attribute on the main thread as the buffer is being played back.  So, I
> > don't think that any proposal which assumes different meanings for the
> > duration argument in the looping/non-looping case is possible to implement.
> 
> I think independently of this, the "loop" parameter should have a tighter
> definition of when it can be changed.  (e.g. when "the buffer is acquired"
> sounds like a good time to copy that parameter too.)

Maybe, but for now let's work under the assumption that it can be changed at will.

> > > The duration certainly CAN be longer than the buffer (length-offset) in the
> > > non-looping case - it will just be silent after it runs out of bits.  The
> > > loop start/end in the looping case let it play longer than that.
> > 
> > Consider what should happen when you have a buffer of 1s, and pass in 2s as
> > the duration with loop==false, then call start() and in about 1.5s, you set
> > loop=true.  Should the implementation play the buffer for the first second,
> > then produce silence for half a second, and then jump back somewhere in the
> > middle of the buffer and start playing back?  That sound insane to me, and
> > I'd be quite strongly opposed to that.
> 
> I would expect the onended would have fired and the memory potentially
> reclaimed already, so no, I wouldn't expect that.

This directly contradicts with your earlier suggestion:

"The duration certainly CAN be longer than the buffer (length-offset) in the non-looping case - it will just be silent after it runs out of bits."

You should pick one behavior or the other, as they're contradictory.

> > > A) what "duration" means (if anything) in the looping case, and 
> > 
> > My proposal is that it should be completely ignored in the looping case. 
> > Ideally we should rename it to nonLoopingPortion to clear up all confusions.
> 
> Or rename it "soundPlayingDuration" and make it work as I suggested.  :)

And how would we solve the point above?

> > > B) the current definition of loopStart/loopEnd states if loopStart is set,
> > > but loopEnd is not, the loopStart is ignored.  I think that's goofy, and
> > > should be changed.
> > 
> > I actually think it makes sense to not accept the cases where
> > loopStart>loopEnd, including the case you mentioned above.  Why do you think
> > that's goofy?
> 
> Because it makes little to no sense to have a looping buffer that has data
> AFTER the loopEnd point; if you're looping, and there is an attack section,
> you would need to set the loopStart, but by default the loopEnd should just
> be the end of the buffer (because there's no way the looping algorithm is
> going to get to any buffer data after the loopEnd anyway).  However, that
> case explicitly doesn't work - you explicitly have to set the bufferEnd to
> the end of the data.  That's goofy.

Well, loopStart and loopEnd are both attributes on the object, what should the following code observe?

console.log(node.loopEnd); // 0
node.loopStart = 10;
console.log(node.loopEnd); // ???

It is very weird to have assinging to loopStart suddenly change loopEnd.

Your suggestion would be OK if these were just internal state, but unfortunately they're not.  (And yes, this is bad API design, a better API would be something like: |void loop(double loopStart, optional double loopEnd);| and then we wouldn't have this problem. :(
Comment 14 Ehsan Akhgari [:ehsan] 2013-09-04 22:42:19 UTC
(In reply to comment #11)
> (In reply to comment #10)
> > > Yes.  In either case, I think duration sets the length of time that the
> > > buffer source will play.
> > 
> > OK, I am not sure if this makes sense at all.  Currently, the spec defines
> > duration as following:
> > 
> > "The duration parameter describes the duration of the portion (in seconds)
> > to be played."
> > 
> > This means that durations longer than the length of the buffer are invalid. 
> > Now, with your definition, they _should_ be valid in the looping case.  The
> > problem is that by the time that the author calls start(), we *don't* know
> > whether we're in the looping case or not, so the implementation will be
> > unable to do the proper check there.  In fact, in a sense we *never* know
> > whether we're looping or not as the web page can keep on toggling the loop
> > attribute on the main thread as the buffer is being played back.  So, I
> > don't think that any proposal which assumes different meanings for the
> > duration argument in the looping/non-looping case is possible to implement.
> 
> Let's break this into two issues, which I think are somewhat independent.
> 
> 1. There is a question of whether "the length of time the buffer source will
> play" makes sense as a definition for duration. I think that this does make
> sense on its face, because for any length of time in a single call to
> start(startTime, ..., duration) the user can make an equivalent pair of
> calls to start(startTime, ...) followed by stop(startTime + duration). This
> is a completely reasonable definition of "length of time the source will
> play" and introduces no new concepts beyond those that predated the
> introduction of the optional duration argument, namely start time and stop
> time.

No, this is not as easy as it seems, since then you need to solve the problem of whether this should apply to the looping case or not, and then you'd hit the problem that I've been trying to explain.  I think the only self-consistent proposal is to ignore the duration parameter in the looping case.  I hope I've constructed an acceptable argument in favor of it.  And that means that the stop() analogy will no longer hold.

> 2. Ehsan raises a good point about the effect of intervening changes to
> looping properties of the buffer prior to its playback. This question also
> exists for start/stop calls *without* duration and I propose that it should
> be logged as a separate API issue that belongs to the overall data-race
> family of questions. For that matter, the entire "buffer" property of the
> node could be changed on the fly, couldn't it?  My belief is that such
> on-the-fly changes are just as unsafe and unpredictable as changing the
> sample frames inside a buffer whose other attributes remain stable.

They're not unsafe at all, they're unpredictable, but Web Audio as a whole has that property pretty much everywhere, since it allows changing the structure of the graph as playback is in progress.  I think it's futile to try to fix that in this one case.
Comment 15 Chris Wilson 2013-09-04 22:57:25 UTC
(In reply to comment #13)
> > > Consider what should happen when you have a buffer of 1s, and pass in 2s as
> > > the duration with loop==false, then call start() and in about 1.5s, you set
> > > loop=true.  Should the implementation play the buffer for the first second,
> > > then produce silence for half a second, and then jump back somewhere in the
> > > middle of the buffer and start playing back?  That sound insane to me, and
> > > I'd be quite strongly opposed to that.
> > 
> > I would expect the onended would have fired and the memory potentially
> > reclaimed already, so no, I wouldn't expect that.
> 
> This directly contradicts with your earlier suggestion:
> 
> "The duration certainly CAN be longer than the buffer (length-offset) in the
> non-looping case - it will just be silent after it runs out of bits."
> 
> You should pick one behavior or the other, as they're contradictory.

No, I don't believe that they are, as I didn't mean that latter literally; I meant it in response to your implicit suggestion this was an error (invalid).  I see hitting the end of the buffer and the end of playback, according to the current settings:
1) single-shot playback, bufferlength is reached, or 
2) single-shot playback, duration is reached

to cause the buffersource to no longer be active, and as per design constraints, it cannot be "revived".  In old parlance, it's FINISHED_STATE.  It's done.  I would likely expect onEnded to fire, for example, not after duration, but after playback is done.

> Well, loopStart and loopEnd are both attributes on the object, what should
> the following code observe?
> 
> console.log(node.loopEnd); // 0
> node.loopStart = 10;
> console.log(node.loopEnd); // ???
> 
> It is very weird to have assinging to loopStart suddenly change loopEnd.
> 
> Your suggestion would be OK if these were just internal state, but
> unfortunately they're not.  (And yes, this is bad API design, a better API
> would be something like: |void loop(double loopStart, optional double
> loopEnd);| and then we wouldn't have this problem. :(

Huh?  I would expect the log above to issue
0
0
loopEnd doesn't get assigned by the assignment of loopStart - it's an input attribute, not a reflection of internal state.  Otherwise that first log shouldn't show zero either - it should show bufferLength, since if loopStart is unassigned, it will play through as if loopEnd were set to the length of the buffer (i.e, loop the entire buffer).  I would not, in short, expect assigning loopStart to change loopEnd.
Comment 16 Chris Wilson 2013-09-04 23:00:05 UTC
(In reply to comment #14)
> > 1. There is a question of whether "the length of time the buffer source will
> > play" makes sense as a definition for duration. I think that this does make
> > sense on its face, because for any length of time in a single call to
> > start(startTime, ..., duration) the user can make an equivalent pair of
> > calls to start(startTime, ...) followed by stop(startTime + duration). This
> > is a completely reasonable definition of "length of time the source will
> > play" and introduces no new concepts beyond those that predated the
> > introduction of the optional duration argument, namely start time and stop
> > time.
> 
> No, this is not as easy as it seems, since then you need to solve the
> problem of whether this should apply to the looping case or not, and then
> you'd hit the problem that I've been trying to explain.  I think the only
> self-consistent proposal is to ignore the duration parameter in the looping
> case.  I hope I've constructed an acceptable argument in favor of it.  And
> that means that the stop() analogy will no longer hold.

I don't understand why you're saying the stop() analogy does not hold, and I certainly disagree that duration as that analogy cannot be self-consistent.
Comment 17 Olivier Thereaux 2014-10-28 17:14:44 UTC
Web Audio API issues have been migrated to Github. 
See https://github.com/WebAudio/web-audio-api/issues
Comment 18 Olivier Thereaux 2014-10-28 17:17:41 UTC
Closing. See https://github.com/WebAudio/web-audio-api/issues for up to date list of issues for the Web Audio API.