Skip to content

Commit f7f6395

Browse files
authored
[test] Add audio worklet parameter tests (and tidy other interactive tests) (#23659)
This is splits out the test changes from #23508 adding an interactive parameter test: ``` test/runner interactive.test_audio_worklet_params_mixing ``` This test is also added to the browser tests now #23665 has merged, offering a single test that touches all the various structs and their offsets. When run as part of the `browser` tests it plays for 1 second, for interactive without the `TEST_AND_EXIT` macro it will play continuously. It also tidies a little the shared code (and related tests) in the process. Interactive tests had `_2gb` and `_4gb` options added (which will fail until #23508 lands, but they're not part of the CI). All features of these tests as interactive now work fully on Chrome, Firefox and Safari (Safari has issues with looping, but it doesn't affect the spirit of the test).
1 parent ee520f0 commit f7f6395

8 files changed

+342
-73
lines changed

test/test_browser.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5500,17 +5500,21 @@ def test_audio_worklet_post_function(self, args):
55005500
def test_audio_worklet_modularize(self, args):
55015501
self.btest_exit('webaudio/audioworklet.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-sMODULARIZE=1', '-sEXPORT_NAME=MyModule', '--shell-file', test_file('shell_that_launches_modularize.html'), '-DTEST_AND_EXIT'] + args)
55025502

5503-
# Tests multiple inputs, forcing a larger stack (note: passing BROWSER_TEST is
5504-
# specific to this test to allow it to exit rather than play forever).
5503+
# Tests an AudioWorklet with multiple stereo inputs mixing in the processor
5504+
# via a varying parameter to a single stereo output (touching all of the API
5505+
# copying from structs)
55055506
@parameterized({
55065507
'': ([],),
55075508
'minimal_with_closure': (['-sMINIMAL_RUNTIME', '--closure=1', '-Oz'],),
55085509
})
5509-
def test_audio_worklet_stereo_io(self, args):
5510+
@no_wasm64('https://github.com/emscripten-core/emscripten/pull/23508')
5511+
@no_2gb('https://github.com/emscripten-core/emscripten/pull/23508')
5512+
@requires_sound_hardware
5513+
def test_audio_worklet_params_mixing(self, args):
55105514
os.mkdir('audio_files')
55115515
shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/')
55125516
shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/')
5513-
self.btest_exit('webaudio/audioworklet_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DBROWSER_TEST'] + args)
5517+
self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT'] + args)
55145518

55155519
# Tests AudioWorklet with emscripten_futex_wake().
55165520
@requires_sound_hardware

test/test_interactive.py

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,10 +329,32 @@ def test_audio_worklet_2x_hard_pan_io(self):
329329
shutil.copy(test_file('webaudio/audio_files/emscripten-bass-mono.mp3'), 'audio_files/')
330330
self.btest_exit('webaudio/audioworklet_2x_in_hard_pan.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'])
331331

332+
# Tests an AudioWorklet with multiple stereo inputs mixing in the processor via a parameter to a single stereo output (6kB stack)
333+
def test_audio_worklet_params_mixing(self):
334+
os.mkdir('audio_files')
335+
shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/')
336+
shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/')
337+
self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS'])
338+
332339

333340
class interactive64(interactive):
334341
def setUp(self):
335342
super().setUp()
336343
self.set_setting('MEMORY64')
337-
self.emcc_args.append('-Wno-experimental')
338344
self.require_wasm64()
345+
346+
347+
class interactive64_4gb(interactive):
348+
def setUp(self):
349+
super().setUp()
350+
self.set_setting('MEMORY64')
351+
self.set_setting('INITIAL_MEMORY', '4200mb')
352+
self.set_setting('GLOBAL_BASE', '4gb')
353+
self.require_wasm64()
354+
355+
356+
class interactive_2gb(interactive):
357+
def setUp(self):
358+
super().setUp()
359+
self.set_setting('INITIAL_MEMORY', '2200mb')
360+
self.set_setting('GLOBAL_BASE', '2gb')

test/webaudio/audioworklet_2x_in_hard_pan.c

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include <assert.h>
22
#include <string.h>
3-
#include <stdio.h>
43

