@@ -31,23 +31,40 @@ function createWasmAudioWorkletProcessor(audioParams) {
3131 let opts = args . processorOptions ;
3232 this . callbackFunction = Module [ 'wasmTable' ] . get ( opts [ 'cb' ] ) ;
3333 this . userData = opts [ 'ud' ] ;
34+
3435 // Then the samples per channel to process, fixed for the lifetime of the
35- // context that created this processor. Note for when moving to Web Audio
36- // 1.1: the typed array passed to process() should be the same size as
37- // this 'render quantum size', and this exercise of passing in the value
38- // shouldn't be required (to be verified with the in/out data lengths ).
36+ // context that created this processor. Even though this 'render quantum
37+ // size' is fixed at 128 samples in the 1.0 spec, it will be variable in
38+ // the 1.1 spec. It's passed in now, just to prove it's settable, but will
39+ // eventually be a property of the AudioWorkletGlobalScope (globalThis ).
3940 this . samplesPerChannel = opts [ 'sc' ] ;
40- // Typed views of the output buffers on the worklet's stack, which after
41- // creation should only change if the audio chain changes.
41+
42+ // Create up-front as many typed views for marshalling the output data as
43+ // may be required (with an arbitrary maximum of 16, for the case where a
44+ // multi-MB stack is passed), allocated at the *top* of the worklet's
45+ // stack (and whose addresses are fixed).
46+ this . maxBuffers = Math . min ( ( Module [ 'sz' ] / ( this . samplesPerChannel * 4 ) ) | 0 , /*sensible limit*/ 16 ) ;
47+ // These are still alloc'd to take advantage of the overflow checks, etc.
48+ var oldStackPtr = stackSave ( ) ;
49+ var viewDataIdx = stackAlloc ( this . maxBuffers * this . samplesPerChannel * 4 ) >> 2 ;
50+ #if WEBAUDIO_DEBUG
51+ console . log ( `AudioWorklet creating ${ this . maxBuffers } buffer one-time views (for a stack size of ${ Module [ 'sz' ] } )` ) ;
52+ #endif
4253 this . outputViews = [ ] ;
54+ for ( var i = this . maxBuffers ; i > 0 ; i -- ) {
55+ // Added in reverse so the lowest indices are closest to the stack top
56+ this . outputViews . unshift (
57+ HEAPF32 . subarray ( viewDataIdx , viewDataIdx += this . samplesPerChannel )
58+ ) ;
59+ }
60+ stackRestore ( oldStackPtr ) ;
4361 }
4462
4563 static get parameterDescriptors ( ) {
4664 return audioParams ;
4765 }
4866
4967 process ( inputList , outputList , parameters ) {
50- var time = Date . now ( ) ;
5168 // Marshal all inputs and parameters to the Wasm memory on the thread stack,
5269 // then perform the wasm audio worklet call,
5370 // and finally marshal audio output data back.
@@ -56,53 +73,42 @@ function createWasmAudioWorkletProcessor(audioParams) {
5673 numOutputs = outputList . length ,
5774 numParams = 0 , i , j , k , dataPtr ,
5875 bytesPerChannel = this . samplesPerChannel * 4 ,
76+ outputStackNeeded = 0 , outputViewsNeeded = 0 ,
5977 stackMemoryNeeded = ( numInputs + numOutputs ) * { { { C_STRUCTS . AudioSampleFrame . __size__ } } } ,
6078 oldStackPtr = stackSave ( ) ,
61- inputsPtr , outputsPtr , outputDataPtr , paramsPtr , requiredViews = 0 ,
79+ inputsPtr , outputsPtr , outputDataPtr , paramsPtr ,
6280 didProduceAudio , paramArray ;
6381
64- // Calculate how much stack space is needed.
65- for ( i of inputList ) stackMemoryNeeded += i . length * bytesPerChannel ;
66- for ( i of outputList ) stackMemoryNeeded += i . length * bytesPerChannel ;
67- for ( i in parameters ) stackMemoryNeeded += parameters [ i ] . byteLength + { { { C_STRUCTS . AudioParamFrame . __size__ } } } , ++ numParams ;
68-
69- // Allocate the necessary stack space (dataPtr is always in bytes, and
70- // advances as space for structs as data is taken, but note the switching
71- // between bytes and indices into the various heaps).
72- dataPtr = stackAlloc ( stackMemoryNeeded ) ;
73-
74- // But start the output view allocs first, since once views are created we
75- // want them to always start from the same address. Even if in the next
76- // process() the outputList is empty, as soon as there are channels again
77- // the views' addresses will still be the same (and only if more views are
78- // required we will recreate them).
79- outputDataPtr = dataPtr ;
80- for ( /*which output*/ i of outputList ) {
82+ // Calculate how much stack space is needed, first for the outputs
83+ // pre-allocated in the constructor from the stack top.
84+ for ( i of outputList ) {
85+ outputStackNeeded += i . length * bytesPerChannel ;
86+ outputViewsNeeded += i . length ;
8187#if ASSERTIONS
8288 for ( /*which channel*/ j of i ) {
8389 console . assert ( j . byteLength === bytesPerChannel , `Unexpected AudioWorklet output buffer size (expected ${ bytesPerChannel } got ${ j . byteLength } )` ) ;
8490 }
8591#endif
86- // Keep advancing to make room for the output views
87- dataPtr += bytesPerChannel * i . length ;
88- // How many output views are needed in total?
89- requiredViews += i . length ;
9092 }
91- // Verify we have enough views (it doesn't matter if we have too many, any
92- // excess won't be accessed) then also verify the views' start address
93- // haven't changed.
94- if ( this . outputViews . length < requiredViews || ( this . outputViews . length && this . outputViews [ 0 ] . byteOffset != outputDataPtr ) ) {
95- this . outputViews = [ ] ;
96- k = outputDataPtr >> 2 ;
97- for ( i of outputList ) {
98- for ( j of i ) {
99- this . outputViews . push (
100- HEAPF32 . subarray ( k , k += this . samplesPerChannel )
101- ) ;
102- }
103- }
93+ // Then the remaining space all together
94+ for ( i of inputList ) stackMemoryNeeded += i . length * bytesPerChannel ;
95+ for ( i in parameters ) stackMemoryNeeded += parameters [ i ] . byteLength + { { { C_STRUCTS . AudioParamFrame . __size__ } } } , ++ numParams ;
96+
97+ outputDataPtr = stackAlloc ( outputStackNeeded ) ;
98+ //#if ASSERTIONS
99+ console . assert ( outputViewsNeeded <= this . outputViews . length , `Too many AudioWorklet outputs (need ${ outputViewsNeeded } but have stack space for ${ this . outputViews . length } )` ) ;
100+ k = outputDataPtr ;
101+ for ( i = outputViewsNeeded - 1 ; i >= 0 ; i -- ) {
102+ console . assert ( this . outputViews [ i ] . byteOffset == k , 'Internal error in addresses of the output array views' ) ;
103+ k += bytesPerChannel ;
104104 }
105+ //#endif
106+
107+ // Allocate the necessary stack space (dataPtr is always in bytes, and
108+ // advances as space for structs as data is taken, but note the switching
109+ // between bytes and indices into the various heaps).
105110
111+ dataPtr = stackAlloc ( stackMemoryNeeded ) ;
106112 // Copy input audio descriptor structs and data to Wasm
107113 inputsPtr = dataPtr ;
108114 k = inputsPtr >> 2 ;
@@ -163,9 +169,6 @@ function createWasmAudioWorkletProcessor(audioParams) {
163169 }
164170
165171 stackRestore ( oldStackPtr ) ;
166-
167- time = Date . now ( ) - time ;
168- //console.log(time);
169172
170173 // Return 'true' to tell the browser to continue running this processor.
171174 // (Returning 1 or any other truthy value won't work in Chrome)
0 commit comments