Skip to content

Commit c98adcf

Browse files
committed
Correct stack offsets and verified code
Tested with various stack sizes, output sizes, and generators.
1 parent 5b65dcf commit c98adcf

File tree

1 file changed

+34
-23
lines changed

1 file changed

+34
-23
lines changed

src/audio_worklet.js

Lines changed: 34 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,16 @@ function createWasmAudioWorkletProcessor(audioParams) {
4040
this.samplesPerChannel = opts['sc'];
4141

4242
// 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
43+
// may be required (with an arbitrary maximum of 10, for the case where a
4444
// 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'] - /*stack guards?*/ 16) / (this.samplesPerChannel * 4)) | 0, /*sensible limit*/ 16);
45+
// stack (and whose addresses are fixed). The 'minimum alloc' firstly
46+
// stops STACK_OVERFLOW_CHECK failing (since the stack will be full, and
47+
// 16 being the minimum allocation size due to alignments) and leaves room
48+
// for a single AudioSampleFrame as a minumum.
49+
this.maxBuffers = Math.min(((Module['sz'] - /*minimum alloc*/ 16) / (this.samplesPerChannel * 4)) | 0, /*sensible limit*/ 10);
50+
#if ASSERTIONS
51+
console.assert(this.maxBuffers > 0, `AudioWorklet needs more stack allocating (at least ${this.samplesPerChannel * 4})`);
52+
#endif
4753
// These are still alloc'd to take advantage of the overflow checks, etc.
4854
var oldStackPtr = stackSave();
4955
var viewDataIdx = stackAlloc(this.maxBuffers * this.samplesPerChannel * 4) >> 2;
@@ -77,7 +83,6 @@ function createWasmAudioWorkletProcessor(audioParams) {
7783
stackMemoryNeeded = (numInputs + numOutputs) * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}},
7884
oldStackPtr = stackSave(),
7985
inputsPtr, outputsPtr, paramsPtr,
80-
outputDataPtr,
8186
didProduceAudio, paramArray;
8287

8388
// Calculate how much stack space is needed
@@ -91,14 +96,18 @@ function createWasmAudioWorkletProcessor(audioParams) {
9196
#endif
9297

9398
// Allocate the necessary stack space (dataPtr is always in bytes, and
94-
// advances as space for structs as data is taken, but note the switching
95-
// between bytes and indices into the various heaps, usually in 'k').
96-
dataPtr = stackAlloc(stackMemoryNeeded);
99+
// advances as space for structs and data is taken, but note the switching
100+
// between bytes and indices into the various heaps, usually in 'k'). This
101+
// will be 16-byte aligned (from _emscripten_stack_alloc()), as were the
102+
// output views, so we round up and advance the required bytes to ensure
103+
// the addresses all work out at the end.
104+
i = (stackMemoryNeeded + 15) & ~15;
105+
dataPtr = stackAlloc(i) + (i - stackMemoryNeeded);
97106

98107
// Copy input audio descriptor structs and data to Wasm
99108
inputsPtr = dataPtr;
100-
k = inputsPtr >> 2;
101109
dataPtr += numInputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}};
110+
k = inputsPtr >> 2;
102111
for (i of inputList) {
103112
// Write the AudioSampleFrame struct instance
104113
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / 4 }}}] = i.length;
@@ -114,8 +123,8 @@ function createWasmAudioWorkletProcessor(audioParams) {
114123

115124
// Copy parameters descriptor structs and data to Wasm
116125
paramsPtr = dataPtr;
117-
k = paramsPtr >> 2;
118126
dataPtr += numParams * {{{ C_STRUCTS.AudioParamFrame.__size__ }}};
127+
k = paramsPtr >> 2;
119128
for (i = 0; paramArray = parameters[i++];) {
120129
// Write the AudioParamFrame struct instance
121130
HEAPU32[k + {{{ C_STRUCTS.AudioParamFrame.length / 4 }}}] = paramArray.length;
@@ -126,13 +135,11 @@ function createWasmAudioWorkletProcessor(audioParams) {
126135
dataPtr += paramArray.length*4;
127136
}
128137

129-
// Copy output audio descriptor structs to Wasm
138+
// Copy output audio descriptor structs to Wasm (not that dataPtr after
139+
// the struct offsets should now be 16-byte aligned).
130140
outputsPtr = dataPtr;
131-
k = outputsPtr >> 2;
132141
dataPtr += numOutputs * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}};
133-
#if ASSERTIONS
134-
outputDataPtr = dataPtr;
135-
#endif
142+
k = outputsPtr >> 2;
136143
for (i of outputList) {
137144
// Write the AudioSampleFrame struct instance
138145
HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / 4 }}}] = i.length;
@@ -144,23 +151,27 @@ function createWasmAudioWorkletProcessor(audioParams) {
144151
}
145152

146153
#if ASSERTIONS
147-
// TODO: addresses are now wrong
148-
k = outputDataPtr;
149-
for (i = outputViewsNeeded - 1; i >= 0; i--) {
150-
console.assert(this.outputViews[i].byteOffset == k, 'Internal error in addresses of the output array views');
151-
k += bytesPerChannel;
154+
// If all the maths worked out, we arrived at the original stack address
155+
console.assert(dataPtr == oldStackPtr, `AudioWorklet stack missmatch (audio data finishes at ${dataPtr} instead of ${oldStackPtr})`);
156+
// Sanity check the output view addresses
157+
if (numOutputs) {
158+
k = dataPtr - bytesPerChannel;
159+
for (i = 0; i < outputViewsNeeded; i++) {
160+
console.assert(k == this.outputViews[i].byteOffset, 'AudioWorklet internal error in addresses of the output array views');
161+
k -= bytesPerChannel;
162+
}
152163
}
153164
#endif
154165

155166
// Call out to Wasm callback to perform audio processing
156167
if (didProduceAudio = this.callbackFunction(numInputs, inputsPtr, numOutputs, outputsPtr, numParams, paramsPtr, this.userData)) {
157168
// Read back the produced audio data to all outputs and their channels.
158-
// The 'outputViews' are subarray views into the heap, each with the
159-
// correct offset and size to be copied directly into the output.
160-
k = 0;
169+
// The preallocated 'outputViews' already have the correct offsets and
170+
// sizes into the stack (recall from the ctor that they run backwards).
171+
k = outputViewsNeeded - 1;
161172
for (i of outputList) {
162173
for (j of i) {
163-
j.set(this.outputViews[k++]);
174+
j.set(this.outputViews[k--]);
164175
}
165176
}
166177
}

0 commit comments

Comments
 (0)