From f38b55ca4c87651b8fb9e9c14661974b3e97401a Mon Sep 17 00:00:00 2001 From: Carl Woffenden Date: Tue, 15 Oct 2024 14:51:07 +0200 Subject: [PATCH 1/2] Reword API to make it clearer --- .../api_reference/wasm_audio_worklets.rst | 4 +-- src/audio_worklet.js | 26 +++++++++---------- src/library_webaudio.js | 2 +- src/struct_info.json | 2 +- src/struct_info_generated.json | 2 +- src/struct_info_generated_wasm64.json | 2 +- system/include/emscripten/webaudio.h | 10 +++---- test/webaudio/audio_worklet_tone_generator.c | 10 +++---- test/webaudio/audioworklet.c | 2 +- 9 files changed, 30 insertions(+), 30 deletions(-) diff --git a/site/source/docs/api_reference/wasm_audio_worklets.rst b/site/source/docs/api_reference/wasm_audio_worklets.rst index 9a1f202ab7804..2f0cb9fdc849c 100644 --- a/site/source/docs/api_reference/wasm_audio_worklets.rst +++ b/site/source/docs/api_reference/wasm_audio_worklets.rst @@ -47,7 +47,7 @@ Once a class type is instantiated on the Web Audio graph and the graph is running, a C/C++ function pointer callback will be invoked for each 128 samples of the processed audio stream that flows through the node. Newer Web Audio API specs allow this to be changed, so for future compatibility use the -``AudioSampleFrame``'s ``quantumSize`` to get the value. +``AudioSampleFrame``'s ``samplesPerChannel`` to get the value. This callback will be executed on a dedicated separate audio processing thread with real-time processing priority. Each Web Audio context will @@ -159,7 +159,7 @@ which resumes the audio context when the user clicks on the DOM Canvas element t void *userData) { for(int i = 0; i < numOutputs; ++i) - for(int j = 0; j < outputs[i].quantumSize*outputs[i].numberOfChannels; ++j) + for(int j = 0; j < outputs[i].samplesPerChannel*outputs[i].numberOfChannels; ++j) outputs[i].data[j] = emscripten_random() * 0.2 - 0.1; // Warning: scale down audio volume by factor of 0.2, raw noise can be really loud otherwise return true; // Keep the graph output going diff --git a/src/audio_worklet.js b/src/audio_worklet.js index d56ce1a44486c..cd97ca9ded54b 100644 --- a/src/audio_worklet.js +++ b/src/audio_worklet.js @@ -31,12 +31,12 @@ function createWasmAudioWorkletProcessor(audioParams) { let opts = args.processorOptions; this.callbackFunction = Module['wasmTable'].get(opts['cb']); this.userData = opts['ud']; - // Plus the number of samples to process, fixed for the lifetime of the + // Then the samples per channel to process, fixed for the lifetime of the // context that created this processor. Note for when moving to Web Audio - // 1.1: the typed array passed to process() should be the same size as the - // the quantum size, and this exercise of passing in the value shouldn't - // be required (to be verified). - this.quantumSize = opts['qs']; + // 1.1: the typed array passed to process() should be the same size as this + // 'render quantum size', and this exercise of passing in the value + // shouldn't be required (to be verified). + this.samplesPerChannel = opts['qs']; } static get parameterDescriptors() { @@ -51,15 +51,15 @@ function createWasmAudioWorkletProcessor(audioParams) { let numInputs = inputList.length, numOutputs = outputList.length, numParams = 0, i, j, k, dataPtr, - quantumBytes = this.quantumSize * 4, + bytesPerChannel = this.samplesPerChannel * 4, stackMemoryNeeded = (numInputs + numOutputs) * {{{ C_STRUCTS.AudioSampleFrame.__size__ }}}, oldStackPtr = stackSave(), inputsPtr, outputsPtr, outputDataPtr, paramsPtr, didProduceAudio, paramArray; // Calculate how much stack space is needed. - for (i of inputList) stackMemoryNeeded += i.length * quantumBytes; - for (i of outputList) stackMemoryNeeded += i.length * quantumBytes; + for (i of inputList) stackMemoryNeeded += i.length * bytesPerChannel; + for (i of outputList) stackMemoryNeeded += i.length * bytesPerChannel; for (i in parameters) stackMemoryNeeded += parameters[i].byteLength + {{{ C_STRUCTS.AudioParamFrame.__size__ }}}, ++numParams; // Allocate the necessary stack space. @@ -71,13 +71,13 @@ function createWasmAudioWorkletProcessor(audioParams) { for (i of inputList) { // Write the AudioSampleFrame struct instance HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / 4 }}}] = i.length; - HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.quantumSize / 4 }}}] = this.quantumSize; + HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.samplesPerChannel / 4 }}}] = this.samplesPerChannel; HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / 4 }}}] = dataPtr; k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / 4 }}}; // Marshal the input audio sample data for each audio channel of this input for (j of i) { HEAPF32.set(j, dataPtr>>2); - dataPtr += quantumBytes; + dataPtr += bytesPerChannel; } } @@ -88,11 +88,11 @@ function createWasmAudioWorkletProcessor(audioParams) { for (i of outputList) { // Write the AudioSampleFrame struct instance HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.numberOfChannels / 4 }}}] = i.length; - HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.quantumSize / 4 }}}] = this.quantumSize; + HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.samplesPerChannel / 4 }}}] = this.samplesPerChannel; HEAPU32[k + {{{ C_STRUCTS.AudioSampleFrame.data / 4 }}}] = dataPtr; k += {{{ C_STRUCTS.AudioSampleFrame.__size__ / 4 }}}; // Reserve space for the output data - dataPtr += quantumBytes * i.length; + dataPtr += bytesPerChannel * i.length; } // Copy parameters descriptor structs and data to Wasm @@ -117,7 +117,7 @@ function createWasmAudioWorkletProcessor(audioParams) { // not have one, so manually copy all bytes in) for (i of outputList) { for (j of i) { - for (k = 0; k < this.quantumSize; ++k) { + for (k = 0; k < this.samplesPerChannel; ++k) { j[k] = HEAPF32[outputDataPtr++]; } } diff --git a/src/library_webaudio.js b/src/library_webaudio.js index 04f2aabb5766a..d840141ab0e3f 100644 --- a/src/library_webaudio.js +++ b/src/library_webaudio.js @@ -37,7 +37,7 @@ let LibraryWebAudio = { // Wasm handle ID. $emscriptenGetAudioObject: (objectHandle) => EmAudio[objectHandle], - // Performs the work of getting the AudioContext's quantum size. + // Performs the work of getting the AudioContext's render quantum size. $emscriptenGetContextQuantumSize: (contextHandle) => { // TODO: in a future release this will be something like: // return EmAudio[contextHandle].renderQuantumSize || 128; diff --git a/src/struct_info.json b/src/struct_info.json index f15a3509ac8a7..43f9e821a1027 100644 --- a/src/struct_info.json +++ b/src/struct_info.json @@ -1203,7 +1203,7 @@ "structs": { "AudioSampleFrame": [ "numberOfChannels", - "quantumSize", + "samplesPerChannel", "data" ], "AudioParamFrame": [ diff --git a/src/struct_info_generated.json b/src/struct_info_generated.json index a946110ed1db7..40051155baeed 100644 --- a/src/struct_info_generated.json +++ b/src/struct_info_generated.json @@ -479,7 +479,7 @@ "__size__": 12, "data": 8, "numberOfChannels": 0, - "quantumSize": 4 + "samplesPerChannel": 4 }, "EmscriptenBatteryEvent": { "__size__": 32, diff --git a/src/struct_info_generated_wasm64.json b/src/struct_info_generated_wasm64.json index 4742c879e77cf..d7c20f62c8b42 100644 --- a/src/struct_info_generated_wasm64.json +++ b/src/struct_info_generated_wasm64.json @@ -479,7 +479,7 @@ "__size__": 16, "data": 8, "numberOfChannels": 0, - "quantumSize": 4 + "samplesPerChannel": 4 }, "EmscriptenBatteryEvent": { "__size__": 32, diff --git a/system/include/emscripten/webaudio.h b/system/include/emscripten/webaudio.h index eb750dd979bfd..4ec2882cdce53 100644 --- a/system/include/emscripten/webaudio.h +++ b/system/include/emscripten/webaudio.h @@ -103,19 +103,19 @@ typedef int EMSCRIPTEN_AUDIO_WORKLET_NODE_T; typedef struct AudioSampleFrame { - // Number of audio channels to process (multiplied by quantumSize gives the elements in data) + // Number of audio channels to process (multiplied by samplesPerChannel gives the elements in data) const int numberOfChannels; // Number of samples per channel in data - const int quantumSize; - // An array of length numberOfChannels*quantumSize elements. Samples are always arranged in a planar fashion, - // where data[channelIndex*quantumSize+i] locates the data of the i'th sample of channel channelIndex. + const int samplesPerChannel; + // An array of length numberOfChannels*samplesPerChannel elements. Samples are always arranged in a planar fashion, + // where data[channelIndex*samplesPerChannel+i] locates the data of the i'th sample of channel channelIndex. float *data; } AudioSampleFrame; typedef struct AudioParamFrame { // Specifies the length of the input array data (in float elements). This will be guaranteed to either have - // a value of 1, for a parameter valid for the entire frame, or emscripten_audio_context_quantum_size() for a parameter that changes during the frame. + // a value of 1, for a parameter valid for the entire frame, or emscripten_audio_context_quantum_size() for a parameter that changes per sample during the frame. int length; // An array of length specified in 'length'. float *data; diff --git a/test/webaudio/audio_worklet_tone_generator.c b/test/webaudio/audio_worklet_tone_generator.c index 22faaccd897b7..659d5da27874c 100644 --- a/test/webaudio/audio_worklet_tone_generator.c +++ b/test/webaudio/audio_worklet_tone_generator.c @@ -40,12 +40,12 @@ bool ProcessAudio(int numInputs, const AudioSampleFrame *inputs, int numOutputs, // Produce a sine wave tone of desired frequency to all output channels. for(int o = 0; o < numOutputs; ++o) - for(int i = 0; i < outputs[o].quantumSize; ++i) + for(int i = 0; i < outputs[o].samplesPerChannel; ++i) { float s = emscripten_math_sin(phase); phase += phaseIncrement; for(int ch = 0; ch < outputs[o].numberOfChannels; ++ch) - outputs[o].data[ch*outputs[o].quantumSize + i] = s * currentVolume; + outputs[o].data[ch*outputs[o].samplesPerChannel + i] = s * currentVolume; } // Range reduce to keep precision around zero. @@ -150,11 +150,11 @@ int main() { EMSCRIPTEN_WEBAUDIO_T context = emscripten_create_audio_context(&attrs); - // Get the context's quantum size. Once the audio API allows this to be user - // defined or exposes the hardware's own value, this will be needed to + // Get the context's render quantum size. Once the audio API allows this to be + // user defined or exposes the hardware's own value, this will be needed to // determine the worklet stack size. int quantumSize = emscripten_audio_context_quantum_size(context); - printf("Context quantum size: %d\n", quantumSize); + printf("Context render quantum size: %d\n", quantumSize); // and kick off Audio Worklet scope initialization, which shares the Wasm // Module and Memory to the AudioWorklet scope and initializes its stack. diff --git a/test/webaudio/audioworklet.c b/test/webaudio/audioworklet.c index 0450f676d0892..4169475ff62b8 100644 --- a/test/webaudio/audioworklet.c +++ b/test/webaudio/audioworklet.c @@ -40,7 +40,7 @@ bool ProcessAudio(int numInputs, const AudioSampleFrame *inputs, int numOutputs, // Produce noise in all output channels. for(int i = 0; i < numOutputs; ++i) - for(int j = 0; j < outputs[i].quantumSize*outputs[i].numberOfChannels; ++j) + for(int j = 0; j < outputs[i].samplesPerChannel*outputs[i].numberOfChannels; ++j) outputs[i].data[j] = (rand() / (float)RAND_MAX * 2.0f - 1.0f) * 0.3f; // We generated audio and want to keep this processor going. Return false here to shut down. From 943fb11ceb7c3f629f9fb3c70ed7f71ad93c7de6 Mon Sep 17 00:00:00 2001 From: Carl Woffenden Date: Thu, 17 Oct 2024 14:47:16 +0200 Subject: [PATCH 2/2] Renamed 'qs' (render quantum size) to 'sc' (samples per channel) Following the rest of the renaming. --- src/audio_worklet.js | 2 +- src/library_webaudio.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/audio_worklet.js b/src/audio_worklet.js index cd97ca9ded54b..6ed827af22e7d 100644 --- a/src/audio_worklet.js +++ b/src/audio_worklet.js @@ -36,7 +36,7 @@ function createWasmAudioWorkletProcessor(audioParams) { // 1.1: the typed array passed to process() should be the same size as this // 'render quantum size', and this exercise of passing in the value // shouldn't be required (to be verified). - this.samplesPerChannel = opts['qs']; + this.samplesPerChannel = opts['sc']; } static get parameterDescriptors() { diff --git a/src/library_webaudio.js b/src/library_webaudio.js index d840141ab0e3f..f4269e9759baa 100644 --- a/src/library_webaudio.js +++ b/src/library_webaudio.js @@ -296,7 +296,7 @@ let LibraryWebAudio = { processorOptions: { 'cb': callback, 'ud': userData, - 'qs': emscriptenGetContextQuantumSize(contextHandle) + 'sc': emscriptenGetContextQuantumSize(contextHandle) } } : void 0;