-
Notifications
You must be signed in to change notification settings - Fork 118
Creating Custom Nodes
There will probably come a time in your Audiolet using life when it becomes necessary to write your own custom AudioletNode, either to add some extra DSP capability, do some tricky maths, or handle a difficult piece of routing between different inputs and channels. Whilst in theory writing custom nodes is relatively straightforward there are a few tricks which are used throughout Audiolet for performance or clarity, so in this tutorial we will run through the creation of a node step-by-step and try to highlight any areas which may not be immediately clear.
For this example we will look at a simple node which calculates Math.pow(sample, n) for each sample. To start with we can create our basic template for a node:
var Pow = function(audiolet, value) {
AudioletNode.call(this, audiolet, 2, 1);
};
extend(Pow, AudioletNode);
Pow.prototype.generate = function(inputBuffers, outputBuffers) {
};
Pow.prototype.toString = function() {
return 'Power';
};
Here you can see we create the new class as a subclass of AudioletNode. Where we call the constructor of AudioletNode we give three arguments, the audiolet object, a request for two inputs (one for the audio input, and one allowing us to dynamically control what power we are using), and a request for a single output. Finally we also create a generate function which will be automatically called to do our signal processing, and a simple toString method which can come in handy when debugging.
With our basic template we now need to fully populate the constructor:
var Pow = function(audiolet, value) {
AudioletNode.call(this, audiolet, 2, 1);
this.linkNumberOfOutputChannels(0, 0);
this.value = new AudioletParameter(this, 1, value || 1);
};
extend(Pow, AudioletNode);
This adds two further lines to the constructor. The first line calls the function linkNumberOfOutputChannels for the first input and the first output. This ensures that the output buffer has the same number of channels as the input buffer, so a mono input gives a mono output, a stereo input creates a stereo output and so on. The second line creates a parameter linked to the second input with an initial value of 1 for controlling which power we are raising the audio to.
With the constructor complete we can now start to look at the generate function which actually processes the audio.
Pow.prototype.generate = function() {
var input = this.inputs[0];
var output = this.outputs[0];
var value = valueParameter.getValue();
...
The first half of the generate function is basically preparation for the actual processing loop. The first two lines get the audio input and output buffers for reading from and writing to. Note that we get them both from the 0 index, corresponding to the node's first input and the first output. The final line gets the value for the power we are raising the audio to.
Now all of the preparation is complete we can move onto actually looping through and processing the audio.
...
var numberOfChannels = input.samples.length;
for (var i = 0; i < numberOfChannels; i++) {
output.samples[i] = Math.pow(input.samples[i], value);
}
};
Hopefully this final section is fairly self-explanatory. We loop through each channel in the input and grab a sample from each. This is then used to calculate the power, which is then placed in the output channel.
As you can see, when writing nodes there are a couple of little tricks you can use which you will see repeated throughout Audiolet's source code. Hopefully this tutorial will help you through using these tricks, and clears up the process of writing custom nodes. If you have written any nodes which you think would be more widely useful then please submit a pull request and we'll try to get them integrated fully into Audiolet.