Skip to content

Commit 37db147

Browse files
committed
Overhauled code and noted browser differences
This now works in Chrome, Firefox and Safari.
1 parent 48361fd commit 37db147

File tree

1 file changed

+33
-15
lines changed

1 file changed

+33
-15
lines changed

test/webaudio/audioworklet_params_mixing.c

Lines changed: 33 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
// Tests processing two stereo audio inputs being mixed to a single stereo audio
99
// output in process() (by adding the inputs together).
1010

11-
// This needs to be big enough for the stereo output, 2x inputs and the worker stack
12-
#define AUDIO_STACK_SIZE 4096
11+
// This needs to be big enough for the stereo output, 2x inputs, 2x params and the worker stack
12+
#define AUDIO_STACK_SIZE 5120
1313

1414
// Shared file playback and bootstrap
1515
#include "audioworklet_test_shared.inc"
@@ -20,26 +20,43 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi
2020

2121
// Single stereo output
2222
assert(numOutputs == 1 && outputs[0].numberOfChannels == 2);
23+
int outSamplesPerChannel = outputs[0].samplesPerChannel;
2324
for (int n = 0; n < numInputs; n++) {
2425
// And all inputs are also stereo
2526
assert(inputs[n].numberOfChannels == 2 || inputs[n].numberOfChannels == 0);
2627
// This should always be the case
27-
assert(inputs[n].samplesPerChannel == outputs[0].samplesPerChannel);
28+
assert(inputs[n].samplesPerChannel == outSamplesPerChannel);
2829
}
29-
// Interestingly, params won't have a length > 1 unless the value changes, but
30-
// we do know two params are incoming
30+
// Interestingly, params varies per browser. Chrome won't have a length > 1
31+
// unless the value changes, and FF has all the samples even for a k-rate
32+
// parameter. The only given is that two params are incoming.
3133
assert(numParams = 2);
34+
assert(params[0].length == 1 || params[0].length == outSamplesPerChannel);
35+
assert(params[1].length == 1 || params[1].length == outSamplesPerChannel);
3236
// We can now do a quick mix since we know the layouts
3337
if (numInputs > 0) {
34-
int totalSamples = outputs[0].samplesPerChannel * outputs[0].numberOfChannels;
38+
int totalSamples = outSamplesPerChannel * outputs[0].numberOfChannels;
39+
// Simple copy of single input's audio data, checking that we have channels
40+
// (since a muted input has zero channels)
3541
float* outputData = outputs[0].data;
36-
memcpy(outputData, inputs[0].data, totalSamples * sizeof(float));
42+
if (inputs[0].numberOfChannels != 0) {
43+
memcpy(outputData, inputs[0].data, totalSamples * sizeof(float));
44+
} else {
45+
// And for muted we need to full the buffer with zeroes otherwise it play the previous frame
46+
memset(outputData, 0, totalSamples * sizeof(float));
47+
}
48+
// Grab the mix level parameter (with either a length of 1 or the samples per channel)
49+
const AudioParamFrame* mixLevel = &params[0];
3750
for (int n = 1; n < numInputs; n++) {
38-
// It's possible to have an input with no channels
39-
if (inputs[n].numberOfChannels == 2) {
51+
if (inputs[n].numberOfChannels != 0) {
4052
float* inputData = inputs[n].data;
4153
for (int i = totalSamples - 1; i >= 0; i--) {
42-
outputData[i] += inputData[i] * params[0].data[(params[0].length > 1) ? i : 0]; // world's worst mixer...
54+
// Output and input buffers are stereo planar in this example so we
55+
// need to get a mixLevel->data[] per channel, hence the quick % (and
56+
// as noticed in the wild, implementations have either one or all
57+
// entries, regardless of the param spec we passed in)
58+
float mixLevelValue = mixLevel->data[(mixLevel->length > 1) ? (i % outSamplesPerChannel) : 0];
59+
outputData[i] += inputData[i] * mixLevelValue;
4360
}
4461
}
4562
}
@@ -53,8 +70,8 @@ EM_JS(void, doFade, (EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletID), {
5370
var worklet = emscriptenGetAudioObject(workletID);
5471
if (worklet) {
5572
// Emscripten's API creates these from a C array, indexing them instead of a
56-
// name, so technically 0 is "0" but we might as well use numerical indices.
57-
var param = worklet.parameters.get(0);
73+
// name. Chrome and FF work with 0 but Safari requires the correct "0".
74+
var param = worklet.parameters.get("0");
5875
if (param) {
5976
param.setTargetAtTime((param.value > 0.5) ? 0 : 1, 0 /* same as context.currentTime */, 0.5);
6077
}
@@ -64,6 +81,7 @@ EM_JS(void, doFade, (EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletID), {
6481
// Registered keypress event to call the JS doFade()
6582
bool onPress(int __unused type, const EmscriptenKeyboardEvent* e, void* data) {
6683
if (!e->repeat && data) {
84+
printf("Toggling fade\n");
6785
doFade(VOIDP_2_WA(data));
6886
}
6987
return false;
@@ -77,7 +95,7 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse
7795
}
7896
printf("Audio worklet processor created\n");
7997
printf("Click to toggle audio playback\n");
80-
printf("Keypress to fade the baseline in or out\n");
98+
printf("Keypress to fade the beat in or out\n");
8199

82100
// Stereo output, two inputs
83101
int outputChannelCounts[1] = { 2 };
@@ -93,11 +111,11 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse
93111
// Note: we can connect the sources to the same input and it'll get mixed for us, but that's not the point
94112
beatID = createTrack(context, "audio_files/emscripten-beat.mp3", true);
95113
if (beatID) {
96-
emscripten_audio_node_connect(beatID, worklet, 0, 0);
114+
emscripten_audio_node_connect(beatID, worklet, 0, 1);
97115
}
98116
bassID = createTrack(context, "audio_files/emscripten-bass.mp3", true);
99117
if (bassID) {
100-
emscripten_audio_node_connect(bassID, worklet, 0, 1);
118+
emscripten_audio_node_connect(bassID, worklet, 0, 0);
101119
}
102120

103121
// Register a click to start playback

0 commit comments

Comments
 (0)