forked from emscripten-core/emscripten
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[AUDIO_WORKLET] Add multichannel audio tests. NFC (emscripten-core#23394
) These are the audio worklet tests from emscripten-core#22753 extracted to a standalone PR. The tests are: - Multiple stereo inputs mixing in the processor to a single stereo output - Multiple stereo inputs copying in the processor to multiple stereo outputs - Multiple mono inputs mixing in the processor to a single mono output - Multiple mono inputs copying in the processor to L+R stereo outputs The tests use different stack sizes (from 2kB to 6kB depending on the requirement). The audio tracks were composed by Tim Wright especially for Emscripten and released under a CC0 license.
- Loading branch information
1 parent
6bed395
commit bcad96d
Showing
12 changed files
with
446 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Emscripten Beat and Emscripten Bass by [CoLD SToRAGE](https://www.coldstorage.org.uk) (Tim Wright). | ||
|
||
Released under the [Creative Commons Zero (CC0)](https://creativecommons.org/publicdomain/zero/1.0/) Public Domain Dedication. | ||
|
||
To the extent possible under law, OGP Phonogramatica has waived all copyright and related or neighbouring rights to these works. |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
#include <assert.h> | ||
#include <string.h> | ||
#include <stdio.h> | ||
|
||
#include <emscripten/em_js.h> | ||
#include <emscripten/webaudio.h> | ||
|
||
// Tests two mono audio inputs being copied to the left and right channels of a | ||
// single stereo output (with a hard pan). | ||
|
||
// This needs to be big enough for the stereo output, 2x mono inputs and the worker stack | ||
#define AUDIO_STACK_SIZE 3072 | ||
|
||
// Shared file playback and bootstrap | ||
#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) { | ||
audioProcessedCount++; | ||
|
||
// Twin mono in, single stereo out | ||
assert(numInputs == 2 && numOutputs == 1); | ||
assert(inputs[0].numberOfChannels == 1 && 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)); | ||
return true; | ||
} | ||
|
||
// Audio processor created, now register the audio callback | ||
void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { | ||
if (!success) { | ||
printf("Audio worklet node creation failed\n"); | ||
return; | ||
} | ||
printf("Audio worklet processor created\n"); | ||
printf("Click to toggle audio playback\n"); | ||
|
||
// Stereo output, two inputs | ||
int outputChannelCounts[2] = { 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 mono 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-mono.mp3", true); | ||
if (beatID) { | ||
emscripten_audio_node_connect(beatID, worklet, 0, 0); | ||
} | ||
bassID = createTrack(context, "audio_files/emscripten-bass-mono.mp3", true); | ||
if (bassID) { | ||
emscripten_audio_node_connect(bassID, worklet, 0, 1); | ||
} | ||
|
||
// 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 | ||
emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
#include <assert.h> | ||
#include <string.h> | ||
#include <stdio.h> | ||
|
||
#include <emscripten/em_js.h> | ||
#include <emscripten/webaudio.h> | ||
|
||
// Tests two stereo audio inputs being copied to two stereo outputs. | ||
|
||
// This needs to be big enough for the 2x stereo outputs, 2x inputs and the worker stack | ||
#define AUDIO_STACK_SIZE 6144 | ||
|
||
// Shared file playback and bootstrap | ||
#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) { | ||
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); | ||
// 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)); | ||
return true; | ||
} | ||
|
||
// Audio processor created, now register the audio callback | ||
void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { | ||
if (!success) { | ||
printf("Audio worklet node creation failed\n"); | ||
return; | ||
} | ||
printf("Audio worklet processor created\n"); | ||
printf("Click to toggle audio playback\n"); | ||
|
||
// Two stereo outputs, two inputs | ||
int outputChannelCounts[2] = { 2, 2 }; | ||
EmscriptenAudioWorkletNodeCreateOptions opts = { | ||
.numberOfInputs = 2, | ||
.numberOfOutputs = 2, | ||
.outputChannelCounts = outputChannelCounts | ||
}; | ||
EMSCRIPTEN_AUDIO_WORKLET_NODE_T worklet = emscripten_create_wasm_audio_worklet_node(context, "mixer", &opts, &process, NULL); | ||
// Both outputs connected to the context | ||
emscripten_audio_node_connect(worklet, context, 0, 0); | ||
emscripten_audio_node_connect(worklet, context, 1, 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, 0); | ||
} | ||
bassID = createTrack(context, "audio_files/emscripten-bass.mp3", true); | ||
if (bassID) { | ||
emscripten_audio_node_connect(bassID, worklet, 0, 1); | ||
} | ||
|
||
// 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 | ||
emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
#include <assert.h> | ||
#include <string.h> | ||
#include <stdio.h> | ||
|
||
#include <emscripten/em_js.h> | ||
#include <emscripten/webaudio.h> | ||
|
||
// Tests processing two mono audio inputs being mixed to a single mono audio | ||
// output in process() (by adding the inputs together). | ||
|
||
// This needs to be big enough for the mono output, 2x inputs and the worker stack | ||
#define AUDIO_STACK_SIZE 2048 | ||
|
||
// 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* data) { | ||
audioProcessedCount++; | ||
|
||
// Single mono output | ||
assert(numOutputs == 1 && outputs[0].numberOfChannels == 1); | ||
for (int n = 0; n < numInputs; n++) { | ||
// And all inputs are also stereo | ||
assert(inputs[n].numberOfChannels == 1 || inputs[n].numberOfChannels == 0); | ||
// This should always be the case | ||
assert(inputs[n].samplesPerChannel == outputs[0].samplesPerChannel); | ||
} | ||
// We can now do a quick mix since we know the layouts | ||
if (numInputs > 0) { | ||
int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels; | ||
float* outputData = outputs[0].data; | ||
memcpy(outputData, inputs[0].data, totalSamples * sizeof(float)); | ||
for (int n = 1; n < numInputs; n++) { | ||
// It's possible to have an input with no channels | ||
if (inputs[n].numberOfChannels == 1) { | ||
float* inputData = inputs[n].data; | ||
for (int i = totalSamples - 1; i >= 0; i--) { | ||
outputData[i] += inputData[i]; | ||
} | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
// Audio processor created, now register the audio callback | ||
void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { | ||
if (!success) { | ||
printf("Audio worklet node creation failed\n"); | ||
return; | ||
} | ||
printf("Audio worklet processor created\n"); | ||
printf("Click to toggle audio playback\n"); | ||
|
||
// Mono output, two inputs | ||
int outputChannelCounts[1] = { 1 }; | ||
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 mono 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-mono.mp3", true); | ||
if (beatID) { | ||
emscripten_audio_node_connect(beatID, worklet, 0, 0); | ||
} | ||
bassID = createTrack(context, "audio_files/emscripten-bass-mono.mp3", true); | ||
if (bassID) { | ||
emscripten_audio_node_connect(bassID, worklet, 0, 1); | ||
} | ||
|
||
// 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 | ||
emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
#include <assert.h> | ||
#include <string.h> | ||
#include <stdio.h> | ||
|
||
#include <emscripten/em_js.h> | ||
#include <emscripten/webaudio.h> | ||
|
||
// Tests processing two stereo audio inputs being mixed to a single stereo audio | ||
// output in process() (by adding the inputs together). | ||
|
||
// This needs to be big enough for the stereo output, 2x inputs and the worker stack | ||
#define AUDIO_STACK_SIZE 4096 | ||
|
||
// 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* data) { | ||
audioProcessedCount++; | ||
|
||
// Single stereo output | ||
assert(numOutputs == 1 && outputs[0].numberOfChannels == 2); | ||
for (int n = 0; n < numInputs; n++) { | ||
// And all inputs are also stereo | ||
assert(inputs[n].numberOfChannels == 2 || inputs[n].numberOfChannels == 0); | ||
// This should always be the case | ||
assert(inputs[n].samplesPerChannel == outputs[0].samplesPerChannel); | ||
} | ||
// We can now do a quick mix since we know the layouts | ||
if (numInputs > 0) { | ||
int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels; | ||
float* outputData = outputs[0].data; | ||
memcpy(outputData, inputs[0].data, totalSamples * sizeof(float)); | ||
for (int n = 1; n < numInputs; n++) { | ||
// It's possible to have an input with no channels | ||
if (inputs[n].numberOfChannels == 2) { | ||
float* inputData = inputs[n].data; | ||
for (int i = totalSamples - 1; i >= 0; i--) { | ||
outputData[i] += inputData[i]; | ||
} | ||
} | ||
} | ||
} | ||
return true; | ||
} | ||
|
||
// Audio processor created, now register the audio callback | ||
void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* data) { | ||
if (!success) { | ||
printf("Audio worklet node creation failed\n"); | ||
return; | ||
} | ||
printf("Audio worklet processor created\n"); | ||
printf("Click to toggle audio playback\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, 0); | ||
} | ||
bassID = createTrack(context, "audio_files/emscripten-bass.mp3", true); | ||
if (bassID) { | ||
emscripten_audio_node_connect(bassID, worklet, 0, 1); | ||
} | ||
|
||
// 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 | ||
emscripten_set_timeout_loop(&playedAndMixed, 16, NULL); | ||
} |
Oops, something went wrong.