54
#include <emscripten/em_js.h>
65
#include <emscripten/webaudio.h>
@@ -16,30 +15,41 @@
1615

1716
// Callback to process and copy the audio tracks
1817
bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) {
18+
#ifdef TEST_AND_EXIT
1919
audioProcessedCount++;
20+
#endif
2021

21-
// Twin mono in, single stereo out
22+
// Twin mono in (or disabled), single stereo out
2223
assert(numInputs == 2 && numOutputs == 1);
23-
assert(inputs[0].numberOfChannels == 1 && inputs[1].numberOfChannels == 1);
24+
assert(inputs[0].numberOfChannels == 0 || inputs[0].numberOfChannels == 1);
25+
assert(inputs[1].numberOfChannels == 0 || inputs[1].numberOfChannels == 1);
2426
assert(outputs[0].numberOfChannels == 2);
2527
// All with the same number of samples
2628
assert(inputs[0].samplesPerChannel == inputs[1].samplesPerChannel);
2729
assert(inputs[0].samplesPerChannel == outputs[0].samplesPerChannel);
28-
// Now with all known quantities we can memcpy the data
29-
int samplesPerChannel = inputs[0].samplesPerChannel;
30-
memcpy(outputs[0].data, inputs[0].data, samplesPerChannel * sizeof(float));
31-
memcpy(outputs[0].data + samplesPerChannel, inputs[1].data, samplesPerChannel * sizeof(float));
30+
// Now with all known quantities we can memcpy the L&R data (or zero it if the
31+
// channels are disabled)
32+
int bytesPerChannel = outputs[0].samplesPerChannel * sizeof(float);
33+
float* outputData = outputs[0].data;
34+
if (inputs[0].numberOfChannels > 0) {
35+
memcpy(outputData, inputs[0].data, bytesPerChannel);
36+
} else {
37+
memset(outputData, 0, bytesPerChannel);
38+
}
39+
outputData += outputs[0].samplesPerChannel;
40+
if (inputs[1].numberOfChannels > 0) {
41+
memcpy(outputData, inputs[1].data, bytesPerChannel);
42+
} else {
43+
memset(outputData, 0, bytesPerChannel);
44+
}
3245
return true;
3346
}
3447

3548
// Audio processor created, now register the audio callback
3649
void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
37-
if (!success) {
38-
printf("Audio worklet node creation failed\n");
39-
return;
40-
}
41-
printf("Audio worklet processor created\n");
42-
printf("Click to toggle audio playback\n");
50+
assert(success && "Audio worklet failed in processorCreated()");
51+
emscripten_out("Audio worklet processor created");
52+
emscripten_out("Click to toggle audio playback");
4353

4454
// Stereo output, two inputs
4555
int outputChannelCounts[2] = { 2 };
@@ -65,6 +75,13 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
6575
// Register a click to start playback
6676
emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(context), false, &onClick);
6777

68-
// Register the counter that exits the test after one second of mixing
78+
#ifdef TEST_AND_EXIT
79+
// Register the counter that exits the test after one second of playback
6980
emscripten_set_timeout_loop(&playedAndMixed, 16, NULL);
81+
#endif
82+
}
83+
84+
// This implementation has no custom start-up requirements
85+
EmscriptenStartWebAudioWorkletCallback getStartCallback(void) {
86+
return &initialised;
7087
}

test/webaudio/audioworklet_2x_in_out_stereo.c

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include <assert.h>
22
#include <string.h>
3-
#include <stdio.h>
43

54
#include <emscripten/em_js.h>
65
#include <emscripten/webaudio.h>
@@ -15,31 +14,41 @@
1514

