Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 116 additions & 22 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -2500,9 +2500,10 @@ returned promise with the rendered result as an
interface OfflineAudioContext : BaseAudioContext {
constructor(OfflineAudioContextOptions contextOptions);
constructor(unsigned long numberOfChannels, unsigned long length, float sampleRate);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we make length nullable I think it would become length?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the discussion in the spec issue, was using length the final resolution?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The discussion has stalled since my last comment. AFAIU, it seems that everyone agreed on using length, but there is some divergence on how it should be used. Should it be optional or nullable?

I'd rather have length be nullable instead of optional. This way callers need to be a little more intentional when creating indefinite-length OfflineAudioContexts. What do you think?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then let's clarify the resolution first. I'd really liked to have a WG decision on this:

IIUC your design is to make length required, and the proposal from @karlt is to make it optional?

@karlt @padenot Can you all chime in here so we can make a decision?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new chunkSize parameter to startRendering() now provides sufficient control over how much is rendered.
That makes the length dictionary member of OfflineAudioContextOptions redundant, but we need to keep the member in some form for backward compat.
We can choose whether it becomes optional or stays required.

I think allowing the length to be omitted is more convenient for developers than having to explicitly state length: null.
To me, a missing length member also seems better representative of unspecified length than an explicit null value for a required length member.
Still, I guess null would be the best default value for an optional length member, so nullable seems appropriate for the type.

IIUC @gabrielsanbrito proposed making the length member required to save the developer from accidentally creating an unlimited/unspecified length context.
I don't feel that an unspecified length context would be a problem.
I see the potential of a simpler API without an unnecessary length member as more valuable than any potential resource recovery immediately on rendering having reached a specified total length.


Now that startRendering() may be called multiple times, there is the question of which length the legacy length OfflineAudioContextOptions dictionary member should describe:

I see the the default length of one rendering chunk (if not specified as a startRendering() parameter) as being consistent with the legacy length member.
IIUC @gabrielsanbrito sees the limit on the sum of the lengths of rendering chunks as being more consistent.
Perhaps this choice is less important if the length member can be optional.

I would prefer that startRendering(chunkSize) have control over how much is rendered, as I would see a limit from a limiting OfflineAudioContextOptions member as action at a distance.

Promise<AudioBuffer> startRendering();
Promise<AudioBuffer> startRendering(optional unsigned long chunkSize);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@karlt @padenot Please chime in here so we can make a group decision.

I don't think this argument is as controversial as length above, but I'd like to record the group's choice.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this asking about the chunkSize parameter?
I assume we want this to provide for variable chunk sizes.
I think this is better than a mutable attribute on the OfflineAudioContext, because the startRendering() parameter is directly associated with the rendering while the context attribute would be action at a distance.

This new parameter would need to be optional for backward compat.

Promise<undefined> resume();
Promise<undefined> suspend(double suspendTime);
Promise<undefined> close();
readonly attribute unsigned long length;
attribute EventHandler oncomplete;
};
Expand Down Expand Up @@ -2596,7 +2597,7 @@ Constructors</h4>

