Skip to content

Commit 392fede

Browse files
committed
Added two audio ins to two audio outs test
1 parent 55e70fa commit 392fede

File tree

2 files changed

+158
-0
lines changed

2 files changed

+158
-0
lines changed

test/test_interactive.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,13 @@ def test_audio_worklet_stereo_io(self):
313313
shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/')
314314
self.btest('webaudio/audioworklet_in_out_stereo.c', expected='0', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'])
315315

316+
# Tests an AudioWorklet with multiple stereo inputs copying in the processor to multiple stereo outputs
317+
def test_audio_worklet_2x_stereo_io(self):
318+
os.mkdir('audio_files')
319+
shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/')
320+
shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/')
321+
self.btest('webaudio/audioworklet_2x_in_out_stereo.c', expected='0', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'])
322+
316323

317324
class interactive64(interactive):
318325
def setUp(self):
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#include <assert.h>
2+
#include <string.h>
3+
#include <stdio.h>
4+
5+
#include <emscripten/em_js.h>
6+
#include <emscripten/webaudio.h>
7+
8+
// Tests processing two stereo audio inputs being copied to two stereo outputs.
9+
10+
// This needs to be big enough for the 2x stereo outputs, 2x inputs and the worker stack
11+
#define AUDIO_STACK_SIZE 6144
12+
13+
// REPORT_RESULT is defined when running in Emscripten test harness.
14+
#ifdef REPORT_RESULT
15+
// Count the mixed frames and return after 375 frames (1 second with the default 128 size)
16+
volatile int audioProcessedCount = 0;
17+
bool playedAndMixed(double time, void* data) {
18+
if (audioProcessedCount >= 375) {
19+
REPORT_RESULT(0);
20+
return false;
21+
}
22+
return true;
23+
}
24+
#endif
25+
26+
// ID to the beat and bass loops
27+
EMSCRIPTEN_WEBAUDIO_T beatID = 0;
28+
EMSCRIPTEN_WEBAUDIO_T bassID = 0;
29+
30+
// Creates a MediaElementAudioSourceNode with the supplied URL (which is
31+
// registered as an internal audio object and the ID returned).
32+
EM_JS(EMSCRIPTEN_WEBAUDIO_T, createTrack, (EMSCRIPTEN_WEBAUDIO_T ctxID, const char* url, bool looping), {
33+
var context = emscriptenGetAudioObject(ctxID);
34+
if (context) {
35+
var audio = document.createElement('audio');
36+
audio.src = UTF8ToString(url);
37+
audio.loop = looping;
38+
var track = context.createMediaElementSource(audio);
39+
return emscriptenRegisterAudioObject(track);
40+
}
41+
return 0;
42+
});
43+
44+
// Toggles the play/pause of a MediaElementAudioSourceNode given its ID
45+
EM_JS(void, toggleTrack, (EMSCRIPTEN_WEBAUDIO_T srcID), {
46+
var source = emscriptenGetAudioObject(srcID);
47+
if (source) {
48+
var audio = source.mediaElement;
49+
if (audio) {
50+
if (audio.paused) {
51+
audio.currentTime = 0;
52+
audio.play();
53+
} else {
54+
audio.pause();
55+
}
56+
}
57+
}
58+
});
59+
60+
// Callback to process and copy the audio tracks
61+
bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) {
62+
#ifdef REPORT_RESULT
63+
audioProcessedCount++;
64+
#endif
65+
// Twin stereo in and out
66+
assert(numInputs == 2 && numOutputs == 2);
67+
assert(inputs[0].numberOfChannels == 2 && inputs[1].numberOfChannels == 2);
68+
assert(outputs[0].numberOfChannels == 2 && outputs[1].numberOfChannels == 2);
69+
// All with the same number of samples
70+
assert(inputs[0].samplesPerChannel == inputs[1].samplesPerChannel);
71+
assert(inputs[0].samplesPerChannel == outputs[0].samplesPerChannel);
72+
assert(outputs[0].samplesPerChannel == outputs[1].samplesPerChannel);
73+
// Now with all known quantities we can memcpy the data
74+
int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels;
75+
memcpy(outputs[0].data, inputs[0].data, totalSamples * sizeof(float));
76+
memcpy(outputs[1].data, inputs[1].data, totalSamples * sizeof(float));
77+
return true;
78+
}
79+
80+
// Registered click even to (1) enable audio playback and (2) toggle playing the tracks
81+
bool onClick(int type, const EmscriptenMouseEvent* e, void* data) {
82+
EMSCRIPTEN_WEBAUDIO_T ctx = (EMSCRIPTEN_WEBAUDIO_T) (data);
83+
if (emscripten_audio_context_state(ctx) != AUDIO_CONTEXT_STATE_RUNNING) {
84+
printf("Resuming playback\n");
85+
emscripten_resume_audio_context_sync(ctx);
86+
}
87+
printf("Toggling audio playback\n");
88+
toggleTrack(beatID);
89+
toggleTrack(bassID);
90+
return false;
91+
}
92+
93+
// Audio processor created, now register the audio callback
94+
void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
95+
if (success) {
96+
printf("Audio worklet processor created\n");
97+
printf("Click to toggle audio playback\n");
98+
99+
// Two stereo outputs, two inputs
100+
int outputChannelCounts[2] = { 2, 2 };
101+
EmscriptenAudioWorkletNodeCreateOptions opts = {
102+
.numberOfInputs = 2,
103+
.numberOfOutputs = 2,
104+
.outputChannelCounts = outputChannelCounts
105+
};
106+
EMSCRIPTEN_AUDIO_WORKLET_NODE_T worklet = emscripten_create_wasm_audio_worklet_node(context, "mixer", &opts, &process, NULL);
107+
// Both outputs connected to the context
108+
emscripten_audio_node_connect(worklet, context, 0, 0);
109+
emscripten_audio_node_connect(worklet, context, 1, 0);
110+
111+
// Create the two stereo source nodes and connect them to the two inputs
112+
// Note: we can connect the sources to the same input and it'll get mixed for us, but that's not the point
113+
beatID = createTrack(context, "audio_files/emscripten-beat.mp3", true);
114+
if (beatID) {
115+
emscripten_audio_node_connect(beatID, worklet, 0, 0);
116+
}
117+
bassID = createTrack(context, "audio_files/emscripten-bass.mp3", true);
118+
if (bassID) {
119+
emscripten_audio_node_connect(bassID, worklet, 0, 1);
120+
}
121+
122+
emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, (void*) (context), false, &onClick);
123+
124+
#ifdef REPORT_RESULT
125+
emscripten_set_timeout_loop(&playedAndMixed, 16, NULL);
126+
#endif
127+
} else {
128+
printf("Audio worklet node creation failed\n");
129+
}
130+
}
131+
132+
// Worklet thread inited, now create the audio processor
133+
void initialised(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
134+
if (success) {
135+
printf("Audio worklet initialised\n");
136+
137+
WebAudioWorkletProcessorCreateOptions opts = {
138+
.name = "mixer",
139+
};
140+
emscripten_create_wasm_audio_worklet_processor_async(context, &opts, &processorCreated, NULL);
141+
} else {
142+
printf("Audio worklet failed to initialise\n");
143+
}
144+
}
145+
146+
int main() {
147+
static char workletStack[AUDIO_STACK_SIZE];
148+
EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(NULL);
149+
emscripten_start_wasm_audio_worklet_thread_async(context, workletStack, sizeof workletStack, &initialised, NULL);
150+
return 0;
151+
}

0 commit comments

Comments
 (0)