Closed olivierthereaux closed 9 years ago
Original comment by Ehsan Akhgari [:ehsan] on W3C Bugzilla. Mon, 19 Aug 2013 16:07:20 GMT
(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...
Original comment by Chris Wilson on W3C Bugzilla. Mon, 19 Aug 2013 17:17:19 GMT
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.)
Original comment by Joe Berkovitz / NF on W3C Bugzilla. Mon, 19 Aug 2013 18:05:42 GMT
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.
Original comment by Chris Wilson on W3C Bugzilla. Mon, 19 Aug 2013 19:23:15 GMT
(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.
Original comment by Ehsan Akhgari [:ehsan] on W3C Bugzilla. Mon, 19 Aug 2013 22:53:35 GMT
(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?
Original comment by Chris Wilson on W3C Bugzilla. Mon, 19 Aug 2013 23:23:13 GMT
(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.
Original comment by Joe Berkovitz / NF on W3C Bugzilla. Tue, 20 Aug 2013 11:57:00 GMT
(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.
Original comment by Ehsan Akhgari [:ehsan] on W3C Bugzilla. Wed, 28 Aug 2013 17:57:45 GMT
(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! ;-)
Original comment by Chris Wilson on W3C Bugzilla. Wed, 28 Aug 2013 18:33:12 GMT
(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.
Original comment by Ehsan Akhgari [:ehsan] on W3C Bugzilla. Wed, 04 Sep 2013 15:23:48 GMT
(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?
Original comment by Joe Berkovitz / NF on W3C Bugzilla. Wed, 04 Sep 2013 16:19:57 GMT
(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.
Original comment by Chris Wilson on W3C Bugzilla. Wed, 04 Sep 2013 16:56:07 GMT
(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.
Original comment by Ehsan Akhgari [:ehsan] on W3C Bugzilla. Wed, 04 Sep 2013 22:38:11 GMT
(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. :(
Original comment by Ehsan Akhgari [:ehsan] on W3C Bugzilla. Wed, 04 Sep 2013 22:42:19 GMT
(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.
- 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.
- 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.
Original comment by Chris Wilson on W3C Bugzilla. Wed, 04 Sep 2013 22:57:25 GMT
(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.
Original comment by Chris Wilson on W3C Bugzilla. Wed, 04 Sep 2013 23:00:05 GMT
(In reply to comment #14)
- 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.
On Wed, 11 Sep 2013 07:28:07 -0700, Olivier Thereaux wrote:
Reported by Joe Berkovitz / NF
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);
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.
It would not be entirely sugar and would even be useful in the case when the when/startTime parameter to start() is zero.
Without this, it would only be possible to specify a precise length of playback when startTime is in the future. See [1].
If the duration parameter is provided, and is greater than the length of the buffer, then there are two options:
a) The ended event can be dispatched once duration has passed or earlier if time passes the |when| parameter to stop(). This point in time might be after some silence, if not looping, but would handle, in a not very useful way, by starting to loop, the case of setting loop to true after reaching the end of the buffer in non-looping playback.
The node would be a timer in the case where there is no buffer.
b) The ended event can be dispatched when the buffer is no longer being played back. It would be necessary to specify that the loop parameter has no effect after buffer playback reaches its end. Race conditions would mean that setting the loop parameter before receiving ended may still be ineffective, but I think we could tolerate that.
We'd need to change the spec wording where it says "portion of the buffer", and change the default value of the duration parameter when looping.
The default value of the duration parameter to start() could be infinite if |loop| is set before start is called.
If loop is not set before start() is called then there are two options when duration is not provided:
i) duration is set to "the total duration of the AudioBuffer minus the offset parameter", in which case setting |loop| to true after the start() call would usually be ineffective if no explicit duration parameter is provided. There is a risk that there may be existing content setting loop after calling start, for which behavior would change.
ii) duration has some magic value that means behavior depends on whether loop gets set or not. This is not an option if (a) is selected above, because we wouldn't know when to dispatch onended.
Karl.
[1] https://github.com/WebAudio/web-audio-api/issues/267#issuecomment-27026097
Duplicate of #421, which has a solution.
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.