Simple Audio Worklet
AudioWorklets handle audio in blocks of 128 frames at a time, provide parameters that can be arrays of either one or 128 values, and make setup that depends on parameters difficult.
This package abstracts the complexity and focuses on the actual processing, one frame at a time. It works with generator functions, classes, and pure functions, and produces efficient code that does not even allocate any memory after initialization.
If you are not already familiar with using Audio Worklets, see this article.
Usage
Whether a generator function, class, or pure function, use this package like this:
let processor = ;
where options looks like the following:
The implementation will be called each frame with args
as follows:
and expected to return either a number
or a Float32Array
.
A returned Float32Array
will map one-to-one with the output frame
channels, if it is long enough. If not, the first value of the array
will be treated as a returned number.
A returned number
will be treated as a mono frame, and will
be copied to all output channels.
Generator function
Generator functions work as straightforward representations of asynchronous processing, which makes them well suited to audio processing applications. Most cases will involve an infinite loop with all processing done inside, and optional setup done beforehand.
For many cases, a generator function is the most readable and writable way to express a DSP algorithm.
; { // setup would go here for ;; let gain = parameters; // or just use parameters.gain for let channel = 0; channel < inputlength; ++channel let noise = Math * 2 - 1 * gain; inputchannel += noise; // Safe and faster to modify input in place input; }let processor = ;
Note: Because of the nature of generators, there is a one-frame delay on the output, which may not be suitable for some cases. The very first frame is zero-filled.
Note: The identity of args, args.parameters, args.input, and args.port is guaranteed not to change. Therefore, it is okay to use
x;
whereas otherwise one would have to use
parameters input = x;
However, the values for the individual parameters must be accessed on the parameters object or updated every yield, or else the values will become outdated. For example,
{ let param = parameters; // BAD, will go stale for ;; let param = parameters; // OK ... parametersparam ...; // OK // compute something as result result; }
Class
The class version looks similar to the standards-compliant processor, but with frame-based processing as opposed to block-based processing. It must define a next() method instead of a process() method, and the parameters differ. It may be more familiar to object oriented programmers than the generator function, has no frame delay, and can use state.
; { // Initialize some instance variables, // same arguments as on first call to next() } { for let channel = 1; channel < inputlength; ++channel let noise = Math * 2 - 1 * parametersgain; inputchannel += noise; return input; } let processor = ;
Pure function
The pure function implementation is best when no state is necessary. Every output frame should be based solely on the frame input and the parameters, not on previous frames.
; { for let channel = 1; channel < inputlength; ++channel let noise = Math * 2 - 1 * parametersgain; inputchannel += noise; return input;} let processor = ;
It is important to keep this function pure in the sense that it does not store information by capturing variables and writing to them; alternatively, we can say the function must be memoryless. This limitation is in place because multiple instances of the processor would be capturing the same variables, leading to unexpected results. Capturing a constant value is fine.
Installation
$ npm install simple-audio-worklet
Browser Module
In Chromium and potentially other browsers, worklets can now use standard ES6 module loading. This means this package can be included into browser code with something like
where simple-audio-worklet.js
has been copied from
/node_modules/simple-audio-worklet/lib/index.js
and then running
let context = ;await contextaudioWorklet;
with my-audio-worklet.js
containing
;
Webpack
Packing files in worklet global scopes with packages like worklet-loader can work but is still relatively immature at time of writing.