Skip to content

Commit

Permalink
Split out the test changes from emscripten-core#23508
Browse files Browse the repository at this point in the history
This mostly adds an Audio Worklet parameter test, but also tidies a little the related tests and shared code.
  • Loading branch information
cwoffenden committed Feb 20, 2025
1 parent 89aca9c commit 0384fad
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 38 deletions.
24 changes: 23 additions & 1 deletion test/test_interactive.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
33 changes: 25 additions & 8 deletions test/webaudio/audioworklet_2x_in_hard_pan.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
32 changes: 24 additions & 8 deletions test/webaudio/audioworklet_2x_in_out_stereo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
23 changes: 18 additions & 5 deletions test/webaudio/audioworklet_in_out_mono.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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];
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}

22 changes: 17 additions & 5 deletions test/webaudio/audioworklet_in_out_stereo.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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];
Expand All @@ -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;
Expand Down Expand Up @@ -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;
}
Loading

0 comments on commit 0384fad

Please sign in to comment.