-
Notifications
You must be signed in to change notification settings - Fork 172
Rework definition of ABSN output behavior for rate-adjusted duration param #1681
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…’s interaction with the start(…duration) argument.
|
LGTM, thank you. |
rtoy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you not check in index.html? People doing this generally cause merge issues in index.html that are hard to resolve.
Algorithm looks good and I think it does what we want. The text part confuses me, though.
index.bs
Outdated
| when: The <code>when</code> parameter describes at what time (in seconds) the sound should start playing. It is in the same time coordinate system as the {{AudioContext}}'s {{BaseAudioContext/currentTime}} attribute. If 0 is passed in for this value or if the value is less than <b>currentTime</b>, then the sound will start playing immediately. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>when</code> is negative.</span> | ||
| offset: The <code>offset</code> parameter supplies a <a>playhead position</a> where playback will begin. If 0 is passed in for this value, then playback will start from the beginning of the buffer. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>offset</code> is negative.</span> If <code>offset</code> is greater than {{AudioBufferSourceNode/loopEnd}}, playback will begin at {{AudioBufferSourceNode/loopEnd}} (and immediately loop to {{AudioBufferSourceNode/loopStart}}). <code>offset</code> is silently clamped to [0, <code>duration</code>], when <code>startTime</code> is reached, where <code>duration</code> is the value of the <code>duration</code> attribute of the {{AudioBuffer}} set to the {{AudioBufferSourceNode/buffer}} attribute of this <code>AudioBufferSourceNode</code>. | ||
| duration: The {{AudioBufferSourceNode/start(when, offset, duration)/duration}} parameter describes the duration of the sound (in seconds) to be played. If this parameter is passed, this method has exactly the same effect as the invocation of <code>start(when, offset)</code> followed by <code>stop(when + duration)</code>. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>duration</code> is negative.</span> | ||
| duration: The {{AudioBufferSourceNode/start(when, offset, duration)/duration}} parameter describes the duration of sound to be played, expressed as seconds of total buffer content to be output, including any whole or partial loop iterations. The units of {{AudioBufferSourceNode/start(when, offset, duration)/duration}} are independent of the effects of {{AudioBufferSourceNode/playbackRate}}. This behavior contrasts with the absolute time units employed by {{AudioScheduledSourceNode/stop()}}. Thus, a {{AudioBufferSourceNode/start(when, offset, duration)/duration}} of 5 seconds with a playback rate of 0.5 will output 5 seconds of buffer content at half speed, producing 10 seconds of audible output. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>duration</code> is negative.</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we need to say anything about stop().
I'm not really sure how much to say here actually about what happens. It's probably best to point to the algorithm. I think what you're saying here is correct and is understandable if we're not looping, but if we are, it's much harder to say clearly in words what happens.
| offset = pos; | ||
| if (arguments.length >= 3) { | ||
| stop = when + duration; | ||
| duration = dur; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to define duration?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The duration variable is already defined, on line 4819 (same place as offset).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
| when: The <code>when</code> parameter describes at what time (in seconds) the sound should start playing. It is in the same time coordinate system as the {{AudioContext}}'s {{BaseAudioContext/currentTime}} attribute. If 0 is passed in for this value or if the value is less than <b>currentTime</b>, then the sound will start playing immediately. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>when</code> is negative.</span> | ||
| offset: The <code>offset</code> parameter supplies a <a>playhead position</a> where playback will begin. If 0 is passed in for this value, then playback will start from the beginning of the buffer. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>offset</code> is negative.</span> If <code>offset</code> is greater than {{AudioBufferSourceNode/loopEnd}}, playback will begin at {{AudioBufferSourceNode/loopEnd}} (and immediately loop to {{AudioBufferSourceNode/loopStart}}). <code>offset</code> is silently clamped to [0, <code>duration</code>], when <code>startTime</code> is reached, where <code>duration</code> is the value of the <code>duration</code> attribute of the {{AudioBuffer}} set to the {{AudioBufferSourceNode/buffer}} attribute of this <code>AudioBufferSourceNode</code>. | ||
| duration: The {{AudioBufferSourceNode/start(when, offset, duration)/duration}} parameter describes the duration of the sound (in seconds) to be played. If this parameter is passed, this method has exactly the same effect as the invocation of <code>start(when, offset)</code> followed by <code>stop(when + duration)</code>. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>duration</code> is negative.</span> | ||
| duration: The {{AudioBufferSourceNode/start(when, offset, duration)/duration}} parameter describes the duration of sound to be played, expressed as seconds of total buffer content to be output, including any whole or partial loop iterations. The units of {{AudioBufferSourceNode/start(when, offset, duration)/duration}} are independent of the effects of {{AudioBufferSourceNode/playbackRate}}. For example, a {{AudioBufferSourceNode/start(when, offset, duration)/duration}} of 5 seconds with a playback rate of 0.5 will output 5 seconds of buffer content at half speed, producing 10 seconds of audible output. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>duration</code> is negative.</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think (as mentioned previously), we shouldn't really try to say in words what supposed to happen. Can we just refer to the algorithm?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel strongly that here (as in many other places in the spec) it's valuable to have a short textual description of what a thing is, as a counterpoint to the algorithm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm only concerned if it's inconsistent with the fine details of the algorithm. A non-normative note is acceptable to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's consistent -- if you would like to rephrase it somehow, please propose (or commit) some alternate wording.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated, I think a non-normative note would be fine. And I think the duration here also applies to looping. The looping playback will play for duration secs, taking into account the playback rate.
index.bs
Outdated
| // Check that currentTime is within allowable range for playback | ||
| if (currentTime < start || currentTime >= stop) { | ||
| // Check that currentTime and bufferDuration are within allowable range for playback | ||
| if (currentTime < start || currentTime >= stop || bufferDuration >= duration) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is wrong? Let duration = 129/sampleRate and let the playbackRate = 1. Since bufferDuration is initialized to 0 in process(), and framesToProcess is 128, bufferDuration is at most 128/sampleRate. So bufferDuration >= duration is never true. This means the following lines produce an output. I think that's wrong. Maybe this should be bufferTime instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps bufferDuration was intended to be declared outside the function?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @karlt for catching my error here. Now fixed.
rtoy
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty close; just a few more comments.
| offset = pos; | ||
| if (arguments.length >= 3) { | ||
| stop = when + duration; | ||
| duration = dur; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ack.
index.bs
Outdated
| if (!started) { | ||
| // Take note that buffer has started playing and get initial playhead position. | ||
| bufferTime = offset + ((currentTime - start) * computedPlaybackRate); | ||
| bufferTime = offset + bufferDuration; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this right? The initial value for bufferTime is now different between this version and the old version. I think the original is the right value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're correct about the inconsistency. But another way to go would be to initialize bufferDuration to (currentTime - start) * computedPlaybackRate in this clause. Wouldn't that be an even better fix? It would cause bufferDuration to respect fractional-frame start times.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed this change too, but thought it was fixing a bug.
For the when parameter, "If 0 is passed in for this value or if the value is
less than currentTime, then the sound will start playing immediately." Also,
"The offset parameter supplies a playhead position where playback will begin."
I expect playback to begin with the head at offset regardless of when.
I couldn't make sense of ((currentTime - start) * computedPlaybackRate).
It seems to be aiming to make playback start with the head at some point
after offset if when is less than currentTime.
(The spec text is helpful to indicate the intention of the algorithm.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I didn't look to see what the original is doing, but I agree with @karlt: playback should start at offset independent of when. I think bufferDuration should actually start at 0. If you choose any other value, then the actual playback duration will be less then the duration arg, even if playback rate is 1.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bufferDuration starts at 0. Simply initializing bufferTime to offset would be more clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
index.bs
Outdated
|
|
||
| // Variables for tracking node's playback state | ||
| let bufferTime = 0, started = false, enteredLoop = false; | ||
| let bufferDuration = 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we rename this? Maybe playbackDuration or something? I find it confusing because there's duration, buffer.duration, and now bufferDuration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I am now remembering that I called it bufferDuration because it is duration in terms of the buffer frame rate, not the audible playback duration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, what should it be? I think that it's now really the audible playback duration, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is changing duration from a measurement in the audible timeline to a measurement across the buffer. i.e. the same units as offset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Now I'm thoroughly confused. I thought if start(when, offset,1) were called and the playback rate is 1/10, the audio would be heard for 10 sec and not 1 sec. (Assuming there's at least 1 sec of audio in the buffer, or we're looping.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, for hearing the 10s. playback would end when bufferDuration is 1.
Perhaps bufferTimeElapsed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rtoy we agree on the meaning. I'll let you modify the branch as heading out of touch for 2 weeks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@joeberkovitz I'll take over this PR. Thanks for your work on this and see in a few weeks.
I think there's just a couple of minor things that need to be fixed:
- Rename
bufferDuration - Set
bufferTimeprobably to justoffset.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Renamed bufferDuration and initialized bufferTime to offset.
| when: The <code>when</code> parameter describes at what time (in seconds) the sound should start playing. It is in the same time coordinate system as the {{AudioContext}}'s {{BaseAudioContext/currentTime}} attribute. If 0 is passed in for this value or if the value is less than <b>currentTime</b>, then the sound will start playing immediately. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>when</code> is negative.</span> | ||
| offset: The <code>offset</code> parameter supplies a <a>playhead position</a> where playback will begin. If 0 is passed in for this value, then playback will start from the beginning of the buffer. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>offset</code> is negative.</span> If <code>offset</code> is greater than {{AudioBufferSourceNode/loopEnd}}, playback will begin at {{AudioBufferSourceNode/loopEnd}} (and immediately loop to {{AudioBufferSourceNode/loopStart}}). <code>offset</code> is silently clamped to [0, <code>duration</code>], when <code>startTime</code> is reached, where <code>duration</code> is the value of the <code>duration</code> attribute of the {{AudioBuffer}} set to the {{AudioBufferSourceNode/buffer}} attribute of this <code>AudioBufferSourceNode</code>. | ||
| duration: The {{AudioBufferSourceNode/start(when, offset, duration)/duration}} parameter describes the duration of the sound (in seconds) to be played. If this parameter is passed, this method has exactly the same effect as the invocation of <code>start(when, offset)</code> followed by <code>stop(when + duration)</code>. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>duration</code> is negative.</span> | ||
| duration: The {{AudioBufferSourceNode/start(when, offset, duration)/duration}} parameter describes the duration of sound to be played, expressed as seconds of total buffer content to be output, including any whole or partial loop iterations. The units of {{AudioBufferSourceNode/start(when, offset, duration)/duration}} are independent of the effects of {{AudioBufferSourceNode/playbackRate}}. For example, a {{AudioBufferSourceNode/start(when, offset, duration)/duration}} of 5 seconds with a playback rate of 0.5 will output 5 seconds of buffer content at half speed, producing 10 seconds of audible output. <span class="synchronous">A {{RangeError}} exception MUST be thrown if <code>duration</code> is negative.</span> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As stated, I think a non-normative note would be fine. And I think the duration here also applies to looping. The looping playback will play for duration secs, taking into account the playback rate.
|
What was the "Ack" comment about above - can you clarify? We need a temporary variable name for the value that gets stored in |
|
By "Ack", I meant that you're right and |
* Rename `bufferDuration` to `bufferTimeElapsed` * Initialize `bufferTime` to `offset`.
|
Marked as non substantive for IPR from ash-nazg. |
|
@joeberkovitz is an invited expert and former chair of the working group. The changes here are minor tweaks to the existing algorithm that @joeberkovitz actually wrote. |
Since WebAudio/web-audio-api#1681 (in 2018) the `duration` argument is specced [1] as being the duration of the buffer's content to be played, and not necessarily the one of the AudioContext's clock. So if the source node has a `playbackRate` that is not `1`, using this parameter will not be the same as using `stop(duration)`. I based the wordings on the specs text, but I don't mind another formulation altogether. Might also be noted that there is an interop issue here, where on Firefox does follow the specs. I'll open issues to get this sorted out though. [1]: https://webaudio.github.io/web-audio-api/#dom-audiobuffersourcenode-start-when-offset-duration-duration
Fixes #1660
Preview | Diff