1615
// Callback to process and copy the audio tracks
1716
bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) {
17+
#ifdef TEST_AND_EXIT
1818
audioProcessedCount++;
19+
#endif
1920

2021
// Twin stereo in and out
2122
assert(numInputs == 2 && numOutputs == 2);
22-
assert(inputs[0].numberOfChannels == 2 && inputs[1].numberOfChannels == 2);
23-
assert(outputs[0].numberOfChannels == 2 && outputs[1].numberOfChannels == 2);
23+
assert(inputs[0].numberOfChannels == 0 || inputs[0].numberOfChannels == 2);
24+
assert(inputs[1].numberOfChannels == 0 || inputs[1].numberOfChannels == 2);
25+
assert(outputs[0].numberOfChannels == 2);
26+
assert(outputs[1].numberOfChannels == 2);
2427
// All with the same number of samples
2528
assert(inputs[0].samplesPerChannel == inputs[1].samplesPerChannel);
2629
assert(inputs[0].samplesPerChannel == outputs[0].samplesPerChannel);
2730
assert(outputs[0].samplesPerChannel == outputs[1].samplesPerChannel);
28-
// Now with all known quantities we can memcpy the data
29-
int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels;
30-
memcpy(outputs[0].data, inputs[0].data, totalSamples * sizeof(float));
31-
memcpy(outputs[1].data, inputs[1].data, totalSamples * sizeof(float));
31+
// Now with all known quantities we can memcpy all the data (or zero it if the
32+
// channels are disabled)
33+
int totalBytes = outputs[0].samplesPerChannel * outputs[0].numberOfChannels * sizeof(float);
34+
if (inputs[0].numberOfChannels > 0) {
35+
memcpy(outputs[0].data, inputs[0].data, totalBytes);
36+
} else {
37+
memset(outputs[0].data, 0, totalBytes);
38+
}
39+
if (inputs[1].numberOfChannels > 0) {
40+
memcpy(outputs[1].data, inputs[1].data, totalBytes);
41+
} else {
42+
memset(outputs[1].data, 0, totalBytes);
43+
}
3244
return true;
3345
}
3446

3547
// Audio processor created, now register the audio callback
3648
void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
37-
if (!success) {
38-
printf("Audio worklet node creation failed\n");
39-
return;
40-
}
41-
printf("Audio worklet processor created\n");
42-
printf("Click to toggle audio playback\n");
49+
assert(success && "Audio worklet failed in processorCreated()");
50+
emscripten_out("Audio worklet processor created");
51+
emscripten_out("Click to toggle audio playback");
4352

4453
// Two stereo outputs, two inputs
4554
int outputChannelCounts[2] = { 2, 2 };
@@ -67,6 +76,13 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
6776
// Register a click to start playback
6877
emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(context), false, &onClick);
6978

70-
// Register the counter that exits the test after one second of mixing
79+
#ifdef TEST_AND_EXIT
80+
// Register the counter that exits the test after one second of playback
7181
emscripten_set_timeout_loop(&playedAndMixed, 16, NULL);
82+
#endif
83+
}
84+
85+
// This implementation has no custom start-up requirements
86+
EmscriptenStartWebAudioWorkletCallback getStartCallback(void) {
87+
return &initialised;
7288
}

test/webaudio/audioworklet_in_out_mono.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include <assert.h>
22
#include <string.h>
3-
#include <stdio.h>
43

54
#include <emscripten/em_js.h>
65
#include <emscripten/webaudio.h>
@@ -16,7 +15,9 @@
1615

