diff --git a/src/lib/libwebaudio.js b/src/lib/libwebaudio.js index 1da3306e37c53..3aeae6f676b4e 100644 --- a/src/lib/libwebaudio.js +++ b/src/lib/libwebaudio.js @@ -186,6 +186,11 @@ var LibraryWebAudio = { assert(stackSize % 16 == 0, `AudioWorklet stack size should be a multiple of 16 bytes! (was ${stackSize} == ${stackSize%16} mod 16)`); assert(!audioContext.audioWorkletInitialized, `emscripten_create_wasm_audio_worklet() was already called for AudioContext ${contextHandle}! Only call this function once per AudioContext`); audioContext.audioWorkletInitialized = 1; +#if PTHREADS + assert(pthreadPtr); +#else + assert(!pthreadPtr); +#endif #endif #if WEBAUDIO_DEBUG diff --git a/system/lib/wasm_worker/audio_worklet.c b/system/lib/wasm_worker/audio_worklet.c index 80cbca2358764..f9c2d0ecbb174 100644 --- a/system/lib/wasm_worker/audio_worklet.c +++ b/system/lib/wasm_worker/audio_worklet.c @@ -14,11 +14,13 @@ // function that adds the _emscripten_get_next_tid() as arg0 void emscripten_start_wasm_audio_worklet_thread_async(EMSCRIPTEN_WEBAUDIO_T audioContext, void *stackLowestAddress, uint32_t stackSize, EmscriptenStartWebAudioWorkletCallback callback, void *userData2) { emscripten_wasm_worker_t wwID = _emscripten_get_next_tid(); - void* pthreadPtr = stackLowestAddress; #ifdef __EMSCRIPTEN_PTHREADS__ + void* pthreadPtr = stackLowestAddress; size_t stackSize_ = stackSize; stackLowestAddress = _emscripten_init_pthread(stackLowestAddress, &stackSize_, wwID); stackSize = stackSize_; +#else + void* pthreadPtr = NULL; #endif _emscripten_create_audio_worklet(wwID, audioContext, stackLowestAddress, stackSize, pthreadPtr, callback, userData2); } diff --git a/test/codesize/audio_worklet_wasm.expected.js b/test/codesize/audio_worklet_wasm.expected.js index 9e7fed0168802..94166f5774a5a 100644 --- a/test/codesize/audio_worklet_wasm.expected.js +++ b/test/codesize/audio_worklet_wasm.expected.js @@ -1,36 +1,36 @@ -var m = globalThis.Module || typeof Module != "undefined" ? Module : {}, r = !!globalThis.AudioWorkletGlobalScope, t = globalThis.name == "em-ww" || r, u, z, J, E, G, I, w, X, F, D, C, Y, A, Z; +var m = globalThis.Module || typeof Module != "undefined" ? Module : {}, r = !!globalThis.WorkerGlobalScope, t = !!globalThis.AudioWorkletGlobalScope, u = globalThis.name == "em-ww" || t, v, A, K, F, H, J, x, W, O, G, E, D, X, C, Z; -function v(a) { - u = a; - w = a.I; - x(); +function w(a) { + v = a; + x = a.I; + y(); m ||= {}; m.wasm = a.H; - y(); + z(); a.H = a.I = 0; } -t && !r && (onmessage = a => { +u && !t && (onmessage = a => { onmessage = null; - v(a.data); + w(a.data); }); -if (r) { +if (t) { function a(c) { class k extends AudioWorkletProcessor { constructor(d) { super(); d = d.processorOptions; - this.A = A.get(d.A); + this.A = C.get(d.A); this.B = d.B; this.u = d.u; this.v = this.u * 4; - this.C = Array(Math.min((u.G - 16) / this.v | 0, 64)); + this.C = Array(Math.min((v.G - 16) / this.v | 0, 64)); this.J(); } J() { - for (var d = C(), g = D(this.C.length * this.v) >> 2, f = this.C.length - 1; f >= 0; f--) this.C[f] = E.subarray(g, g += this.u); - F(d); + for (var d = D(), g = E(this.C.length * this.v) >> 2, f = this.C.length - 1; f >= 0; f--) this.C[f] = F.subarray(g, g += this.u); + G(d); } static get parameterDescriptors() { return c; @@ -39,30 +39,30 @@ if (r) { var l = d.length, n = g.length, e, q, h = (l + n) * 12, p = 0; for (e of d) p += e.length; p *= this.v; - var H = 0; - for (e of g) H += e.length; - p += H * this.v; - var O = 0; - for (e in f) ++O, h += 8, p += f[e].byteLength; - var V = C(), B = h + p + 15 & -16; - h = D(B); + var I = 0; + for (e of g) I += e.length; + p += I * this.v; + var P = 0; + for (e in f) ++P, h += 8, p += f[e].byteLength; + var Y = D(), B = h + p + 15 & -16; + h = E(B); p = h + (B - p); B = h; for (e of d) { - G[h >> 2] = e.length; - G[h + 4 >> 2] = this.u; - G[h + 8 >> 2] = p; + H[h >> 2] = e.length; + H[h + 4 >> 2] = this.u; + H[h + 8 >> 2] = p; h += 12; - for (q of e) E.set(q, p >> 2), p += this.v; + for (q of e) F.set(q, p >> 2), p += this.v; } d = h; - for (e = 0; q = f[e++]; ) G[h >> 2] = q.length, G[h + 4 >> 2] = p, h += 8, E.set(q, p >> 2), + for (e = 0; q = f[e++]; ) H[h >> 2] = q.length, H[h + 4 >> 2] = p, h += 8, F.set(q, p >> 2), p += q.length * 4; f = h; - for (e of g) G[h >> 2] = e.length, G[h + 4 >> 2] = this.u, G[h + 8 >> 2] = p, h += 12, + for (e of g) H[h >> 2] = e.length, H[h + 4 >> 2] = this.u, H[h + 8 >> 2] = p, h += 12, p += this.v * e.length; - if (l = this.A(l, B, n, f, O, d, this.B)) for (e of g) for (q of e) q.set(this.C[--H]); - F(V); + if (l = this.A(l, B, n, f, P, d, this.B)) for (e of g) for (q of e) q.set(this.C[--I]); + G(Y); return !!l; } } @@ -72,50 +72,52 @@ if (r) { class b extends AudioWorkletProcessor { constructor(c) { super(); - v(c.processorOptions); + w(c.processorOptions); port instanceof MessagePort || (this.port.onmessage = port.onmessage, port = this.port); } process() {} } registerProcessor("em-bootstrap", b); port.onmessage = async c => { - await z; + await A; c = c.data; - c._boot ? v(c) : c._wpn ? (registerProcessor(c._wpn, a(c.K)), port.postMessage({ + c._boot ? w(c) : c._wpn ? (registerProcessor(c._wpn, a(c.K)), port.postMessage({ _wsc: c.A, D: [ c.L, 1, c.B ] - })) : c._wsc && A.get(c._wsc)(...c.D); + })) : c._wsc && C.get(c._wsc)(...c.D); }; } -function x() { - var a = w.buffer; - I = new Uint8Array(a); - J = new Int32Array(a); - G = new Uint32Array(a); - E = new Float32Array(a); +function y() { + var a = x.buffer; + J = new Uint8Array(a); + K = new Int32Array(a); + H = new Uint32Array(a); + F = new Float32Array(a); } -t || (w = m.mem || new WebAssembly.Memory({ +u || (x = m.mem || new WebAssembly.Memory({ initial: 256, maximum: 256, shared: !0 -}), x()); +}), y()); -var K = [], L = a => { +var L = [], M = a => { a = a.data; var b = a._wsc; - b && A.get(b)(...a.x); -}, M = a => { - K.push(a); + b && C.get(b)(...a.x); }, N = a => { + L.push(a); +}, aa = () => { + O(0, !r && !t, !u, r && !t); +}, ba = a => { a = a.data; var b = a._wsc; - b && A.get(b)(...a.D); -}, Q = (a, b, c, k, d, g, f) => { - var l = P[b], n = l.audioWorklet; + b && C.get(b)(...a.D); +}, ca = (a, b, c, k, d, g, f) => { + var l = Q[b], n = l.audioWorklet; d = () => { - A.get(g)(b, 0, f); + C.get(g)(b, 0, f); }; if (!n) return d(); n.addModule(m.js).then((() => { @@ -132,19 +134,19 @@ var K = [], L = a => { _boot: 1, N: a, H: m.wasm, - I: w, + I: x, M: c, G: k }); - n.port.onmessage = N; - A.get(g)(b, 1, f); + n.port.onmessage = ba; + C.get(g)(b, 1, f); })).catch(d); -}, R = (a, b, c, k) => { - b = P[b]; - P[a].connect(b.destination || b, c, k); -}, P = {}, S = 0, T = globalThis.TextDecoder && new TextDecoder, U = (a = 0) => { - for (var b = I, c = a, k = c + void 0; b[c] && !(c >= k); ) ++c; - if (c - a > 16 && b.buffer && T) return T.decode(b.slice(a, c)); +}, da = (a, b, c, k) => { + b = Q[b]; + Q[a].connect(b.destination || b, c, k); +}, Q = {}, R = 0, S = globalThis.TextDecoder && new TextDecoder, T = (a = 0) => { + for (var b = J, c = a, k = c + void 0; b[c] && !(c >= k); ) ++c; + if (c - a > 16 && b.buffer && S) return S.decode(b.slice(a, c)); for (k = ""; a < c; ) { var d = b[a++]; if (d & 128) { @@ -157,102 +159,109 @@ var K = [], L = a => { } else k += String.fromCharCode(d); } return k; -}, aa = a => { +}, ea = a => { if (a) { - var b = G[a >> 2]; - b = (b ? U(b) : "") || void 0; - var c = J[a + 8 >> 2]; + var b = H[a >> 2]; + b = (b ? T(b) : "") || void 0; + var c = K[a + 8 >> 2]; a = { latencyHint: b, - sampleRate: G[a + 4 >> 2] || void 0, + sampleRate: H[a + 4 >> 2] || void 0, O: c < 0 ? "hardware" : c || "default" }; } else a = void 0; a = new AudioContext(a); - P[++S] = a; - return S; -}, ba = (a, b, c, k, d) => { - var g = c ? J[c + 4 >> 2] : 0; + Q[++R] = a; + return R; +}, fa = (a, b, c, k, d) => { + var g = c ? K[c + 4 >> 2] : 0; if (c) { - var f = J[c >> 2], l = G[c + 8 >> 2], n = g; + var f = K[c >> 2], l = H[c + 8 >> 2], n = g; if (l) { l >>= 2; - for (var e = []; n--; ) e.push(G[l++]); + for (var e = []; n--; ) e.push(H[l++]); l = e; } else l = void 0; c = { numberOfInputs: f, numberOfOutputs: g, outputChannelCount: l, - channelCount: G[c + 12 >> 2] || void 0, - channelCountMode: [ , "clamped-max", "explicit" ][J[c + 16 >> 2]], - channelInterpretation: [ , "discrete" ][J[c + 20 >> 2]], + channelCount: H[c + 12 >> 2] || void 0, + channelCountMode: [ , "clamped-max", "explicit" ][K[c + 16 >> 2]], + channelInterpretation: [ , "discrete" ][K[c + 20 >> 2]], processorOptions: { A: k, B: d, - u: P[a].renderQuantumSize || 128 + u: Q[a].renderQuantumSize || 128 } }; } else c = void 0; - a = new AudioWorkletNode(P[a], b ? U(b) : "", c); - P[++S] = a; - return S; -}, ca = (a, b, c, k) => { - var d = (d = G[b >> 2]) ? U(d) : "", g = J[b + 4 >> 2]; - b = G[b + 8 >> 2]; + a = new AudioWorkletNode(Q[a], b ? T(b) : "", c); + Q[++R] = a; + return R; +}, ha = (a, b, c, k) => { + var d = (d = H[b >> 2]) ? T(d) : "", g = K[b + 4 >> 2]; + b = H[b + 8 >> 2]; for (var f = [], l = 0; g--; ) f.push({ name: l++, - defaultValue: E[b >> 2], - minValue: E[b + 4 >> 2], - maxValue: E[b + 8 >> 2], - automationRate: (J[b + 12 >> 2] ? "k" : "a") + "-rate" + defaultValue: F[b >> 2], + minValue: F[b + 4 >> 2], + maxValue: F[b + 8 >> 2], + automationRate: (K[b + 12 >> 2] ? "k" : "a") + "-rate" }), b += 16; - P[a].audioWorklet.port.postMessage({ + Q[a].audioWorklet.port.postMessage({ _wpn: d, K: f, L: a, A: c, B: k }); -}, da = () => !1, ea = () => r ? () => 138 : a => (a.set(crypto.getRandomValues(new Uint8Array(a.byteLength))), -0), W = a => (W = ea())(a), fa = (a, b) => W(I.subarray(a, a + b)); +}, U = globalThis.performance?.now ? () => performance.now() : Date.now, ia = a => { + a = [ a ? T(a) : "" ]; + return console.log(...a); +}, ja = () => !1, ka = () => t ? () => 138 : a => (a.set(crypto.getRandomValues(new Uint8Array(a.byteLength))), +0), V = a => (V = ka())(a), la = (a, b) => V(J.subarray(a, a + b)); -function ha(a) { +function ma(a) { var b = document.createElement("button"); b.innerHTML = "Toggle playback"; document.body.appendChild(b); - a = P[a]; + a = Q[a]; b.onclick = () => { a.state != "running" ? a.resume() : a.suspend(); }; } -function y() { +function z() { Z = { - g: ha, - c: Q, - h: R, + i: ma, e: aa, - i: ba, - f: ca, - b: da, - a: w, - d: fa + d: ca, + k: da, + g: ea, + l: fa, + h: ha, + b: U, + c: ia, + j: ja, + a: x, + f: la }; - z = WebAssembly.instantiate(m.wasm, { + A = WebAssembly.instantiate(m.wasm, { a: Z }).then((a => { a = (a.instance || a).exports; - X = a.k; - F = a.m; - D = a.n; + W = a.n; + O = a.p; + G = a.q; + E = a.r; + D = a.s; + X = a.t; C = a.o; - Y = a.p; - A = a.l; - t ? (Y(u.N, u.M, u.G), r || (removeEventListener("message", M), K = K.forEach(L), - addEventListener("message", L))) : a.j(); - t || X(); + u ? (X(v.N, v.M, v.G), t || (removeEventListener("message", N), L = L.forEach(M), + addEventListener("message", M))) : a.m(); + u || W(); })); } -t || y(); \ No newline at end of file +u || z(); \ No newline at end of file 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 03fa3d3ab4fda..408b66eab7fa1 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": 515, "a.html.gz": 355, - "a.js": 4448, - "a.js.gz": 2289, - "a.wasm": 1409, - "a.wasm.gz": 936, - "total": 6372, - "total_gz": 3580 + "a.js": 4647, + "a.js.gz": 2380, + "a.wasm": 11064, + "a.wasm.gz": 6028, + "total": 16226, + "total_gz": 8763 } diff --git a/test/webaudio/audioworklet.c b/test/webaudio/audioworklet.c index 228b7e5343c99..ce7ff6c2ef479 100644 --- a/test/webaudio/audioworklet.c +++ b/test/webaudio/audioworklet.c @@ -1,3 +1,5 @@ +#define _GNU_SOURCE // for gettid +#include #include #include #include @@ -31,6 +33,9 @@ _Thread_local int testTlsVariable = 1; int lastTlsVariableValueInAudioThread = 1; #endif +bool worklet_started = false; +pid_t main_tid = 0; + // This function will be called for every fixed-size buffer of audio samples to be processed. bool ProcessAudio(int numInputs, const AudioSampleFrame* inputs, @@ -41,6 +46,17 @@ bool ProcessAudio(int numInputs, void* userData) { assert(!emscripten_is_main_browser_thread()); assert(emscripten_current_thread_is_audio_worklet()); + if (!worklet_started) { + worklet_started = true; + pid_t worklet_tid = gettid(); + emscripten_outf("worklet thread tid: %d\n", worklet_tid); + assert(worklet_tid && worklet_tid > main_tid); +#ifdef __EMSCRIPTEN_PTHREADS__ + emscripten_outf("worklet thread pthread_self: %p\n", pthread_self()); + assert(pthread_self()); +#endif + } + #ifdef TEST_AND_EXIT // Only running in the test harness, see main_thread_tls_access() assert(testTlsVariable == lastTlsVariableValueInAudioThread); @@ -150,6 +166,14 @@ uint8_t wasmAudioWorkletStack[4096]; int main() { assert(emscripten_is_main_browser_thread()); assert(!emscripten_current_thread_is_audio_worklet()); + main_tid = gettid(); + emscripten_outf("main thread tid: %d\n", main_tid); + emscripten_outf("wasmAudioWorkletStack: %p\n", wasmAudioWorkletStack); + assert(main_tid); +#ifdef __EMSCRIPTEN_PTHREADS__ + emscripten_outf("main thread pthread_self: %p\n", pthread_self()); + assert(pthread_self()); +#endif // Create an audio context context = emscripten_create_audio_context(0 /* use default constructor options */);