8
8
// Tests processing two stereo audio inputs being mixed to a single stereo audio
9
9
// output in process() (by adding the inputs together).
10
10
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
13
13
14
14
// Shared file playback and bootstrap
15
15
#include "audioworklet_test_shared.inc"
@@ -20,26 +20,43 @@ bool process(int numInputs, const AudioSampleFrame* inputs, int numOutputs, Audi
20
20
21
21
// Single stereo output
22
22
assert (numOutputs == 1 && outputs [0 ].numberOfChannels == 2 );
23
+ int outSamplesPerChannel = outputs [0 ].samplesPerChannel ;
23
24
for (int n = 0 ; n < numInputs ; n ++ ) {
24
25
// And all inputs are also stereo
25
26
assert (inputs [n ].numberOfChannels == 2 || inputs [n ].numberOfChannels == 0 );
26
27
// This should always be the case
27
- assert (inputs [n ].samplesPerChannel == outputs [ 0 ]. samplesPerChannel );
28
+ assert (inputs [n ].samplesPerChannel == outSamplesPerChannel );
28
29
}
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.
31
33
assert (numParams = 2 );
34
+ assert (params [0 ].length == 1 || params [0 ].length == outSamplesPerChannel );
35
+ assert (params [1 ].length == 1 || params [1 ].length == outSamplesPerChannel );
32
36
// We can now do a quick mix since we know the layouts
33
37
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)
35
41
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 ];
37
50
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 ) {
40
52
float * inputData = inputs [n ].data ;
41
53
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 ;
43
60
}
44
61
}
45
62
}
@@ -53,8 +70,8 @@ EM_JS(void, doFade, (EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletID), {
53
70
var worklet = emscriptenGetAudioObject (workletID );
54
71
if (worklet ) {
55
72
// 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" );
58
75
if (param ) {
59
76
param .setTargetAtTime ((param .value > 0.5 ) ? 0 : 1 , 0 /* same as context.currentTime */ , 0.5 );
60
77
}
@@ -64,6 +81,7 @@ EM_JS(void, doFade, (EMSCRIPTEN_AUDIO_WORKLET_NODE_T workletID), {
64
81
// Registered keypress event to call the JS doFade()
65
82
bool onPress (int __unused type , const EmscriptenKeyboardEvent * e , void * data ) {
66
83
if (!e -> repeat && data ) {
84
+ printf ("Toggling fade\n" );
67
85
doFade (VOIDP_2_WA (data ));
68
86
}
69
87
return false;
@@ -77,7 +95,7 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse
77
95
}
78
96
printf ("Audio worklet processor created\n" );
79
97
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" );
81
99
82
100
// Stereo output, two inputs
83
101
int outputChannelCounts [1 ] = { 2 };
@@ -93,11 +111,11 @@ void processorCreated(EMSCRIPTEN_WEBAUDIO_T context, bool success, void* __unuse
93
111
// Note: we can connect the sources to the same input and it'll get mixed for us, but that's not the point
94
112
beatID = createTrack (context , "audio_files/emscripten-beat.mp3" , true);
95
113
if (beatID ) {
96
- emscripten_audio_node_connect (beatID , worklet , 0 , 0 );
114
+ emscripten_audio_node_connect (beatID , worklet , 0 , 1 );
97
115
}
98
116
bassID = createTrack (context , "audio_files/emscripten-bass.mp3" , true);
99
117
if (bassID ) {
100
- emscripten_audio_node_connect (bassID , worklet , 0 , 1 );
118
+ emscripten_audio_node_connect (bassID , worklet , 0 , 0 );
101
119
}
102
120
103
121
// Register a click to start playback
0 commit comments