diff --git a/src/audio_worklet.js b/src/audio_worklet.js index ab8dfa2d9ef66..26dfa5d3ad823 100644 --- a/src/audio_worklet.js +++ b/src/audio_worklet.js @@ -50,7 +50,7 @@ function createWasmAudioWorkletProcessor() { // 64 frames, for the case where a multi-MB stack is passed. this.outputViews = new Array(Math.min(((wwParams.stackSize - {{{ STACK_ALIGN }}}) / this.bytesPerChannel) | 0, /*sensible limit*/ 64)); #if ASSERTIONS - console.assert(this.outputViews.length > 0, `AudioWorklet needs more stack allocating (at least ${this.bytesPerChannel})`); + assert(this.outputViews.length > 0, `AudioWorklet needs more stack allocating (at least ${this.bytesPerChannel})`); #endif this.createOutputViews(); @@ -138,8 +138,8 @@ function createWasmAudioWorkletProcessor() { #endif var oldStackPtr = stackSave(); #if ASSERTIONS - console.assert(oldStackPtr == this.ctorOldStackPtr, 'AudioWorklet stack address has unexpectedly moved'); - console.assert(outputViewsNeeded <= this.outputViews.length, `Too many AudioWorklet outputs (need ${outputViewsNeeded} but have stack space for ${this.outputViews.length})`); + assert(oldStackPtr == this.ctorOldStackPtr, 'AudioWorklet stack address has unexpectedly moved'); + assert(outputViewsNeeded <= this.outputViews.length, `Too many AudioWorklet outputs (need ${outputViewsNeeded} but have stack space for ${this.outputViews.length})`); #endif // Allocate the necessary stack space. All pointer variables are in bytes; @@ -154,6 +154,10 @@ function createWasmAudioWorkletProcessor() { var stackMemoryAligned = (stackMemoryStruct + stackMemoryData + 15) & ~15; var structPtr = stackAlloc(stackMemoryAligned); var dataPtr = structPtr + (stackMemoryAligned - stackMemoryData); +#if ASSERTIONS + // TODO: look at why stackAlloc isn't tripping the assertions + assert(stackMemoryAligned <= wwParams.stackSize, `Not enough stack allocated to the AudioWorklet (need ${stackMemoryAligned}, got ${wwParams.stackSize})`); +#endif // Copy input audio descriptor structs and data to Wasm (recall, structs // first, audio data after). 'inputsPtr' is the start of the C callback's @@ -221,7 +225,7 @@ function createWasmAudioWorkletProcessor() { // And that the views' size match the passed in output buffers for (entry of outputList) { for (subentry of entry) { - console.assert(subentry.byteLength == this.bytesPerChannel, `AudioWorklet unexpected output buffer size (expected ${this.bytesPerChannel} got ${subentry.byteLength})`); + assert(subentry.byteLength == this.bytesPerChannel, `AudioWorklet unexpected output buffer size (expected ${this.bytesPerChannel} got ${subentry.byteLength})`); } } } diff --git a/src/lib/libwebaudio.js b/src/lib/libwebaudio.js index dde80718a2cce..2c028659ff796 100644 --- a/src/lib/libwebaudio.js +++ b/src/lib/libwebaudio.js @@ -77,12 +77,7 @@ var LibraryWebAudio = { // Performs the work of getting the AudioContext's render quantum size. $emscriptenGetContextQuantumSize: (contextHandle) => { - // TODO: in a future release this will be something like: - // return EmAudio[contextHandle].renderQuantumSize || 128; - // It comes two caveats: it needs the hint when generating the context adding to - // emscripten_create_audio_context(), and altering the quantum requires a secure - // context and fallback implementing. Until then we simply use the 1.0 API value: - return 128; + return EmAudio[contextHandle]['renderQuantumSize'] || 128; }, // emscripten_create_audio_context() does not itself use the @@ -100,9 +95,15 @@ var LibraryWebAudio = { #endif #endif + // Converts AUDIO_CONTEXT_RENDER_SIZE_* into AudioContextRenderSizeCategory + // enums, otherwise returns a positive int value. + function readRenderSizeHint(val) { + return (val < 0) ? 'hardware' : (val || 'default'); + } var opts = options ? { latencyHint: UTF8ToString({{{ makeGetValue('options', C_STRUCTS.EmscriptenWebAudioCreateAttributes.latencyHint, '*') }}}) || undefined, - sampleRate: {{{ makeGetValue('options', C_STRUCTS.EmscriptenWebAudioCreateAttributes.sampleRate, 'u32') }}} || undefined + sampleRate: {{{ makeGetValue('options', C_STRUCTS.EmscriptenWebAudioCreateAttributes.sampleRate, 'u32') }}} || undefined, + renderSizeHint: readRenderSizeHint({{{ makeGetValue('options', C_STRUCTS.EmscriptenWebAudioCreateAttributes.renderSizeHint, 'i32') }}}) } : undefined; #if WEBAUDIO_DEBUG diff --git a/src/struct_info.json b/src/struct_info.json index 531d76afd8fd9..04c86495b6c12 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -1269,7 +1269,8 @@ "structs": { "EmscriptenWebAudioCreateAttributes": [ "latencyHint", - "sampleRate" + "sampleRate", + "renderSizeHint" ], "WebAudioParamDescriptor": [ "defaultValue", diff --git a/src/struct_info_generated.json b/src/struct_info_generated.json index e9383d3f2ac77..2292ecd56ae48 100644 --- a/src/struct_info_generated.json +++ b/src/struct_info_generated.json @@ -700,8 +700,9 @@ "visibilityState": 4 }, "EmscriptenWebAudioCreateAttributes": { - "__size__": 8, + "__size__": 12, "latencyHint": 0, + "renderSizeHint": 8, "sampleRate": 4 }, "EmscriptenWebGLContextAttributes": { diff --git a/src/struct_info_generated_wasm64.json b/src/struct_info_generated_wasm64.json index 38d3a40980dd3..545192606f982 100644 --- a/src/struct_info_generated_wasm64.json +++ b/src/struct_info_generated_wasm64.json @@ -702,6 +702,7 @@ "EmscriptenWebAudioCreateAttributes": { "__size__": 16, "latencyHint": 0, + "renderSizeHint": 12, "sampleRate": 8 }, "EmscriptenWebGLContextAttributes": { diff --git a/system/include/emscripten/webaudio.h b/system/include/emscripten/webaudio.h index f8c113a0a13f7..213bc81f7f985 100644 --- a/system/include/emscripten/webaudio.h +++ b/system/include/emscripten/webaudio.h @@ -19,10 +19,16 @@ extern "C" { typedef int EMSCRIPTEN_WEBAUDIO_T; +// Default render size of 128 frames +#define AUDIO_CONTEXT_RENDER_SIZE_DEFAULT 0 +// Let the hardware determine the best render size +#define AUDIO_CONTEXT_RENDER_SIZE_HARDWARE -1 + typedef struct EmscriptenWebAudioCreateAttributes { const char *latencyHint; // Specify one of "balanced", "interactive" or "playback" uint32_t sampleRate; // E.g. 44100 or 48000 + int32_t renderSizeHint; // AUDIO_CONTEXT_RENDER_SIZE_* or number of samples } EmscriptenWebAudioCreateAttributes; // Creates a new Web Audio AudioContext, and returns a handle to it. diff --git a/test/codesize/audio_worklet_wasm.expected.js b/test/codesize/audio_worklet_wasm.expected.js index 24730436b4982..f18e8adf6f120 100644 --- a/test/codesize/audio_worklet_wasm.expected.js +++ b/test/codesize/audio_worklet_wasm.expected.js @@ -1,4 +1,4 @@ -var m = globalThis.Module || "undefined" != typeof Module ? Module : {}, r = !!globalThis.AudioWorkletGlobalScope, t = "em-ww" == globalThis.name || r, u, z, I, J, G, E, w, X, F, D, C, Y, A, Z; +var m = globalThis.Module || "undefined" != typeof Module ? Module : {}, p = !!globalThis.AudioWorkletGlobalScope, t = "em-ww" == globalThis.name || p, u, z, I, J, G, E, w, X, F, D, C, Y, A, Z; function v(a) { u = a; @@ -10,12 +10,12 @@ function v(a) { a.G = a.M = 0; } -t && !r && (onmessage = a => { +t && !p && (onmessage = a => { onmessage = null; v(a.data); }); -if (r) { +if (p) { function a(b) { class h extends AudioWorkletProcessor { constructor(d) { @@ -36,7 +36,7 @@ if (r) { return b; } process(d, g, e) { - var l = d.length, p = g.length, f, q, k = 12 * (l + p), n = 0; + var l = d.length, q = g.length, f, r, k = 12 * (l + q), n = 0; for (f of d) n += f.length; n *= this.s; var H = 0; @@ -53,15 +53,15 @@ if (r) { G[k + 4 >> 2] = this.u; G[k + 8 >> 2] = n; k += 12; - for (q of f) E.set(q, n >> 2), n += this.s; + for (r of f) E.set(r, n >> 2), n += this.s; } d = k; - for (f = 0; q = e[f++]; ) G[k >> 2] = q.length, G[k + 4 >> 2] = n, k += 8, E.set(q, n >> 2), - n += 4 * q.length; + for (f = 0; r = e[f++]; ) G[k >> 2] = r.length, G[k + 4 >> 2] = n, k += 8, E.set(r, n >> 2), + n += 4 * r.length; e = k; for (f of g) G[k >> 2] = f.length, G[k + 4 >> 2] = this.u, G[k + 8 >> 2] = n, k += 12, n += this.s * f.length; - if (l = this.v(l, B, p, e, N, d, this.A)) for (f of g) for (q of f) q.set(this.B[--H]); + if (l = this.v(l, B, q, e, N, d, this.A)) for (f of g) for (r of f) r.set(this.B[--H]); F(U); return !!l; } @@ -129,9 +129,12 @@ var K = [], L = a => { }, T = a => { if (a) { var c = G[a >> 2]; + c = (c ? S(c) : "") || void 0; + var b = J[a + 8 >> 2]; a = { - latencyHint: (c ? S(c) : "") || void 0, - sampleRate: G[a + 4 >> 2] || void 0 + latencyHint: c, + sampleRate: G[a + 4 >> 2] || void 0, + N: 0 > b ? "hardware" : b || "default" }; } else a = void 0; a = new AudioContext(a); @@ -140,10 +143,10 @@ var K = [], L = a => { }, V = (a, c, b, h, d) => { var g = b ? J[b + 4 >> 2] : 0; if (b) { - var e = J[b >> 2], l = G[b + 8 >> 2], p = g; + var e = J[b >> 2], l = G[b + 8 >> 2], q = g; if (l) { l >>= 2; - for (var f = []; p--; ) f.push(G[l++]); + for (var f = []; q--; ) f.push(G[l++]); l = f; } else l = void 0; b = { @@ -156,7 +159,7 @@ var K = [], L = a => { processorOptions: { v: h, A: d, - u: 128 + u: O[a].renderQuantumSize || 128 } }; } else b = void 0; @@ -191,17 +194,17 @@ var K = [], L = a => { if (!e) return l(); e.addModule(m.js).then((() => { e.port || (e.port = { - postMessage: p => { - p._boot ? (e.D = new AudioWorkletNode(g, "em-bootstrap", { - processorOptions: p + postMessage: q => { + q._boot ? (e.D = new AudioWorkletNode(g, "em-bootstrap", { + processorOptions: q }), e.D.port.onmessage = f => { e.port.onmessage(f); - }) : e.D.port.postMessage(p); + }) : e.D.port.postMessage(q); } }); e.port.postMessage({ _boot: 1, - N: ba++, + O: ba++, G: m.wasm, L: w, J: c, @@ -243,7 +246,7 @@ function y() { C = a.n; Y = a.o; A = a.k; - t ? (Y(u.J, u.F), r || (removeEventListener("message", M), K = K.forEach(L), addEventListener("message", L))) : a.i(); + t ? (Y(u.J, u.F), p || (removeEventListener("message", M), K = K.forEach(L), addEventListener("message", L))) : a.i(); t || X(); })); } diff --git a/test/codesize/test_minimal_runtime_code_size_audio_worklet.json b/test/codesize/test_minimal_runtime_code_size_audio_worklet.json index b2092c1fd2632..50ff7beecdafb 100644 --- a/test/codesize/test_minimal_runtime_code_size_audio_worklet.json +++ b/test/codesize/test_minimal_runtime_code_size_audio_worklet.json @@ -1,10 +1,10 @@ { "a.html": 519, "a.html.gz": 357, - "a.js": 4235, - "a.js.gz": 2170, + "a.js": 4309, + "a.js.gz": 2219, "a.wasm": 1329, "a.wasm.gz": 895, - "total": 6083, - "total_gz": 3422 + "total": 6157, + "total_gz": 3471 } diff --git a/test/webaudio/audioworklet_params_mixing.c b/test/webaudio/audioworklet_params_mixing.c index db877cfe653d1..931e3318cd2fd 100644 --- a/test/webaudio/audioworklet_params_mixing.c +++ b/test/webaudio/audioworklet_params_mixing.c @@ -9,10 +9,18 @@ // create variable parameter data sizes, depending on the browser, it's also the // ideal to test audio worklets don't corrupt TLS variables. +// Large render size (approx 42ms) +#define RENDER_SIZE_HINT 2048 + // This needs to be big enough for the stereo output, 2x inputs, 2x params and // the worker stack. To note that different browsers have different stack size // requirement (see notes in process() plus the expansion of the params). +#ifndef RENDER_SIZE_HINT #define AUDIO_STACK_SIZE 6144 +#else +// float bytes * stereo * ins/outs + extra stack +#define AUDIO_STACK_SIZE ((RENDER_SIZE_HINT * 4 * 2 * 5) + 1024) +#endif // Shared file playback and bootstrap #include "audioworklet_test_shared.inc" diff --git a/test/webaudio/audioworklet_test_shared.inc b/test/webaudio/audioworklet_test_shared.inc index 8af562a7c0635..3e2b2d3ab67db 100644 --- a/test/webaudio/audioworklet_test_shared.inc +++ b/test/webaudio/audioworklet_test_shared.inc @@ -95,8 +95,12 @@ int main(void) { assert(workletStack); // Set at least the latency hint to test the attribute setting + // (The render size we take from the calling code if set) EmscriptenWebAudioCreateAttributes attrs = { - .latencyHint = "balanced" + .latencyHint = "balanced", +#ifdef RENDER_SIZE_HINT + .renderSizeHint = RENDER_SIZE_HINT +#endif }; EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(&attrs); emscripten_start_wasm_audio_worklet_thread_async(context, workletStack, AUDIO_STACK_SIZE, getStartCallback(), NULL);