Describe the issue
Currently, OfflineAudioContext.startRendering will unconditionally and always result in the creation of an AudioBuffer containing the rendered audio, regardless of whether it is actually used or not.
This is a problem when used to render a massive duration, as it results in a gargantuan memory impact. Take the following settings for example:
- Sample rate: 48kHz
- Channels: 4
- Duration: 4 hours
Rendering this monster, we have 48000 * 4 * 4 * 60 * 60 = 2_764_800_000 float32 values total, which amounts to more than 11 Gigabytes of in-memory data (more specifically, we don't, because no end-user browser will allow this allocation without flags, some even with flags, not speaking of systems which don't even have this much RAM in the first place).
Where Is It
https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-startrendering, more specifically in step 4 of the OfflineAudioContext.startRendering process:
Create a new AudioBuffer, with a number of channels, length and sample rate equal respectively to the numberOfChannels, length and sampleRate values passed to this instance’s constructor in the contextOptions parameter. Assign this buffer to an internal slot [[rendered buffer]] in the OfflineAudioContext.
Additional Information
To avoid the above issue, OfflineAudioContext should "somehow" allow skipping this preliminary forced memory allocation, and be able to deliver data at regular intervals.
The way I see it, there are several options here, and I personally think the MediaRecorder.ondataavailable is a good starting point, which is an already established example of incremental data delivery.
- Having OfflineAudioContext expose a ReadableStream that's pushed as data is available from rendering
- Note: this has the potential to synergize extremely well with the proposed WebCodecs API, which has a proposal to work out of a ReadableStream -- if the OfflineAudioContext exposed a ReadableStream, it would be possible to plug it into the WebCodecs API directly and acquire formatted audio files in an efficient and elegant manner, which is a very strong point for this option
- Emitting some sort of a
dataavailable event, possibly with an AudioBuffer for each event containing a chunk of the rendered data
- Probably unnecessary, as AudioWorklet is a viable alternative already
- Rendering to a Blob (I personally don't see how the details would work out here)
- Simply not returning anything, and instead letting user code work it out using AudioWorklets (emitting custom "events", building a stream, etc.)
Polyfilling
While the memory impact is unavoidable with the current implementations of startRendering, for some options above, a polyfill based on an AudioWorkletNode (or even ScriptProcessorNode) could easily make the incremental data delivery an option early on, allowing for prototyping the API in advance and allowing progressive upgrades as the change lands in browsers.
Describe the issue
Currently,
OfflineAudioContext.startRenderingwill unconditionally and always result in the creation of an AudioBuffer containing the rendered audio, regardless of whether it is actually used or not.This is a problem when used to render a massive duration, as it results in a gargantuan memory impact. Take the following settings for example:
Rendering this monster, we have
48000 * 4 * 4 * 60 * 60 = 2_764_800_000float32 values total, which amounts to more than 11 Gigabytes of in-memory data (more specifically, we don't, because no end-user browser will allow this allocation without flags, some even with flags, not speaking of systems which don't even have this much RAM in the first place).Where Is It
https://webaudio.github.io/web-audio-api/#dom-offlineaudiocontext-startrendering, more specifically in step 4 of the OfflineAudioContext.startRendering process:
Additional Information
To avoid the above issue, OfflineAudioContext should "somehow" allow skipping this preliminary forced memory allocation, and be able to deliver data at regular intervals.
The way I see it, there are several options here, and I personally think the MediaRecorder.ondataavailable is a good starting point, which is an already established example of incremental data delivery.
dataavailableevent, possibly with an AudioBuffer for each event containing a chunk of the rendered dataPolyfilling
While the memory impact is unavoidable with the current implementations of startRendering, for some options above, a polyfill based on an AudioWorkletNode (or even ScriptProcessorNode) could easily make the incremental data delivery an option early on, allowing for prototyping the API in advance and allowing progressive upgrades as the change lands in browsers.