<pre class=argumentdef for="OfflineAudioContext/constructor(numberOfChannels, length, sampleRate)">
numberOfChannels: Determines how many channels the buffer will have. See {{BaseAudioContext/createBuffer()}} for the supported number of channels.
length: Determines the size of the buffer in sample-frames.
length: Determines the total size of the audio render in sample-frames.
sampleRate: Describes the sample-rate of the [=linear PCM=] audio data in the buffer in sample-frames per second. See [[#sample-rates]] for the required supported range.
</pre>
</dl>
Expand All @@ -2607,8 +2608,11 @@ Attributes</h4>
<dl dfn-type=attribute dfn-for="OfflineAudioContext">
: <dfn>length</dfn>
::
The size of the buffer in sample-frames. This is the same as the
value of the <code>length</code> parameter for the constructor.
The total size of the audio render in sample-frames. This is the same
as the value of the <code>length</code> parameter for the constructor.

For undefined-length rendering, this attribute SHOULD be set to
<code>null</code>.

: <dfn>oncomplete</dfn>
::
Expand All @@ -2621,7 +2625,7 @@ Attributes</h4>
Methods</h4>

<dl dfn-type=method dfn-for="OfflineAudioContext">
: <dfn>startRendering()</dfn>
: <dfn>startRendering(chunkSize)</dfn>
::
Given the current connections and scheduled changes, starts
rendering audio.
Expand All @@ -2640,21 +2644,41 @@ Methods</h4>
<ol>
<li>If [=this=]'s [=relevant global object=]'s [=associated Document=] is not [=fully active=] then return [=a promise rejected with=] "{{InvalidStateError}}" {{DOMException}}.

<li>If the {{[[rendering started]]}} slot on the
{{OfflineAudioContext}} is <em>true</em>, return a rejected
promise with {{InvalidStateError}}, and abort these
steps.

<li>Set the {{[[rendering started]]}} slot of the
{{OfflineAudioContext}} to <em>true</em>.

<li>Let <var>promise</var> be a new promise.

<lI>Let <var>bufferLength</var> be a number initialized to the
value of {{OfflineAudioContext/length}}.

<ol>
<li> If {{OfflineAudioContext/startRendering(chunkSize)/chunkSize}}
is provided:
<ol>
<li> Let <var>renderedFrames</var> be the number of
sample-frames already rendered by the
{{OfflineAudioContext}}.
<li> If <var>renderedFrames</var> +
{{OfflineAudioContext/startRendering(chunkSize)/chunkSize}}
is less than or equal to {{OfflineAudioContext/length}},
set <var>bufferLength</var> to
{{OfflineAudioContext/startRendering(chunkSize)/chunkSize}}.
<li> Otherwise, set <var>bufferLength</var> to
{{OfflineAudioContext/length}} -
<var>renderedFrames</var>.
</ol>
<li> Otherwise, if {{OfflineAudioContext/length}} is
<code>null</code>, set <var>bufferLength</var> to the
<a>render quantum size</a>.
</ol>

<li>Create a new {{AudioBuffer}}, with a number of
channels, length and sample rate equal respectively to the
<code>numberOfChannels</code>, <code>length</code> and
channels and sample rate equal respectively to the
<code>numberOfChannels</code> and
<code>sampleRate</code> values passed to this instance's
constructor in the <code>contextOptions</code> parameter.
constructor in the <code>contextOptions</code> parameter;
and length equal to <var>bufferLength</var>.
Assign this buffer to an internal slot
<dfn attribute for="OfflineAudioContext">[[rendered buffer]]</dfn> in the {{OfflineAudioContext}}.

Expand All @@ -2677,9 +2701,12 @@ Methods</h4>
occasion.

<ol>
<li>Let <var>numberOfFrames</var> be a number initialized to
the value of the length of {{[[rendered buffer]]}}.

<li>Given the current connections and scheduled changes, start
rendering <code>length</code> sample-frames of audio into
{{[[rendered buffer]]}}
rendering <var>numberOfFrames</var> sample-frames of audio into
{{[[rendered buffer]]}}.

<li>For every <a>render quantum</a>, check and
{{OfflineAudioContext/suspend()|suspend}}
Expand All @@ -2696,19 +2723,21 @@ Methods</h4>
<li>Resolve the <var ignore>promise</var> created by {{startRendering()}}
with {{[[rendered buffer]]}}.

<li>[=Queue a media element task=] to [=fire an event=] named
{{OfflineAudioContext/complete}} at the {{OfflineAudioContext}} using
{{OfflineAudioCompletionEvent}} whose `renderedBuffer` property is set to
{{[[rendered buffer]]}}.
<li>If {{OfflineAudioContext/length}} is not
<code>null</code>, [=Queue a media element task=] to
[=fire an event=] named {{OfflineAudioContext/complete}}
at the {{OfflineAudioContext}} using
{{OfflineAudioCompletionEvent}} whose `renderedBuffer`
property is set to {{[[rendered buffer]]}}.

</ol>

</ol>
</div>

<div>
<em>No parameters.</em>
</div>
<pre class=argumentdef for="OfflineAudioContext/startRendering()">
chunkSize: The number of sample-frames to render during this call.
</pre>
<div>
<em>Return type:</em> {{Promise}}&lt;{{AudioBuffer}}&gt;
</div>
Expand Down Expand Up @@ -2803,6 +2832,71 @@ Methods</h4>
<div>
<em>Return type:</em> {{Promise}}&lt;{{undefined}}&gt;
</div>

: <dfn>close()</dfn>
::
Closes the {{OfflineAudioContext}}. This will not automatically release
all {{AudioContext}}-created objects, but will suspend the progression
of the {{AudioContext}}'s {{BaseAudioContext/currentTime}}, and stop
processing audio data.

<div algorithm="OfflineAudioContext.close()">
<span class="synchronous">When close is called, execute these steps:</span>

1. If [=this=]'s [=relevant global object=]'s
[=associated Document=] is not [=fully active=] then return
[=a promise rejected with=] "{{InvalidStateError}}"
{{DOMException}}.

1. Let <var>promise</var> be a new Promise.

1. If the {{[[control thread state]]}} flag on the
{{OfflineAudioContext}} is <code>closed</code> reject the
promise with {{InvalidStateError}}, abort these steps,
returning <var>promise</var>.

1. Set the {{[[control thread state]]}} flag on the
{{OfflineAudioContext}} to <code>closed</code>.

1. <a>Queue a control message</a> to close the
{{OfflineAudioContext}}.

1. Return <em>promise</em>.
</div>

<div algorithm="run a control message to close an OfflineAudioContext">
Running a <a>control message</a> to close an {{OfflineAudioContext}}
means running these steps on the <a>rendering thread</a>:

1. Set the {{[[rendering thread state]]}} to <code>suspended</code>.
<div class="note">
This will stop rendering.
</div>

1. If this <a>control message</a> is being run in a reaction to the
document being unloaded, abort this algorithm.
<div class="note">
There is no need to notify the control thread in this case.
</div>

1. <a href="https://html.spec.whatwg.org/multipage/media.html#queue-a-media-element-task">
queue a media element task</a> to execute the following steps:

1. Resolve <em>promise</em>.
1. If the {{BaseAudioContext/state}} attribute of the
{{OfflineAudioContext}} is not already
"{{AudioContextState/closed}}":
1. Set the {{BaseAudioContext/state}} attribute of the
{{OfflineAudioContext}} to
"{{AudioContextState/closed}}".

1. <a href="https://html.spec.whatwg.org/multipage/media.html#queue-a-media-element-task">
queue a media element task</a> to
<a spec="dom" lt="fire an event">fire an event</a> named
{{BaseAudioContext/statechange}} at the
{{OfflineAudioContext}}.
</div>

</dl>

<h4 dictionary id="OfflineAudioContextOptions">
Expand Down
Loading