-
Notifications
You must be signed in to change notification settings - Fork 171
Add AudioPlayoutStats interface #2645
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
base: main
Are you sure you want to change the base?
Changes from 4 commits
66fa3b7
2aaed39
32ca908
af18297
c39cef2
73b7409
dd0b3a5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -663,6 +663,9 @@ The interfaces defined are: | |||||||||||||||||||||||||
{{AudioNode}} which applies a non-linear waveshaping | ||||||||||||||||||||||||||
effect for distortion and other more subtle warming effects. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
* An {{AudioPlayoutStats}} interface, which provides statistics about the audio | ||||||||||||||||||||||||||
played from the {{AudioContext}}. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
There are also several features that have been deprecated from the | ||||||||||||||||||||||||||
Web Audio API but not yet removed, pending implementation experience | ||||||||||||||||||||||||||
of their replacements: | ||||||||||||||||||||||||||
|
@@ -1488,6 +1491,7 @@ interface AudioContext : BaseAudioContext { | |||||||||||||||||||||||||
[SecureContext] readonly attribute (DOMString or AudioSinkInfo) sinkId; | ||||||||||||||||||||||||||
attribute EventHandler onsinkchange; | ||||||||||||||||||||||||||
attribute EventHandler onerror; | ||||||||||||||||||||||||||
[SameObject] readonly attribute AudioPlayoutStats playoutStats; | ||||||||||||||||||||||||||
AudioTimestamp getOutputTimestamp (); | ||||||||||||||||||||||||||
Promise<undefined> resume (); | ||||||||||||||||||||||||||
Promise<undefined> suspend (); | ||||||||||||||||||||||||||
|
@@ -1533,6 +1537,11 @@ and to allow it only when the {{AudioContext}}'s [=relevant global object=] has | |||||||||||||||||||||||||
:: | ||||||||||||||||||||||||||
An ordered list to store pending {{Promise}}s created by | ||||||||||||||||||||||||||
{{AudioContext/resume()}}. It is initially empty. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
: <dfn>[[playout stats]]</dfn> | ||||||||||||||||||||||||||
:: | ||||||||||||||||||||||||||
A slot where an instance of {{AudioPlayoutStats}} can be stored. It is | ||||||||||||||||||||||||||
initially null. | ||||||||||||||||||||||||||
</dl> | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
<h4 id="AudioContext-constructors"> | ||||||||||||||||||||||||||
|
@@ -1769,6 +1778,22 @@ Attributes</h4> | |||||||||||||||||||||||||
the context is {{AudioContextState/running}}. | ||||||||||||||||||||||||||
* When the operating system reports an audio device malfunction. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
: <dfn>playoutStats</dfn> | ||||||||||||||||||||||||||
:: | ||||||||||||||||||||||||||
An instance of {{AudioPlayoutStats}} for this {{AudioContext}}. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
<div algorithm="access playoutStats"> | ||||||||||||||||||||||||||
<span class="synchronous">When accessing this attribute, run the | ||||||||||||||||||||||||||
following steps:</span> | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
1. If the {{[[playout stats]]}} slot is null, construct a new | ||||||||||||||||||||||||||
{{AudioPlayoutStats}} object with [=this=] as the argument, and | ||||||||||||||||||||||||||
store it in {{[[playout stats]]}}. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
1. Return the value of the {{[[playout stats]]}} internal slot. | ||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
</dl> | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
<h4 id="AudioContext-methods"> | ||||||||||||||||||||||||||
|
@@ -11536,6 +11561,313 @@ context.audioWorklet.addModule('vumeter-processor.js').then(() => { | |||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||
</xmp> | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
<h3 interface lt="AudioPlayoutStats" id="AudioPlayoutStats"> | ||||||||||||||||||||||||||
The {{AudioPlayoutStats}} Interface</h3> | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Provides audio underrun and latency statistics for audio played through the | ||||||||||||||||||||||||||
{{AudioContext}}. | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
Audio underruns (also commonly called glitches) are gaps in the audio | ||||||||||||||||||||||||||
playout which occur when the audio pipeline cannot deliver audio on time. | ||||||||||||||||||||||||||
Underruns (often manifesting as audible "clicks" in the playout) are bad | ||||||||||||||||||||||||||
for the user experience, so if any of these occur it | ||||||||||||||||||||||||||
can be useful for the application to be able to detect them and possibly | ||||||||||||||||||||||||||
take some action to improve the playout. | ||||||||||||||||||||||||||
|
Audio underruns (also commonly called glitches) are gaps in the audio | |
playout which occur when the audio pipeline cannot deliver audio on time. | |
Underruns (often manifesting as audible "clicks" in the playout) are bad | |
for the user experience, so if any of these occur it | |
can be useful for the application to be able to detect them and possibly | |
take some action to improve the playout. | |
Audio underruns (also commonly called glitches) are gaps in the audio | |
playback that occur when an audio pipeline cannot deliver audio on time. | |
Underruns (often manifesting as audible "clicks" in the playout) are bad | |
for the user experience, so if any of these occur it | |
can be useful for the application to be able to detect them and possibly | |
take some action to improve the playout. |
we probably need to rephrase this. When the audio isn't delievered on time, it causes an audio underrun, that causes a discontinuity, that causes and audible click, that can be called a glitch. This is because of the synchronous nature of the AudioContext
, unlike for example a PeerConnection
, that will do something when audio input is missing to cover it up.
Outdated
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.
{{AudioPlayoutStats}} is a dedicated object for audio stats reporting; | |
{{AudioPlayoutStats}} is a dedicated object for audio statistics reporting; |
Outdated
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.
{{AudioContext's}} playout path via | |
{{AudioContext's}} playback path via |
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.
Let's standardize on "playback" or "audio output". "playout" is an rtc/voip term. Both in normative text + API.
Outdated
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.
{{AudioContext's}} playout path via | |
{{AudioDestinationNode}} and the associated output device. This allows | |
{{AudioContext's}} playout path via the | |
{{AudioDestinationNode}} and its associated audio output device. This allows |
Outdated
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 is important, but unrelated to AudioWorklet
. We want to point out two possible causes:
- The audio graph itself is too expensive for the current setup -- latency is too low, computer is generally too slow, etc.
- The audio graph itself would render fine, but an external factor causes issues: other audio program on the device, global system overload, overload because of thermal throttling, etc.
it is important to make it extra clear what is being discussed here (especially in relation to the other section).
Outdated
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.
that was not provided by the playout path. | |
that was not provided by the AudioContext. |
Outdated
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.
It's actually the opposite -- remember, this is all synchronous rendering.
Outdated
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.
to the output device on time, in which case underrun frames will be played | |
(underrun frames are typically silence). | |
to the output device on time, in which case it will still have to play something. | |
NOTE: Underrun frames are typically silence. |
Outdated
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.
pipeline isn't a term that exists in this specification, find something else. rendering graph, maybe?
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.
We should remove this, and make it clear in a dedicated section, possibly non-normative.
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.
What is "this"? Is it a point in time, a duration? Both sentences seem to disagree.
Outdated
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.
Playback
Outdated
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.
Do we want to make this constructable?
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.
If our definition of an underrun event is a duration, this can be the sum of the duration of all underrun events.
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 define this in terms of currentTime
, latency and underrun duration?
Outdated
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.
frames don't have a latency, maybe we just want to say The average audio output latency of the AudioContext over the currently tracked interval
.
Outdated
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.
We want to define currently tracked interval
to be from the last reset()
to now (roughly). Shouldn't it be initialized on the first "suspended" -> "running" transition or something like that? Akin to outputLatency
, but I'm not sure that we define this properly.
Outdated
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 is in the clock domain of AudioContext.currentTime
Outdated
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.
Use markup shorthands.
Outdated
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.
Measures the duration of [=underrun frames=] played by the | |
Returns the duration of [=underrun frames=] played by the |
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.
throughout
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.
Maybe we just want to drop the first sentence of those attributes, we're repeating ourselves here.
Outdated
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 metric can be used together with {{totalDuration}} to | |
NOTE: This metric can be used together with {{totalDuration}} to |
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.
Why are we restricting this to the latency? A developer that notices that the underrun figures increase and make changes to its processing will want to know if it increases again. OTOH the latency is typically but not always constant with hopefully a very tight stddev.
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.
by definition, an AudioContext
doesn't play an under-run frame, this is backwards.
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 curious if this design holds up to distinguish these three cases
- occasional spike
- consistent overload -> consistent underruns (every quantum processed)
- periodic overload. as an example, a misaligned block based computation that ends up processing every N frames
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.
Hi!
- An occasional spike will manifest as a single increase in
underrunDuration
, and an increase inunderrunEvents
by 1. - Consistent underruns will manifest as many small increases in
underrunDuration
, and many increases inunderrunEvents
. - Similar to 1, but periodic. Since the API updates at most once per second (for privacy reasons), this might not be possible to immediately detect if N is small enough that we get several underruns per second. If N is large, it should be possible to see that the underruns occur at regular intervals. Also (if my math is correct)
underrunEvents / currentTime
should converge steadily towardssampleRate / N
, so you could also look for that.
Outdated
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.
Let's use the API name here, and not introduce another term.
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, but only if:
- There is a linearization point somewhere on the system (typically the audio mixer, be it in the OS or in the browser)
- The callbacks are effectively synchronous all the way from this linearization point, without a buffer in between that could flatten load spikes (that could be because of a different
AudioContextLatencyCategory
).
Outdated
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.
why not MUST?
Outdated
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.
Also why not MUST? Is this normative at all?
Outdated
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 the current prose makes this example unnecessary, it's already quite 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.
Why are we doing this little dance and not return it directly? An implementation can do this lazy initialization if it wants to, but this it isn't useful to normatively require it, and it isn't observable.