1716
// Callback to process and mix the audio tracks
1817
bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) {
18+
#ifdef TEST_AND_EXIT
1919
audioProcessedCount++;
20+
#endif
2021

2122
// Single mono output
2223
assert(numOutputs == 1 && outputs[0].numberOfChannels == 1);
@@ -29,11 +30,18 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi
2930
// We can now do a quick mix since we know the layouts
3031
if (numInputs > 0) {
3132
int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels;
33+
// Simple copy of the first input's audio data, checking that we have
34+
// channels (since a muted input has zero channels).
3235
float* outputData = outputs[0].data;
33-
memcpy(outputData, inputs[0].data, totalSamples * sizeof(float));
36+
if (inputs[0].numberOfChannels > 0) {
37+
memcpy(outputData, inputs[0].data, totalSamples * sizeof(float));
38+
} else {
39+
// And for muted we need to fill the buffer with zeroes otherwise it repeats the last frame
40+
memset(outputData, 0, totalSamples * sizeof(float));
41+
}
42+
// Now add another inputs
3443
for (int n = 1; n < numInputs; n++) {
35-
// It's possible to have an input with no channels
36-
if (inputs[n].numberOfChannels == 1) {
44+
if (inputs[n].numberOfChannels > 0) {
3745
float* inputData = inputs[n].data;
3846
for (int i = totalSamples - 1; i >= 0; i--) {
3947
outputData[i] += inputData[i];
@@ -46,12 +54,9 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi
4654

4755
// Audio processor created, now register the audio callback
4856
void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
49-
if (!success) {
50-
printf("Audio worklet node creation failed\n");
51-
return;
52-
}
53-
printf("Audio worklet processor created\n");
54-
printf("Click to toggle audio playback\n");
57+
assert(success && "Audio worklet failed in processorCreated()");
58+
emscripten_out("Audio worklet processor created");
59+
emscripten_out("Click to toggle audio playback");
5560

5661
// Mono output, two inputs
5762
int outputChannelCounts[1] = { 1 };
@@ -77,6 +82,13 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
7782
// Register a click to start playback
7883
emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(context), false, &onClick);
7984

85+
#ifdef TEST_AND_EXIT
8086
// Register the counter that exits the test after one second of mixing
8187
emscripten_set_timeout_loop(&playedAndMixed, 16, NULL);
88+
#endif
89+
}
90+
91+
// This implementation has no custom start-up requirements
92+
EmscriptenStartWebAudioWorkletCallback getStartCallback(void) {
93+
return &initialised;
8294
}

test/webaudio/audioworklet_in_out_stereo.c

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
#include <assert.h>
22
#include <string.h>
3-
#include <stdio.h>
43

54
#include <emscripten/em_js.h>
65
#include <emscripten/webaudio.h>
@@ -16,7 +15,9 @@
1615

1716
// Callback to process and mix the audio tracks
1817
bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) {
18+
#ifdef TEST_AND_EXIT
1919
audioProcessedCount++;
20+
#endif
2021

2122
// Single stereo output
2223
assert(numOutputs == 1 && outputs[0].numberOfChannels == 2);
@@ -29,11 +30,18 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi
2930
// We can now do a quick mix since we know the layouts
3031
if (numInputs > 0) {
3132
int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels;
33+
// Simple copy of the first input's audio data, checking that we have
34+
// channels (since a muted input has zero channels).
3235
float* outputData = outputs[0].data;
33-
memcpy(outputData, inputs[0].data, totalSamples * sizeof(float));
36+
if (inputs[0].numberOfChannels > 0) {
37+
memcpy(outputData, inputs[0].data, totalSamples * sizeof(float));
38+
} else {
39+
// And for muted we need to fill the buffer with zeroes otherwise it repeats the last frame
40+
memset(outputData, 0, totalSamples * sizeof(float));
41+
}
42+
// Now add another inputs
3443
for (int n = 1; n < numInputs; n++) {
35-
// It's possible to have an input with no channels
36-
if (inputs[n].numberOfChannels == 2) {
44+
if (inputs[n].numberOfChannels > 0) {
3745
float* inputData = inputs[n].data;
3846
for (int i = totalSamples - 1; i >= 0; i--) {
3947
outputData[i] += inputData[i];
@@ -46,12 +54,9 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi
4654

4755
// Audio processor created, now register the audio callback
4856
void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
49-
if (!success) {
50-
printf("Audio worklet node creation failed\n");
51-
return;
52-
}
53-
printf("Audio worklet processor created\n");
54-
printf("Click to toggle audio playback\n");
57+
assert(success && "Audio worklet failed in processorCreated()");
58+
emscripten_out("Audio worklet processor created");
59+
emscripten_out("Click to toggle audio playback");
5560

5661
// Stereo output, two inputs
5762
int outputChannelCounts[1] = { 2 };
@@ -77,6 +82,13 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) {
7782
// Register a click to start playback
7883
emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(context), false, &onClick);
7984

85+
#ifdef TEST_AND_EXIT
8086
// Register the counter that exits the test after one second of mixing
8187
emscripten_set_timeout_loop(&playedAndMixed, 16, NULL);
88+
#endif
89+
}
90+
91+
// This implementation has no custom start-up requirements
92+
EmscriptenStartWebAudioWorkletCallback getStartCallback(void) {
93+
return &initialised;
8294
}

0 commit comments

Comments
 (0)