From 0384faddc36f483f687cbe03f5f2c693ba2a3b62 Mon Sep 17 00:00:00 2001 From: Carl Woffenden Date: Wed, 12 Feb 2025 11:05:56 +0100 Subject: [PATCH 1/5] Split out the test changes from #23508 This mostly adds an Audio Worklet parameter test, but also tidies a little the related tests and shared code. --- test/test_interactive.py | 24 ++- test/webaudio/audioworklet_2x_in_hard_pan.c | 33 +++- test/webaudio/audioworklet_2x_in_out_stereo.c | 32 +++- test/webaudio/audioworklet_in_out_mono.c | 23 ++- test/webaudio/audioworklet_in_out_stereo.c | 22 ++- test/webaudio/audioworklet_params_mixing.c | 177 ++++++++++++++++++ test/webaudio/audioworklet_test_shared.inc | 41 ++-- 7 files changed, 314 insertions(+), 38 deletions(-) create mode 100644 test/webaudio/audioworklet_params_mixing.c diff --git a/test/test_interactive.py b/test/test_interactive.py index 78de7771cd5f7..e25bd12f4f9b2 100644 --- a/test/test_interactive.py +++ b/test/test_interactive.py @@ -334,10 +334,32 @@ def test_audio_worklet_2x_hard_pan_io(self): shutil.copy(test_file('webaudio/audio_files/emscripten-bass-mono.mp3'), 'audio_files/') self.btest_exit('webaudio/audioworklet_2x_in_hard_pan.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) + # Tests an AudioWorklet with multiple stereo inputs mixing in the processor via a parameter to a single stereo output (6kB stack) + def test_audio_worklet_params_mixing(self): + os.mkdir('audio_files') + shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/') + shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/') + self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) + class interactive64(interactive): def setUp(self): super().setUp() self.set_setting('MEMORY64') - self.emcc_args.append('-Wno-experimental') self.require_wasm64() + + +class interactive64_4gb(interactive): + def setUp(self): + super().setUp() + self.set_setting('MEMORY64') + self.set_setting('INITIAL_MEMORY', '4200mb') + self.set_setting('GLOBAL_BASE', '4gb') + self.require_wasm64() + + +class interactive_2gb(interactive): + def setUp(self): + super().setUp() + self.set_setting('INITIAL_MEMORY', '2200mb') + self.set_setting('GLOBAL_BASE', '2gb') diff --git a/test/webaudio/audioworklet_2x_in_hard_pan.c b/test/webaudio/audioworklet_2x_in_hard_pan.c index 682a6ec053cef..eec8e5aac32fb 100644 --- a/test/webaudio/audioworklet_2x_in_hard_pan.c +++ b/test/webaudio/audioworklet_2x_in_hard_pan.c @@ -15,25 +15,37 @@ #include "audioworklet_test_shared.inc" // Callback to process and copy the audio tracks -bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) { +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { audioProcessedCount++; - // Twin mono in, single stereo out + // Twin mono in (or disabled), single stereo out assert(numInputs == 2 && numOutputs == 1); - assert(inputs[0].numberOfChannels == 1 && inputs[1].numberOfChannels == 1); + assert(inputs[0].numberOfChannels == 0 || inputs[0].numberOfChannels == 1); + assert(inputs[1].numberOfChannels == 0 || inputs[1].numberOfChannels == 1); assert(outputs[0].numberOfChannels == 2); // All with the same number of samples assert(inputs[0].samplesPerChannel == inputs[1].samplesPerChannel); assert(inputs[0].samplesPerChannel == outputs[0].samplesPerChannel); - // Now with all known quantities we can memcpy the data - int samplesPerChannel = inputs[0].samplesPerChannel; - memcpy(outputs[0].data, inputs[0].data, samplesPerChannel * sizeof(float)); - memcpy(outputs[0].data + samplesPerChannel, inputs[1].data, samplesPerChannel * sizeof(float)); + // Now with all known quantities we can memcpy the L&R data (or zero it if the + // channels are disabled) + int bytesPerChannel = outputs[0].samplesPerChannel * sizeof(float); + float* outputData = outputs[0].data; + if (inputs[0].numberOfChannels > 0) { + memcpy(outputData, inputs[0].data, bytesPerChannel); + } else { + memset(outputData, 0, bytesPerChannel); + } + outputData += outputs[0].samplesPerChannel; + if (inputs[1].numberOfChannels > 0) { + memcpy(outputData, inputs[1].data, bytesPerChannel); + } else { + memset(outputData, 0, bytesPerChannel); + } return true; } // Audio processor created, now register the audio callback -void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { printf("Audio worklet node creation failed\n"); return; @@ -68,3 +80,8 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { // Register the counter that exits the test after one second of mixing emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); } + +// This implementation has no custom start-up requirements +EmscriptenStartWebAudioWorkletCallback getStartCallback(void) { + return &initialised; +} diff --git a/test/webaudio/audioworklet_2x_in_out_stereo.c b/test/webaudio/audioworklet_2x_in_out_stereo.c index eb51bcdaa9db7..a4865a9b3d028 100644 --- a/test/webaudio/audioworklet_2x_in_out_stereo.c +++ b/test/webaudio/audioworklet_2x_in_out_stereo.c @@ -14,26 +14,37 @@ #include "audioworklet_test_shared.inc" // Callback to process and copy the audio tracks -bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) { +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { audioProcessedCount++; // Twin stereo in and out assert(numInputs == 2 && numOutputs == 2); - assert(inputs[0].numberOfChannels == 2 && inputs[1].numberOfChannels == 2); - assert(outputs[0].numberOfChannels == 2 && outputs[1].numberOfChannels == 2); + assert(inputs[0].numberOfChannels == 0 || inputs[0].numberOfChannels == 2); + assert(inputs[1].numberOfChannels == 0 || inputs[1].numberOfChannels == 2); + assert(outputs[0].numberOfChannels == 2); + assert(outputs[1].numberOfChannels == 2); // All with the same number of samples assert(inputs[0].samplesPerChannel == inputs[1].samplesPerChannel); assert(inputs[0].samplesPerChannel == outputs[0].samplesPerChannel); assert(outputs[0].samplesPerChannel == outputs[1].samplesPerChannel); - // Now with all known quantities we can memcpy the data - int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels; - memcpy(outputs[0].data, inputs[0].data, totalSamples * sizeof(float)); - memcpy(outputs[1].data, inputs[1].data, totalSamples * sizeof(float)); + // Now with all known quantities we can memcpy all the data (or zero it if the + // channels are disabled) + int totalBytes = outputs[0].samplesPerChannel * outputs[0].numberOfChannels * sizeof(float); + if (inputs[0].numberOfChannels > 0) { + memcpy(outputs[0].data, inputs[0].data, totalBytes); + } else { + memset(outputs[0].data, 0, totalBytes); + } + if (inputs[1].numberOfChannels > 0) { + memcpy(outputs[1].data, inputs[1].data, totalBytes); + } else { + memset(outputs[1].data, 0, totalBytes); + } return true; } // Audio processor created, now register the audio callback -void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { printf("Audio worklet node creation failed\n"); return; @@ -70,3 +81,8 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { // Register the counter that exits the test after one second of mixing emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); } + +// This implementation has no custom start-up requirements +EmscriptenStartWebAudioWorkletCallback getStartCallback(void) { + return &initialised; +} diff --git a/test/webaudio/audioworklet_in_out_mono.c b/test/webaudio/audioworklet_in_out_mono.c index 56e405d95c2e4..00741d9703896 100644 --- a/test/webaudio/audioworklet_in_out_mono.c +++ b/test/webaudio/audioworklet_in_out_mono.c @@ -15,7 +15,7 @@ #include "audioworklet_test_shared.inc" // Callback to process and mix the audio tracks -bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) { +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { audioProcessedCount++; // Single mono output @@ -29,11 +29,18 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // We can now do a quick mix since we know the layouts if (numInputs > 0) { int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels; + // Simple copy of the first input's audio data, checking that we have + // channels (since a muted input has zero channels). float* outputData = outputs[0].data; - memcpy(outputData, inputs[0].data, totalSamples * sizeof(float)); + if (inputs[0].numberOfChannels > 0) { + memcpy(outputData, inputs[0].data, totalSamples * sizeof(float)); + } else { + // And for muted we need to fill the buffer with zeroes otherwise it repeats the last frame + memset(outputData, 0, totalSamples * sizeof(float)); + } + // Now add another inputs for (int n = 1; n < numInputs; n++) { - // It's possible to have an input with no channels - if (inputs[n].numberOfChannels == 1) { + if (inputs[n].numberOfChannels > 0) { float* inputData = inputs[n].data; for (int i = totalSamples - 1; i >= 0; i--) { outputData[i] += inputData[i]; @@ -45,7 +52,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi } // Audio processor created, now register the audio callback -void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { printf("Audio worklet node creation failed\n"); return; @@ -80,3 +87,9 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { // Register the counter that exits the test after one second of mixing emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); } + +// This implementation has no custom start-up requirements +EmscriptenStartWebAudioWorkletCallback getStartCallback(void) { + return &initialised; +} + diff --git a/test/webaudio/audioworklet_in_out_stereo.c b/test/webaudio/audioworklet_in_out_stereo.c index 107e0638a9e2c..7e3612f2f3deb 100644 --- a/test/webaudio/audioworklet_in_out_stereo.c +++ b/test/webaudio/audioworklet_in_out_stereo.c @@ -15,7 +15,7 @@ #include "audioworklet_test_shared.inc" // Callback to process and mix the audio tracks -bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) { +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { audioProcessedCount++; // Single stereo output @@ -29,11 +29,18 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // We can now do a quick mix since we know the layouts if (numInputs > 0) { int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels; + // Simple copy of the first input's audio data, checking that we have + // channels (since a muted input has zero channels). float* outputData = outputs[0].data; - memcpy(outputData, inputs[0].data, totalSamples * sizeof(float)); + if (inputs[0].numberOfChannels > 0) { + memcpy(outputData, inputs[0].data, totalSamples * sizeof(float)); + } else { + // And for muted we need to fill the buffer with zeroes otherwise it repeats the last frame + memset(outputData, 0, totalSamples * sizeof(float)); + } + // Now add another inputs for (int n = 1; n < numInputs; n++) { - // It's possible to have an input with no channels - if (inputs[n].numberOfChannels == 2) { + if (inputs[n].numberOfChannels > 0) { float* inputData = inputs[n].data; for (int i = totalSamples - 1; i >= 0; i--) { outputData[i] += inputData[i]; @@ -45,7 +52,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi } // Audio processor created, now register the audio callback -void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { printf("Audio worklet node creation failed\n"); return; @@ -80,3 +87,8 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { // Register the counter that exits the test after one second of mixing emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); } + +// This implementation has no custom start-up requirements +EmscriptenStartWebAudioWorkletCallback getStartCallback(void) { + return &initialised; +} diff --git a/test/webaudio/audioworklet_params_mixing.c b/test/webaudio/audioworklet_params_mixing.c new file mode 100644 index 0000000000000..676d90d8c41d5 --- /dev/null +++ b/test/webaudio/audioworklet_params_mixing.c @@ -0,0 +1,177 @@ +#include +#include +#include + +#include +#include + +// Tests processing two stereo audio inputs being mixed to a single stereo audio +// output in process(), then applying a fade from the parameters. + +// 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). +#define AUDIO_STACK_SIZE 6144 + +// Shared file playback and bootstrap +#include "audioworklet_test_shared.inc" + +// Callback to process and mix the audio tracks +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* __unused data) { + audioProcessedCount++; + + // Single stereo output + assert(numOutputs == 1 && outputs[0].numberOfChannels == 2); + int outSamplesPerChannel = outputs[0].samplesPerChannel; + for (int n = 0; n < numInputs; n++) { + // And all inputs are also stereo (or disabled) + assert(inputs[n].numberOfChannels == 2 || inputs[n].numberOfChannels == 0); + // This should always be the case + assert(inputs[n].samplesPerChannel == outSamplesPerChannel); + } + // Interestingly, params varies per browser. Chrome won't have a length > 1 + // unless the value changes, and FF has all 128 entries even for a k-rate + // parameter. The only given is that two params are incoming: + assert(numParams = 2); + assert(params[0].length == 1 || params[0].length == outSamplesPerChannel); + assert(params[1].length == 1 || params[1].length == outSamplesPerChannel); + // We can now do a quick mix since we know the layouts + if (numInputs > 0) { + int totalSamples = outSamplesPerChannel * outputs[0].numberOfChannels; + // Simple copy of the first input's audio data, checking that we have + // channels (since a muted input has zero channels). + float* outputData = outputs[0].data; + if (inputs[0].numberOfChannels > 0) { + memcpy(outputData, inputs[0].data, totalSamples * sizeof(float)); + } else { + // And for muted we need to fill the buffer with zeroes otherwise it repeats the last frame + memset(outputData, 0, totalSamples * sizeof(float)); + } + // Grab the mix level parameter and expand it to have one entry per output + // sample. This simplifies the mixer and smooths out browser differences. + // Output and input buffers are stereo planar, so the mix data just repeats. + float* const mixLevel = alloca(totalSamples * sizeof(float)); + if (params[0].length > 1) { + // This is the regular path, one entry per sample by number of channels + for (int ch = outputs[0].numberOfChannels - 1; ch >= 0; ch--) { + memcpy(mixLevel + ch * outSamplesPerChannel, params[0].data, outSamplesPerChannel * sizeof(float)); + } + } else { + // Chrome will take this path when the k-rate parameter doesn't change + float singleLevel = params[0].data[0]; + for (int n = totalSamples - 1; n >= 0; n--) { + mixLevel[n] = singleLevel; + } + } + // Now add another inputs with the mix level + for (int n = 1; n < numInputs; n++) { + if (inputs[n].numberOfChannels > 0) { + float* inputData = inputs[n].data; + for (int i = totalSamples - 1; i >= 0; i--) { + outputData[i] += inputData[i] * mixLevel[i]; + } + } + } + } + return true; +} + +// Grabs the known worklet parameter and fades in or out (depending on whether +// it's already fading up or down, we reverse the fade direction). +EM_JS(void, doFade, (EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletID), { + var worklet = emscriptenGetAudioObject(workletID); + if (worklet) { + // Emscripten's API creates these from a C array, indexing them instead of a + // name. Chrome and FF work with 0 but Safari requires the correct "0". + var param = worklet.parameters.get("0"); + if (param) { + param.setTargetAtTime((param.value > 0.5) ? 0 : 1, 0 /* same as context.currentTime */, 0.5); + } + } +}) + +// Registered keypress event to call the JS doFade() +bool onPress(int __unused type, const EmscriptenKeyboardEvent* e, void* data) { + if (!e->repeat && data) { + printf("Toggling fade\n"); + doFade(VOIDP_2_WA(data)); + } + return false; +} + +// Audio processor created, now register the audio callback +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { + if (!success) { + printf("Audio worklet node creation failed\n"); + return; + } + printf("Audio worklet processor created\n"); + printf("Click to toggle audio playback\n"); + printf("Keypress to fade the beat in or out\n"); + + // Stereo output, two inputs + int outputChannelCounts[1] = { 2 }; + EmscriptenAudioWorkletNodeCreateOptions opts = { + .numberOfInputs = 2, + .numberOfOutputs = 1, + .outputChannelCounts = outputChannelCounts + }; + EMSCRIPTEN_AUDIO_WORKLET_NODE_T worklet = emscripten_create_wasm_audio_worklet_node(context, "mixer", &opts, &process, NULL); + emscripten_audio_node_connect(worklet, context, 0, 0); + + // Create the two stereo source nodes and connect them to the two inputs + // Note: we can connect the sources to the same input and it'll get mixed for us, but that's not the point + beatID = createTrack(context, "audio_files/emscripten-beat.mp3", true); + if (beatID) { + emscripten_audio_node_connect(beatID, worklet, 0, 1); + } + bassID = createTrack(context, "audio_files/emscripten-bass.mp3", true); + if (bassID) { + emscripten_audio_node_connect(bassID, worklet, 0, 0); + } + + // Register a click to start playback + emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(context), false, &onClick); + // And a keypress to do affect the fader + emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(worklet), false, &onPress); + + // Register the counter that exits the test after one second of mixing + emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); +} + +// Worklet thread inited, now create the audio processor +void initialisedWithParams(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { + if (!success) { + printf("Audio worklet failed to initialise\n"); + return; + } + printf("Audio worklet initialised\n"); + + // Custom audio params we'll use as a fader + WebAudioParamDescriptor faderParam[] = { + { + // This a-rate (one entry per sample) is used to set the mix level + .defaultValue = 1.0f, + .minValue = 0.0f, + .maxValue = 1.0f, + .automationRate = WEBAUDIO_PARAM_A_RATE + }, { + // This k-rate (one entry per frame) is used just to test + .defaultValue = 0.0f, + .minValue = -100.0f, + .maxValue = 100.0f, + .automationRate = WEBAUDIO_PARAM_K_RATE + } + }; + WebAudioWorkletProcessorCreateOptions opts = { + .name = "mixer", + .numAudioParams = 2, + .audioParamDescriptors = faderParam + }; + emscripten_create_wasm_audio_worklet_processor_async(context, &opts, &processorCreated, NULL); +} + +// This implementation has no custom start-up requirements +EmscriptenStartWebAudioWorkletCallback getStartCallback(void) { + return &initialisedWithParams; +} diff --git a/test/webaudio/audioworklet_test_shared.inc b/test/webaudio/audioworklet_test_shared.inc index 7077a6cb833be..70fc330de5b9e 100644 --- a/test/webaudio/audioworklet_test_shared.inc +++ b/test/webaudio/audioworklet_test_shared.inc @@ -5,12 +5,18 @@ // Helper for MEMORY64 to cast a void* to an audio context or type #define VOIDP_2_WA(ptr) ((EMSCRIPTEN_WEBAUDIO_T) (intptr_t) ptr) +// Attribute that marks a function, parameter, variable, etc., as unused +// (silencing any -Wunused warnings). +#ifndef __unused +#define __unused __attribute__((unused)) +#endif + // Count the audio callbacks and return after 375 frames (1 second with the default 128 size) // // *** Remove this in your own code *** // volatile int audioProcessedCount = 0; -bool playedAndMixed(double time, void* data) { +bool playedAndMixed(double __unused time, void* __unused data) { if (audioProcessedCount >= 375) { emscripten_force_exit(0); return false; @@ -28,13 +34,18 @@ EM_JS(EMSCRIPTEN_WEBAUDIO_T, createTrack, (EMSCRIPTEN_WEBAUDIO_T ctxID, const ch var context = emscriptenGetAudioObject(ctxID); if (context) { var audio = document.createElement('audio'); +#if __wasm64__ + // Number() wrapper is a workaround for UTF8ToString() needing a JS number + // and from64() not being available in EM_JS macros. Fix in UTF8ToString? + url = Number(url); +#endif audio.src = UTF8ToString(url); audio.loop = looping; var track = context.createMediaElementSource(audio); return emscriptenRegisterAudioObject(track); } return 0; -}); +}) // Toggles the play/pause of a MediaElementAudioSourceNode given its ID EM_JS(void, toggleTrack, (EMSCRIPTEN_WEBAUDIO_T srcID), { @@ -50,10 +61,10 @@ EM_JS(void, toggleTrack, (EMSCRIPTEN_WEBAUDIO_T srcID), { } } } -}); +}) -// Registered click even to (1) enable audio playback and (2) toggle playing the tracks -bool onClick(int type, const EmscriptenMouseEvent* e, void* data) { +// Registered click event to (1) enable audio playback and (2) toggle playing the tracks +bool onClick(int __unused type, const EmscriptenMouseEvent* __unused e, void* data) { EMSCRIPTEN_WEBAUDIO_T ctx = VOIDP_2_WA(data); if (emscripten_audio_context_state(ctx) != AUDIO_CONTEXT_STATE_RUNNING) { printf("Resuming playback\n"); @@ -69,7 +80,7 @@ bool onClick(int type, const EmscriptenMouseEvent* e, void* data) { void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data); // Worklet thread inited, now create the audio processor -void initialised(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { +void initialised(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { printf("Audio worklet failed to initialise\n"); return; @@ -77,19 +88,27 @@ void initialised(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { printf("Audio worklet initialised\n"); WebAudioWorkletProcessorCreateOptions opts = { - .name = "mixer", + .name = "mixer" }; emscripten_create_wasm_audio_worklet_processor_async(context, &opts, &processorCreated, NULL); } +// To be implemented by the test code, allowing initialised() to be changed. +EmscriptenStartWebAudioWorkletCallback getStartCallback(void); + // Common entry point for the mixer tests -int main() { - static char workletStack[AUDIO_STACK_SIZE]; +int main(void) { + char* const workletStack = memalign(16, AUDIO_STACK_SIZE); + printf("Audio worklet stack at 0x%p\n", workletStack); + assert(workletStack); + EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(NULL); - emscripten_start_wasm_audio_worklet_thread_async(context, workletStack, sizeof workletStack, &initialised, NULL); + emscripten_start_wasm_audio_worklet_thread_async(context, workletStack, AUDIO_STACK_SIZE, getStartCallback(), NULL); + #ifndef BROWSER_TEST // Special case: browser tests need to exit instantly, interactive tests need to wait emscripten_runtime_keepalive_push(); #endif - return 0; + + return EXIT_SUCCESS; } From 632bd0d17186c64265685c6e40cb68f6131da9f0 Mon Sep 17 00:00:00 2001 From: Carl Woffenden Date: Thu, 20 Feb 2025 13:01:51 +0100 Subject: [PATCH 2/5] Mixer test migrated to use CI changes --- test/test_browser.py | 12 +++++++---- test/test_interactive.py | 2 +- test/webaudio/audioworklet_params_mixing.c | 19 +++++++++------- test/webaudio/audioworklet_test_shared.inc | 25 ++++++++++++---------- 4 files changed, 34 insertions(+), 24 deletions(-) diff --git a/test/test_browser.py b/test/test_browser.py index 36f27b55b6534..c1ce997417fee 100644 --- a/test/test_browser.py +++ b/test/test_browser.py @@ -5495,17 +5495,21 @@ def test_audio_worklet_post_function(self, args): def test_audio_worklet_modularize(self, args): 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')] + args) - # Tests multiple inputs, forcing a larger stack (note: passing BROWSER_TEST is - # specific to this test to allow it to exit rather than play forever). + # Tests an AudioWorklet with multiple stereo inputs mixing in the processor + # via a varying parameter to a single stereo output (touching all of the API + # copying from structs) @parameterized({ '': ([],), 'minimal_with_closure': (['-sMINIMAL_RUNTIME', '--closure=1', '-Oz'],), }) - def test_audio_worklet_stereo_io(self, args): + @no_wasm64('https://github.com/emscripten-core/emscripten/pull/23508') + @no_2gb('https://github.com/emscripten-core/emscripten/pull/23508') + @requires_sound_hardware + def test_audio_worklet_params_mixing(self, args): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DBROWSER_TEST'] + args) + self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT'] + args) def test_error_reporting(self): # Test catching/reporting Error objects diff --git a/test/test_interactive.py b/test/test_interactive.py index e25bd12f4f9b2..2b0c55097531a 100644 --- a/test/test_interactive.py +++ b/test/test_interactive.py @@ -339,7 +339,7 @@ def test_audio_worklet_params_mixing(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) + self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT']) class interactive64(interactive): diff --git a/test/webaudio/audioworklet_params_mixing.c b/test/webaudio/audioworklet_params_mixing.c index 676d90d8c41d5..a9c5b5deaf035 100644 --- a/test/webaudio/audioworklet_params_mixing.c +++ b/test/webaudio/audioworklet_params_mixing.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -18,7 +17,9 @@ // Callback to process and mix the audio tracks bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* __unused data) { +#ifdef TEST_AND_EXIT audioProcessedCount++; +#endif // Single stereo output assert(numOutputs == 1 && outputs[0].numberOfChannels == 2); @@ -93,7 +94,7 @@ EM_JS(void, doFade, (EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletID), { // Registered keypress event to call the JS doFade() bool onPress(int __unused type, const EmscriptenKeyboardEvent* e, void* data) { if (!e->repeat && data) { - printf("Toggling fade\n"); + emscripten_out("Toggling fade"); doFade(VOIDP_2_WA(data)); } return false; @@ -102,12 +103,12 @@ bool onPress(int __unused type, const EmscriptenKeyboardEvent* e, void* data) { // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { - printf("Audio worklet node creation failed\n"); + assert("Audio worklet failed in processorCreated()" && success); return; } - printf("Audio worklet processor created\n"); - printf("Click to toggle audio playback\n"); - printf("Keypress to fade the beat in or out\n"); + emscripten_out("Audio worklet processor created"); + emscripten_out("Click to toggle audio playback"); + emscripten_out("Keypress to fade the beat in or out"); // Stereo output, two inputs int outputChannelCounts[1] = { 2 }; @@ -135,17 +136,19 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse // And a keypress to do affect the fader emscripten_set_keypress_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(worklet), false, &onPress); +#ifdef TEST_AND_EXIT // Register the counter that exits the test after one second of mixing emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); +#endif } // Worklet thread inited, now create the audio processor void initialisedWithParams(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { - printf("Audio worklet failed to initialise\n"); + assert("Audio worklet failed initialised()" && success); return; } - printf("Audio worklet initialised\n"); + emscripten_out("Audio worklet initialised"); // Custom audio params we'll use as a fader WebAudioParamDescriptor faderParam[] = { diff --git a/test/webaudio/audioworklet_test_shared.inc b/test/webaudio/audioworklet_test_shared.inc index 70fc330de5b9e..bf64be3220dbe 100644 --- a/test/webaudio/audioworklet_test_shared.inc +++ b/test/webaudio/audioworklet_test_shared.inc @@ -11,18 +11,21 @@ #define __unused __attribute__((unused)) #endif -// Count the audio callbacks and return after 375 frames (1 second with the default 128 size) +// Count the audio callbacks and return after 375 frames (1 second with the +// default 128 size). // // *** Remove this in your own code *** // +#ifdef TEST_AND_EXIT volatile int audioProcessedCount = 0; bool playedAndMixed(double __unused time, void* __unused data) { if (audioProcessedCount >= 375) { - emscripten_force_exit(0); + emscripten_force_exit(EXIT_SUCCESS); return false; } return true; } +#endif // ID to the beat and bass loops EMSCRIPTEN_WEBAUDIO_T beatID = 0; @@ -67,10 +70,10 @@ EM_JS(void, toggleTrack, (EMSCRIPTEN_WEBAUDIO_T srcID), { bool onClick(int __unused type, const EmscriptenMouseEvent* __unused e, void* data) { EMSCRIPTEN_WEBAUDIO_T ctx = VOIDP_2_WA(data); if (emscripten_audio_context_state(ctx) != AUDIO_CONTEXT_STATE_RUNNING) { - printf("Resuming playback\n"); + emscripten_out("Resuming playback"); emscripten_resume_audio_context_sync(ctx); } - printf("Toggling audio playback\n"); + emscripten_out("Toggling audio playback"); toggleTrack(beatID); toggleTrack(bassID); return false; @@ -82,10 +85,10 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data); // Worklet thread inited, now create the audio processor void initialised(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { - printf("Audio worklet failed to initialise\n"); + assert("Audio worklet failed in initialised()" && success); return; } - printf("Audio worklet initialised\n"); + emscripten_out("Audio worklet initialised"); WebAudioWorkletProcessorCreateOptions opts = { .name = "mixer" @@ -99,16 +102,16 @@ EmscriptenStartWebAudioWorkletCallback getStartCallback(void); // Common entry point for the mixer tests int main(void) { char* const workletStack = memalign(16, AUDIO_STACK_SIZE); - printf("Audio worklet stack at 0x%p\n", workletStack); + emscripten_outf("Audio worklet stack at 0x%p", workletStack); assert(workletStack); EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(NULL); emscripten_start_wasm_audio_worklet_thread_async(context, workletStack, AUDIO_STACK_SIZE, getStartCallback(), NULL); -#ifndef BROWSER_TEST - // Special case: browser tests need to exit instantly, interactive tests need to wait - emscripten_runtime_keepalive_push(); -#endif +#ifdef TEST_AND_EXIT + // We're in the test harness and exiting is via playedAndMixed() + emscripten_exit_with_live_runtime(); + #endif return EXIT_SUCCESS; } From 6fc8fbe52e3553ba2d870c636cf4f22b28548c70 Mon Sep 17 00:00:00 2001 From: Carl Woffenden Date: Thu, 20 Feb 2025 14:36:16 +0100 Subject: [PATCH 3/5] All tests follow same pattern --- test/test_interactive.py | 10 +++++----- test/webaudio/audioworklet_2x_in_hard_pan.c | 13 ++++++++----- test/webaudio/audioworklet_2x_in_out_stereo.c | 13 ++++++++----- test/webaudio/audioworklet_in_out_mono.c | 12 +++++++----- test/webaudio/audioworklet_in_out_stereo.c | 11 +++++++---- test/webaudio/audioworklet_test_shared.inc | 14 ++++++++++++-- 6 files changed, 47 insertions(+), 26 deletions(-) diff --git a/test/test_interactive.py b/test/test_interactive.py index 2b0c55097531a..d3ca404828ce1 100644 --- a/test/test_interactive.py +++ b/test/test_interactive.py @@ -311,35 +311,35 @@ def test_audio_worklet_stereo_io(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) + self.btest_exit('webaudio/audioworklet_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) # Tests an AudioWorklet with multiple stereo inputs copying in the processor to multiple stereo outputs (6kB stack) def test_audio_worklet_2x_stereo_io(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_2x_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) + self.btest_exit('webaudio/audioworklet_2x_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) # Tests an AudioWorklet with multiple mono inputs mixing in the processor to a single mono output (2kB stack) def test_audio_worklet_mono_io(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat-mono.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass-mono.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_in_out_mono.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) + self.btest_exit('webaudio/audioworklet_in_out_mono.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) # Tests an AudioWorklet with multiple mono inputs copying in the processor to L+R stereo outputs (3kB stack) def test_audio_worklet_2x_hard_pan_io(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat-mono.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass-mono.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_2x_in_hard_pan.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) + self.btest_exit('webaudio/audioworklet_2x_in_hard_pan.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) # Tests an AudioWorklet with multiple stereo inputs mixing in the processor via a parameter to a single stereo output (6kB stack) def test_audio_worklet_params_mixing(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT']) + self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) class interactive64(interactive): diff --git a/test/webaudio/audioworklet_2x_in_hard_pan.c b/test/webaudio/audioworklet_2x_in_hard_pan.c index eec8e5aac32fb..f87f58675887e 100644 --- a/test/webaudio/audioworklet_2x_in_hard_pan.c +++ b/test/webaudio/audioworklet_2x_in_hard_pan.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -16,7 +15,9 @@ // Callback to process and copy the audio tracks bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { +#ifdef TEST_AND_EXIT audioProcessedCount++; +#endif // Twin mono in (or disabled), single stereo out assert(numInputs == 2 && numOutputs == 1); @@ -47,11 +48,11 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { - printf("Audio worklet node creation failed\n"); + assert("Audio worklet failed in processorCreated()" && success); return; } - printf("Audio worklet processor created\n"); - printf("Click to toggle audio playback\n"); + emscripten_out("Audio worklet processor created"); + emscripten_out("Click to toggle audio playback"); // Stereo output, two inputs int outputChannelCounts[2] = { 2 }; @@ -77,8 +78,10 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse // Register a click to start playback emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(context), false, &onClick); - // Register the counter that exits the test after one second of mixing +#ifdef TEST_AND_EXIT + // Register the counter that exits the test after one second of playback emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); +#endif } // This implementation has no custom start-up requirements diff --git a/test/webaudio/audioworklet_2x_in_out_stereo.c b/test/webaudio/audioworklet_2x_in_out_stereo.c index a4865a9b3d028..4bcb24651213b 100644 --- a/test/webaudio/audioworklet_2x_in_out_stereo.c +++ b/test/webaudio/audioworklet_2x_in_out_stereo.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -15,7 +14,9 @@ // Callback to process and copy the audio tracks bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { +#ifdef TEST_AND_EXIT audioProcessedCount++; +#endif // Twin stereo in and out assert(numInputs == 2 && numOutputs == 2); @@ -46,11 +47,11 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { - printf("Audio worklet node creation failed\n"); + assert("Audio worklet failed in processorCreated()" && success); return; } - printf("Audio worklet processor created\n"); - printf("Click to toggle audio playback\n"); + emscripten_out("Audio worklet processor created"); + emscripten_out("Click to toggle audio playback"); // Two stereo outputs, two inputs int outputChannelCounts[2] = { 2, 2 }; @@ -78,8 +79,10 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse // Register a click to start playback emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(context), false, &onClick); - // Register the counter that exits the test after one second of mixing +#ifdef TEST_AND_EXIT + // Register the counter that exits the test after one second of playback emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); +#endif } // This implementation has no custom start-up requirements diff --git a/test/webaudio/audioworklet_in_out_mono.c b/test/webaudio/audioworklet_in_out_mono.c index 00741d9703896..2aedf52fa3315 100644 --- a/test/webaudio/audioworklet_in_out_mono.c +++ b/test/webaudio/audioworklet_in_out_mono.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -16,7 +15,9 @@ // Callback to process and mix the audio tracks bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { +#ifdef TEST_AND_EXIT audioProcessedCount++; +#endif // Single mono output assert(numOutputs == 1 && outputs[0].numberOfChannels == 1); @@ -54,11 +55,11 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { - printf("Audio worklet node creation failed\n"); + assert("Audio worklet failed in processorCreated()" && success); return; } - printf("Audio worklet processor created\n"); - printf("Click to toggle audio playback\n"); + emscripten_out("Audio worklet processor created"); + emscripten_out("Click to toggle audio playback"); // Mono output, two inputs int outputChannelCounts[1] = { 1 }; @@ -84,12 +85,13 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse // Register a click to start playback emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(context), false, &onClick); +#ifdef TEST_AND_EXIT // Register the counter that exits the test after one second of mixing emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); +#endif } // This implementation has no custom start-up requirements EmscriptenStartWebAudioWorkletCallback getStartCallback(void) { return &initialised; } - diff --git a/test/webaudio/audioworklet_in_out_stereo.c b/test/webaudio/audioworklet_in_out_stereo.c index 7e3612f2f3deb..34189f7ed86c8 100644 --- a/test/webaudio/audioworklet_in_out_stereo.c +++ b/test/webaudio/audioworklet_in_out_stereo.c @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -16,7 +15,9 @@ // Callback to process and mix the audio tracks bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { +#ifdef TEST_AND_EXIT audioProcessedCount++; +#endif // Single stereo output assert(numOutputs == 1 && outputs[0].numberOfChannels == 2); @@ -54,11 +55,11 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { if (!success) { - printf("Audio worklet node creation failed\n"); + assert("Audio worklet failed in processorCreated()" && success); return; } - printf("Audio worklet processor created\n"); - printf("Click to toggle audio playback\n"); + emscripten_out("Audio worklet processor created"); + emscripten_out("Click to toggle audio playback"); // Stereo output, two inputs int outputChannelCounts[1] = { 2 }; @@ -84,8 +85,10 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse // Register a click to start playback emscripten_set_click_callback(EMSCRIPTEN_EVENT_TARGET_DOCUMENT, WA_2_VOIDP(context), false, &onClick); +#ifdef TEST_AND_EXIT // Register the counter that exits the test after one second of mixing emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); +#endif } // This implementation has no custom start-up requirements diff --git a/test/webaudio/audioworklet_test_shared.inc b/test/webaudio/audioworklet_test_shared.inc index bf64be3220dbe..5a7d876e56ea2 100644 --- a/test/webaudio/audioworklet_test_shared.inc +++ b/test/webaudio/audioworklet_test_shared.inc @@ -19,7 +19,12 @@ #ifdef TEST_AND_EXIT volatile int audioProcessedCount = 0; bool playedAndMixed(double __unused time, void* __unused data) { +#if TEST_AND_EXIT > 0 + // Special interactive case to play for 10 seconds + if (audioProcessedCount >= 3750) { +#else if (audioProcessedCount >= 375) { +#endif emscripten_force_exit(EXIT_SUCCESS); return false; } @@ -110,8 +115,13 @@ int main(void) { #ifdef TEST_AND_EXIT // We're in the test harness and exiting is via playedAndMixed() - emscripten_exit_with_live_runtime(); - #endif +#if TEST_AND_EXIT > 0 + emscripten_out("In test mode, will exit after 10 seconds of playback"); +#else + emscripten_out("In test mode, will exit after 1 second of playback"); +#endif + emscripten_exit_with_live_runtime(); +#endif return EXIT_SUCCESS; } From 27a0573148bda73059b39e3f667be93879167c92 Mon Sep 17 00:00:00 2001 From: Carl Woffenden Date: Thu, 20 Feb 2025 18:54:48 +0100 Subject: [PATCH 4/5] Followed same review comments from #23695 --- test/webaudio/audioworklet_2x_in_hard_pan.c | 5 +---- test/webaudio/audioworklet_2x_in_out_stereo.c | 5 +---- test/webaudio/audioworklet_in_out_mono.c | 5 +---- test/webaudio/audioworklet_in_out_stereo.c | 5 +---- test/webaudio/audioworklet_params_mixing.c | 10 ++-------- test/webaudio/audioworklet_test_shared.inc | 7 ++----- 6 files changed, 8 insertions(+), 29 deletions(-) diff --git a/test/webaudio/audioworklet_2x_in_hard_pan.c b/test/webaudio/audioworklet_2x_in_hard_pan.c index f87f58675887e..a3df55d25f7d9 100644 --- a/test/webaudio/audioworklet_2x_in_hard_pan.c +++ b/test/webaudio/audioworklet_2x_in_hard_pan.c @@ -47,10 +47,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { - if (!success) { - assert("Audio worklet failed in processorCreated()" && success); - return; - } + assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); diff --git a/test/webaudio/audioworklet_2x_in_out_stereo.c b/test/webaudio/audioworklet_2x_in_out_stereo.c index 4bcb24651213b..7a17b57fff023 100644 --- a/test/webaudio/audioworklet_2x_in_out_stereo.c +++ b/test/webaudio/audioworklet_2x_in_out_stereo.c @@ -46,10 +46,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { - if (!success) { - assert("Audio worklet failed in processorCreated()" && success); - return; - } + assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); diff --git a/test/webaudio/audioworklet_in_out_mono.c b/test/webaudio/audioworklet_in_out_mono.c index 2aedf52fa3315..9a81fab1b2fd6 100644 --- a/test/webaudio/audioworklet_in_out_mono.c +++ b/test/webaudio/audioworklet_in_out_mono.c @@ -54,10 +54,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { - if (!success) { - assert("Audio worklet failed in processorCreated()" && success); - return; - } + assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); diff --git a/test/webaudio/audioworklet_in_out_stereo.c b/test/webaudio/audioworklet_in_out_stereo.c index 34189f7ed86c8..3bf1f6e8210bd 100644 --- a/test/webaudio/audioworklet_in_out_stereo.c +++ b/test/webaudio/audioworklet_in_out_stereo.c @@ -54,10 +54,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { - if (!success) { - assert("Audio worklet failed in processorCreated()" && success); - return; - } + assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); diff --git a/test/webaudio/audioworklet_params_mixing.c b/test/webaudio/audioworklet_params_mixing.c index a9c5b5deaf035..69b2a0d7f5554 100644 --- a/test/webaudio/audioworklet_params_mixing.c +++ b/test/webaudio/audioworklet_params_mixing.c @@ -102,10 +102,7 @@ bool onPress(int __unused type, const EmscriptenKeyboardEvent* e, void* data) { // Audio processor created, now register the audio callback void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { - if (!success) { - assert("Audio worklet failed in processorCreated()" && success); - return; - } + assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); emscripten_out("Keypress to fade the beat in or out"); @@ -144,10 +141,7 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse // Worklet thread inited, now create the audio processor void initialisedWithParams(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { - if (!success) { - assert("Audio worklet failed initialised()" && success); - return; - } + assert(success && "Audio worklet failed initialised()"); emscripten_out("Audio worklet initialised"); // Custom audio params we'll use as a fader diff --git a/test/webaudio/audioworklet_test_shared.inc b/test/webaudio/audioworklet_test_shared.inc index 5a7d876e56ea2..463b2bdd41418 100644 --- a/test/webaudio/audioworklet_test_shared.inc +++ b/test/webaudio/audioworklet_test_shared.inc @@ -25,7 +25,7 @@ bool playedAndMixed(double __unused time, void* __unused data) { #else if (audioProcessedCount >= 375) { #endif - emscripten_force_exit(EXIT_SUCCESS); + emscripten_force_exit(0); return false; } return true; @@ -89,10 +89,7 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data); // Worklet thread inited, now create the audio processor void initialised(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { - if (!success) { - assert("Audio worklet failed in initialised()" && success); - return; - } + assert(success && "Audio worklet failed in initialised()"); emscripten_out("Audio worklet initialised"); WebAudioWorkletProcessorCreateOptions opts = { From a55981e08e59d14774f81f550a83904fa197f099 Mon Sep 17 00:00:00 2001 From: Carl Woffenden Date: Thu, 20 Feb 2025 19:43:10 +0100 Subject: [PATCH 5/5] Suggestions following review --- test/test_interactive.py | 10 ++++---- test/webaudio/audioworklet_2x_in_hard_pan.c | 4 +-- test/webaudio/audioworklet_2x_in_out_stereo.c | 4 +-- test/webaudio/audioworklet_in_out_mono.c | 4 +-- test/webaudio/audioworklet_in_out_stereo.c | 4 +-- test/webaudio/audioworklet_params_mixing.c | 8 +++--- test/webaudio/audioworklet_test_shared.inc | 25 +++---------------- 7 files changed, 21 insertions(+), 38 deletions(-) diff --git a/test/test_interactive.py b/test/test_interactive.py index d3ca404828ce1..e25bd12f4f9b2 100644 --- a/test/test_interactive.py +++ b/test/test_interactive.py @@ -311,35 +311,35 @@ def test_audio_worklet_stereo_io(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) + self.btest_exit('webaudio/audioworklet_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) # Tests an AudioWorklet with multiple stereo inputs copying in the processor to multiple stereo outputs (6kB stack) def test_audio_worklet_2x_stereo_io(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_2x_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) + self.btest_exit('webaudio/audioworklet_2x_in_out_stereo.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) # Tests an AudioWorklet with multiple mono inputs mixing in the processor to a single mono output (2kB stack) def test_audio_worklet_mono_io(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat-mono.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass-mono.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_in_out_mono.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) + self.btest_exit('webaudio/audioworklet_in_out_mono.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) # Tests an AudioWorklet with multiple mono inputs copying in the processor to L+R stereo outputs (3kB stack) def test_audio_worklet_2x_hard_pan_io(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat-mono.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass-mono.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_2x_in_hard_pan.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) + self.btest_exit('webaudio/audioworklet_2x_in_hard_pan.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) # Tests an AudioWorklet with multiple stereo inputs mixing in the processor via a parameter to a single stereo output (6kB stack) def test_audio_worklet_params_mixing(self): os.mkdir('audio_files') shutil.copy(test_file('webaudio/audio_files/emscripten-beat.mp3'), 'audio_files/') shutil.copy(test_file('webaudio/audio_files/emscripten-bass.mp3'), 'audio_files/') - self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS', '-DTEST_AND_EXIT=1']) + self.btest_exit('webaudio/audioworklet_params_mixing.c', args=['-sAUDIO_WORKLET', '-sWASM_WORKERS']) class interactive64(interactive): diff --git a/test/webaudio/audioworklet_2x_in_hard_pan.c b/test/webaudio/audioworklet_2x_in_hard_pan.c index a3df55d25f7d9..636b44e777b78 100644 --- a/test/webaudio/audioworklet_2x_in_hard_pan.c +++ b/test/webaudio/audioworklet_2x_in_hard_pan.c @@ -14,7 +14,7 @@ #include "audioworklet_test_shared.inc" // Callback to process and copy the audio tracks -bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) { #ifdef TEST_AND_EXIT audioProcessedCount++; #endif @@ -46,7 +46,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi } // Audio processor created, now register the audio callback -void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); diff --git a/test/webaudio/audioworklet_2x_in_out_stereo.c b/test/webaudio/audioworklet_2x_in_out_stereo.c index 7a17b57fff023..f9a773742812a 100644 --- a/test/webaudio/audioworklet_2x_in_out_stereo.c +++ b/test/webaudio/audioworklet_2x_in_out_stereo.c @@ -13,7 +13,7 @@ #include "audioworklet_test_shared.inc" // Callback to process and copy the audio tracks -bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) { #ifdef TEST_AND_EXIT audioProcessedCount++; #endif @@ -45,7 +45,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi } // Audio processor created, now register the audio callback -void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); diff --git a/test/webaudio/audioworklet_in_out_mono.c b/test/webaudio/audioworklet_in_out_mono.c index 9a81fab1b2fd6..bd76e1fc1c863 100644 --- a/test/webaudio/audioworklet_in_out_mono.c +++ b/test/webaudio/audioworklet_in_out_mono.c @@ -14,7 +14,7 @@ #include "audioworklet_test_shared.inc" // Callback to process and mix the audio tracks -bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) { #ifdef TEST_AND_EXIT audioProcessedCount++; #endif @@ -53,7 +53,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi } // Audio processor created, now register the audio callback -void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); diff --git a/test/webaudio/audioworklet_in_out_stereo.c b/test/webaudio/audioworklet_in_out_stereo.c index 3bf1f6e8210bd..3c41a26c41e4a 100644 --- a/test/webaudio/audioworklet_in_out_stereo.c +++ b/test/webaudio/audioworklet_in_out_stereo.c @@ -14,7 +14,7 @@ #include "audioworklet_test_shared.inc" // Callback to process and mix the audio tracks -bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int __unused numParams, const AudioParamFrame* __unused params, void* __unused data) { +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) { #ifdef TEST_AND_EXIT audioProcessedCount++; #endif @@ -53,7 +53,7 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi } // Audio processor created, now register the audio callback -void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); diff --git a/test/webaudio/audioworklet_params_mixing.c b/test/webaudio/audioworklet_params_mixing.c index 69b2a0d7f5554..446888dcea4cc 100644 --- a/test/webaudio/audioworklet_params_mixing.c +++ b/test/webaudio/audioworklet_params_mixing.c @@ -16,7 +16,7 @@ #include "audioworklet_test_shared.inc" // Callback to process and mix the audio tracks -bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* __unused data) { +bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, AudioSampleFrame* outputs, int numParams, const AudioParamFrame* params, void* data) { #ifdef TEST_AND_EXIT audioProcessedCount++; #endif @@ -92,7 +92,7 @@ EM_JS(void, doFade, (EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletID), { }) // Registered keypress event to call the JS doFade() -bool onPress(int __unused type, const EmscriptenKeyboardEvent* e, void* data) { +bool onPress(int type, const EmscriptenKeyboardEvent* e, void* data) { if (!e->repeat && data) { emscripten_out("Toggling fade"); doFade(VOIDP_2_WA(data)); @@ -101,7 +101,7 @@ bool onPress(int __unused type, const EmscriptenKeyboardEvent* e, void* data) { } // Audio processor created, now register the audio callback -void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { +void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { assert(success && "Audio worklet failed in processorCreated()"); emscripten_out("Audio worklet processor created"); emscripten_out("Click to toggle audio playback"); @@ -140,7 +140,7 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse } // Worklet thread inited, now create the audio processor -void initialisedWithParams(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { +void initialisedWithParams(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { assert(success && "Audio worklet failed initialised()"); emscripten_out("Audio worklet initialised"); diff --git a/test/webaudio/audioworklet_test_shared.inc b/test/webaudio/audioworklet_test_shared.inc index 463b2bdd41418..777f106b1e57c 100644 --- a/test/webaudio/audioworklet_test_shared.inc +++ b/test/webaudio/audioworklet_test_shared.inc @@ -5,11 +5,6 @@ // Helper for MEMORY64 to cast a void* to an audio context or type #define VOIDP_2_WA(ptr) ((EMSCRIPTEN_WEBAUDIO_T) (intptr_t) ptr) -// Attribute that marks a function, parameter, variable, etc., as unused -// (silencing any -Wunused warnings). -#ifndef __unused -#define __unused __attribute__((unused)) -#endif // Count the audio callbacks and return after 375 frames (1 second with the // default 128 size). @@ -18,13 +13,8 @@ // #ifdef TEST_AND_EXIT volatile int audioProcessedCount = 0; -bool playedAndMixed(double __unused time, void* __unused data) { -#if TEST_AND_EXIT > 0 - // Special interactive case to play for 10 seconds - if (audioProcessedCount >= 3750) { -#else +bool playedAndMixed(double time, void* data) { if (audioProcessedCount >= 375) { -#endif emscripten_force_exit(0); return false; } @@ -42,12 +32,9 @@ EM_JS(EMSCRIPTEN_WEBAUDIO_T, createTrack, (EMSCRIPTEN_WEBAUDIO_T ctxID, const ch var context = emscriptenGetAudioObject(ctxID); if (context) { var audio = document.createElement('audio'); -#if __wasm64__ // Number() wrapper is a workaround for UTF8ToString() needing a JS number // and from64() not being available in EM_JS macros. Fix in UTF8ToString? - url = Number(url); -#endif - audio.src = UTF8ToString(url); + audio.src = UTF8ToString(Number(url)); audio.loop = looping; var track = context.createMediaElementSource(audio); return emscriptenRegisterAudioObject(track); @@ -72,7 +59,7 @@ EM_JS(void, toggleTrack, (EMSCRIPTEN_WEBAUDIO_T srcID), { }) // Registered click event to (1) enable audio playback and (2) toggle playing the tracks -bool onClick(int __unused type, const EmscriptenMouseEvent* __unused e, void* data) { +bool onClick(int type, const EmscriptenMouseEvent* e, void* data) { EMSCRIPTEN_WEBAUDIO_T ctx = VOIDP_2_WA(data); if (emscripten_audio_context_state(ctx) != AUDIO_CONTEXT_STATE_RUNNING) { emscripten_out("Resuming playback"); @@ -88,7 +75,7 @@ bool onClick(int __unused type, const EmscriptenMouseEvent* __unused e, void* da void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data); // Worklet thread inited, now create the audio processor -void initialised(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unused data) { +void initialised(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { assert(success && "Audio worklet failed in initialised()"); emscripten_out("Audio worklet initialised"); @@ -112,13 +99,9 @@ int main(void) { #ifdef TEST_AND_EXIT // We're in the test harness and exiting is via playedAndMixed() -#if TEST_AND_EXIT > 0 - emscripten_out("In test mode, will exit after 10 seconds of playback"); -#else emscripten_out("In test mode, will exit after 1 second of playback"); #endif emscripten_exit_with_live_runtime(); -#endif return EXIT_SUCCESS; }