Add testing for audio worklets + pthreads. NFC (#26982)
This should have been part of #25930
diff --git a/src/lib/libwebaudio.js b/src/lib/libwebaudio.js
index 1da3306..3aeae6f 100644
--- a/src/lib/libwebaudio.js
+++ b/src/lib/libwebaudio.js
@@ -186,6 +186,11 @@
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 80cbca2..f9c2d0e 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 9e7fed0..94166f5 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 @@
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 @@
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 @@
_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 @@
} 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 03fa3d3..408b66e 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 228b7e5..ce7ff6c 100644
--- a/test/webaudio/audioworklet.c
+++ b/test/webaudio/audioworklet.c
@@ -1,3 +1,5 @@
+#define _GNU_SOURCE // for gettid
+#include <emscripten/console.h>
#include <emscripten/webaudio.h>
#include <emscripten/threading.h>
#include <assert.h>
@@ -31,6 +33,9 @@
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 @@
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 @@
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 */);