Skip to content

How to create new plugins

hyperpenelope edited this page May 17, 2022 · 59 revisions

Work in progress: This part of the documentation is still in draft form but do not hesitate to ask questions and propose changes.

Table of Contents


Introduction

The following listing shows the structure of Bulgroz, an example plugin included with Camomile:

Plugins/Examples/Bulgroz/
├── Bulgroz.pd
├── Bulgroz.png
├── Bulgroz.txt
├── delback~.pd
├── Infos.txt
├── param.change.pd
├── param.get.pd
├── param.set.pd
└── program.get.pd

The name of the directory determines the name of the plugin - this is what will show up in your DAW. So this plugin's name is "Bulgroz".

The main patch

Bulgroz.pd is the main patch which the plugin will load, because it has the same name as the plugin directory.

Plugin properties file

Bulgroz.txt contains information which determines how the Camomile plugin will behave when it is loaded into a DAW. For example, the number of audio channels is specified in this file, among other things.

The format of the plugin properties file is described in the section All Plugin Properties.

Other patches

If the main patch uses subpatches or abstractions, be sure to include those patches in the directory as well.

For example, the Bulgroz.pd patch uses a [delback~] object. Thus it expects to find delback~.pd in the plugin directory.

Miscellaneous

The plugin properties file can specify things like an image to display in the GUI or a plugin description held in a separate text file. These are optional, but, of course, if your plugin properties file makes reference to other files, they need to be in the plugin directory as well.


Audio

The use of audio-only requires to define the supported configuration of audio channels. However, many options and properties can be implemented and used to adapt the audio processing to the audio context and to offer more flexibility.

Important: The plugin should not be MIDI only. Ensure that the entry midionly is not defined or false (midionly false).

Main Audio Bus

An audio bus is a channel's configuration and corresponds to a number of input audio channels associated with a number of output audio channels. To support an audio bus, add the entry bus followed by the number of input audio channels and the number of output audio channels in the properties file of the plugin.

For example, adding the entry bus 2 2 enables the support for an audio channels configuration with two inputs associated with two outputs. In the patch, use the objects [adc~] and [dac~]with the standard syntax of arguments to receive and to send the audio signals.

Audio-Main-Bus

When no input channel is necessary, for a synthesizer, for example, using the entry bus 0 2 enables the support for an audio channels configuration with only two outputs. Or when only an input channel is necessary, for a volume unit meter, for example, using the entry bus 1 0 enables the support for an audio channels configuration with only one input.

In theory, a plugin accepts any number of input audio channels and any number of output audio channels, but in practice, most of the digital audio workstations highly restrict the possible configurations. Refer to the documentation of your digital audio workstation to see what are the available configurations.

Sample Rate

The digital audio workstation controls the sample rate of the audio chain. When the sample rate changes, the plugin stops the audio processing, changes the internal sample rate of Pd and restarts the audio processing. If the processing requires adaptation to the sample rate, for example when using FFT, in the patch, use the audio start notification to retrieve the running sample rate in order to adapt the audio processing.

Audio-Sample-Rate

Block Size

The digital audio workstation controls the size of the audio block, the number of samples per audio block, of the audio chain. Although the size of the audio block can be set in the preferences of the digital audio workstation, the number of samples per audio block is not guaranteed and may vary with each block. In addition, Pd internally requires that the block size be 64 samples but the plugin manages the proper operation of both systems - using a default latency of 64 samples but which is compensated by the digital audio workstation. In the main patch, the size of the audio block will always be 64 samples but it is still possible to use a larger or smaller block size in the abstractions and sub-patches using the object [block~].

Latency

Audio processing may involve latency, for example when using FFT, but digital audio workstations offer a system to compensate for this artefact. As this latency can change for many reasons, for example when the sample rate has changed, since version 1.0.4, the plugin offers a dynamic system for notifying the digital audio workstation. Thus to warn the digital audio workstation that the latency involved in the audio processing has changed, send the message [audio latency $1( with $1 replaced by the latency in samples to the plugin via the object [send camomile].

Audio-Latency

Tail Length

Audio processing may also involve an audio tail which should be taken into account by the digital audio workstations, for example when using delay lines. To define the audio tail length the plugin, use the entry taillength followed by the length in seconds in the properties file of the plugin. For example, if the tail length is 12.5 seconds, then add taillength 12.5.

Important: Digital audio workstations do not support dynamic changes of tail length. So always prefer a very long tail length that will suit all your needs.

Bypass

Users can bypass a plugin to preserve the incoming audio signals and MIDI events. By default, the plugin manages this functionality and since the VST3 format, a default parameter is used to do so. When a plugin is bypassed, the DSP method of Pd is avoided and the incoming signals are preserved and the MIDI events are not consumed. But if the patch implies latency, bypassing the plugin will create artefacts such as pre-echoes or time-shifting. Moreover, one could want to cross-fade the signals to avoid glitches and crackles for examples. Thus, since v1.0.6, Camomile offers to override the bypass parameter to manually control the bypass mechanism within the patch.

To override the default bypass parameter, set entry autobypass to false in the properties file of the plugin (autobypass false) and create manually new bypass parameter with the name Bypass or bypass, for example: param -name Bypass -list Off/On (see the parameter section for further information).

In the patch, you can receive the bypass state by using the index of the parameter. Here is an example: patch.

Audio-Bypass

In this example, the manual bypass is used to make-up the latency of the plugin and to avoid from crackles: patch.

Audio-Bypass

At last, to avoid unnecessary computation, the sub-patches and abstractions can be switched off using the object [block~]. If you use one or the two previous mechanisms to switch between the wet and dry signals and to make-up the latency, they must still be active. So in the previous example, in the Effect sub-patch, you can use: patch.

Audio-Bypass

Side-Chain

This feature is still experimental and the operation differs between plugin formats. Maybe the approach will change in the future to ensure more consistency.

Some digital audio workstations also support an extra audio bus for side-chain processing. A side-chain corresponds to one or more extra channels that can be fed by an external input like another track. For example, a compressor with a side-chain input controls the gain from main input to output based on the level of the signal at the side-chain input. To support an extra audio bus for side-chain processing, add a second entry bus followed by the number of input audio channels and the number of output audio channels required by the side-chain in the properties file of the plugin.

For example, the support for the main bus defined by two input channels with two output channels and a side-chain defined by one input channel with zero output channel required to add:

bus 2 2;
bus 1 0;

In the patch, use the objects [adc~] and [dac~]with the standard syntax of arguments to receive and to send the audio signals. The channels' numbers of the side-chain follow the channels' numbers of the main.

Audio-Side-Chain

In theory, a side-chain can correspond to any bus, but in practice, as for the main audio bus, the available configurations are restricted by the digital audio workstations. And keep in mind that even if side-chain processing is a common approach, all the digital audio workstations does not support this feature.

Multichannel

This feature is still experimental and the operation differs between plugin formats. Maybe the approach will change in the future to ensure more consistency.

Some digital audio workstations also well support multichannel processing and dynamic change of audio buses.


MIDI

By default, the MIDI support is disabled to avoid handling unnecessary information in order to optimize the plugin. To use MIDI events in the plugin, the MIDI input and/or the MIDI output must be enabled. If the plugin handles only MIDI event and no audio signal the plugin can also be optimized by disabling audio processing. MIDI support can be enabled regardless of whether the plugin is an effect or an instrument.

MIDI In

To support input MIDI events, the properties file of the plugin must contain the entry midiin true (default is midiin false). In the patch, use the MIDI objects [notein], [ctlin], [bendin], [touchin], [polytouchin], [pgmin], [midiin], [sysexin] and [midirealtimein] with the standard syntax of arguments to receive MIDI events. The MIDI port of the objects [midiin], [sysexin] and [midirealtimein] doesn't seem to be supported by the digital audio workstation so their port numbers are always set to zero.

MIDI In Example

Important: The objects [midiin], [sysexin] and [midirealtimein] have not been well tested yet. If you encounter any issue, feel free to report it.

MIDI Out

To support output MIDI events, the properties file of the plugin must contain the entry midiout true (default is midiout false). In the patch, use the MIDI objects [noteout], [ctlout], [bendout], [touchout], [polytouchout], [pgmout] and [midiout] with the standard syntax of arguments to send MIDI events. The MIDI port of the object [midiout] doesn't seem to be supported by the digital audio workstation so the port number is always set to zero.

MIDI Out Example

Important: The objects [midiout] has not been well tested yet. If you encounter any issue, feel free to report it.

MIDI Only

If the plugin doesn't process any audio signal, the audio processing can be bypassed using the entry midionly true in the properties file of the plugin (default is midionly false). This option optimizes the plugin and helps the digital audio workstation to manage the audio chain.

Important: The VST3 plugins require at least 1 audio channel even for MIDI only plugins otherwise the plugin doesn't receive the sample rate and the sample advancement.

Play Head

To support play head information, the properties file of the plugin must contain the entry playhead true (default is playhead false). In the patch, use the object [receive playhead] to retrieve the play head information.

MIDI Play Head Example

The object outputs lists of messages with different symbol selectors that can be used to route the information. At each DSP tick, each message is sent in this specific order:

  • playing followed by one number: 1 is the transport is currently looping and otherwise 0.
  • recording followed by one number: 1 is the transport is currently recording and otherwise 0.
  • looping followed by three numbers: 1 is the transport is currently looping and otherwise 0, the start position of the loop in units of quarter-notes and the end position of the loop in units of quarter-notes.
  • edittime followed by one number: the position of the start of the timeline in seconds.
  • framerate followed by one number: the video frame rate, if applicable.
  • bpm followed by one number: the tempo in beats per minute.
  • lastbar followed by one number: the position of the start of the last bar, in units of quarter-notes.
  • timesig followed by two numbers: the time signature numerator and the time signature denumerator.
  • position followed by three numbers: the current play head position in units of quarter-notes from the start of the timeline, the current play head position in samples from the start of the timeline and the current play head position in seconds from the start of the timeline.

MIDI Play Head Example

This information can be used to control the patch. For example, using the current play head position in units of quarter-notes with the time signature and the last bar information can be used to compute the play head position in the current bar that can trigger a MIDI note. Here is an example (patch):


Controls

This section covers the various methods to control the data related to the plugin status. These methods include the management of parameters, the data that can be modified in real time, and programs, the presets grouping the status of several parameters as well as additional meta-data.

Parameters

A parameter represents an aspect of the plugin engine with a number, such as the frequency of ring modulation for example. Parameters are used to record and play automations, save and recall presets, and so on. Thus, the parameters are part of the protocol that allows digital audio workstations to control the plugin engine in a real-time context.

The use of the parameters first requires defining them and then creating the communication system in the patch to interact with the digital audio workstations via the plugin.

Each new param entry in the plugin properties file adds a new parameter to the plugin. The only essential properties of the parameters are their indices defined according to their order of creation which depends on the reading of the property file from top to bottom:

param;
param;

Important: Digital audio workstations only support the addition or deletion of parameters at the end of the parameter lists. Inserting or deleting parameters in the middle of the list breaks the compatibility of automations and presets.

By default, a parameter is just a value between 0 and 1. A set of optional arguments can be used after the param entry to define several aspects of their properties using options.
A dash - with a name allows defining an option. The options are:

  • name followed by a string of characters defines the name, for example -name Frequency . May contain spaces and special characters other than minus (-).
  • label followed by a string of characters defines the label, for example -name Hertz.
  • min followed by a number defines the minimum value, for example -min 20 (the default is -min 0).
  • max followed by a number defines the maximum value, for example -max 44100 (the default is -max 1).
  • default followed by a number defines the default value, for example -default 220 (the default is the minimum value).
  • nsteps followed by a number defines the number of steps between the minimum and the maximum values, for example, a switch has two steps -nsteps 2 (the default is -nsteps 0 for continuous values).
  • auto followed by a boolean value defines if the parameter is automatable, for example -auto false, (the default is -auto true).
  • meta followed by a boolean value defines if the parameter is meta-parameter (if it controls other parameters), for example -meta true, (the default is -meta false).
  • list followed by a list of strings of characters enable the list mode of the parameter. The graphical interface no longer displays number but the elements of the list depending on the current value of the parameter. With the list mode, the minimum value is zero, the maximum value is the number of elements in the list minus one and the number of steps is the number of elements in the list (thus these options are no more supported). The elements of the list are separated by slash /, for example -list Triangular/Sawtooth/Square. Elements may contain spaces, but not the minus character (-). The element names are purely for usability, and the index (starting from 0) of the selected option is the value which will be stored and sent to your patch.

Here are examples of parameters definition:

param -name Frequency -label Hz -min 0 -max 2000 -default 220;
param -name Modulation -label % -min 0 -max 100 -default 0;
param -name Volume -label dB -min -90 -max 0 -default 0;
param -name Octave -min -3 -max 3 -default -1 -nsteps 7;
param -name Waveform -list Triangle/Sine/Narrow Rectangular/Square -default 2;

And here is the resulting interface in the digital audio workstation with the default values:

Parameter Representation

At each DSP tick, the plugin sends the current values of all parameters. In the patch, use the object [receive param] to retrieve these values as two-item lists that contain the index of the parameter followed by its value. So to select the value of a particular parameter, use the object [route] with the index of the argument parameter, for example, use [route 1] to select the value of the first parameter. Parameter indices start at 1 and lists are sent in ascending order. The set of values are sent each time, ie all 64 samples, even if the digital audio workstation has not changed them. Thus, to avoid repeated and unnecessary calculations, most uses require filtering the redundant values with the object [change]. The values sent are not normalized and cover the same ranges of values as those defined in their definitions. Finally, since the values ​​of the parameters are used in real time, if they directly control or modify audio signals, pay attention to their changes does not generate audio glitches or crackles. Here is an example (patch):

Parameter Receive

It is also possible to change the parameter values from the patch and to notify the digital audio workstation. First, to notify the digital audio workstation that a parameter is going to be changed, send the message [param change $1 1( via the object [send camomile] with $1 the parameter index For example, [param changes 2 1(, notifies the digital audio workstation that the second parameter will change. Subsequently, to set a new value, send the message [param set $1 $2( via the object [send camomile] with $1 to the parameter index and $2 to the new value. For example [param set 2 34.5(, gives the value 34.5 to the second parameter. The values of the parameters must be sent on the same value ranges as those used for their definitions. Finally, to notify the digital audio workstation that the change of a parameter is complete, send the message [param change $1 0 ( via the object send camomile with $1 the parameter index to For example, [param changes 2 0(, notifies the digital audio workstation that the second parameter is no longer changed. It is possible to modify the value of a parameter several times and over a greater or lesser period of time before notifying the digital audio workstation that the value changes have been completed. This prevents the digital audio workstation from overwriting the values with its own automations and presets. Here is an example (patch):

Parameter Receive

Changing the parameter values from the patch is essentially useful when using dedicated graphical interfaces. Refer to this section to understand how to set up this mechanism.

A better way to manage a large number of parameters is explained in this issue.

A set of objects to help manage parameters are included with the example plugins: [param.change], [param.set] and [param.get]. To use them in your patch, copy the corresponding files from an example plugin to your plugin folder. [param.change] has no inlets or outlets and manages parameter communication between the patch and camomile. You only need to make one of this per plugin.

[param.set param-index recieve-ID] takes up to two arguments: the index number of the parameter (required), and a receive ID to listen on (optional). Sending a value to this object’s inlet, or to the receive ID registered as the second argument, updates the parameter’s value.

[param.get param-index recieve-ID] takes up to two arguments: the index number of the parameter (required), and a send ID (optional). When the specified parameter is changed, the new value is sent out of this object’s outlet, and also to the optional send ID registered as the second argument.

Bear in mind that using these objects to interface between UI elements and your DSP code will only work when the plugin is loaded into a DAW, as it relies on Camomile sending parameter values on every DSP tick.

Programs

Most digital audio workstations offer the ability to save plugin states in the form of presets, called programs, which can then be reloaded. The various plugin formats offer methods allowing the plugin, on the one hand, to save in a file all the data necessary for the recall of a state and on the other hand, to recover this data from a file. These methods are usually also used when recording and opening a project. Basically, the plugin automatically saves the current value of each parameter when writing the file and retrieves these values ​​when reading the file. It is also possible to save and read additional data that can not be represented by parameters such as the contents of an audio buffer for example or simply text.

When saving a program, the plugin sends a message bang to the patch via the object [receive save]. When receiving this notification in the patch, send a list starting with the symbol [save ...( to the object [send camomile] to save the arguments of the list as additional data in the program. Several lists can be sent to save distinct data. When loading a program, the plugin sends back the additional data as lists of messages via the object [receive load]. If several messages have been saved within the program, all these messages will be returned successively in the same order.

Here an example that demonstrates how to save and load the content of two arrays within a program (patch):

Parameter Receive

Here an example that demonstrates how to save and load the content of a text object within a program (patch):

Parameter Receive

When the internal state of your plugin has changed, you have to notify the host - otherwise the host could possibly avoid the saving operation considering that the plugin seems unchanged - by sending the message [updated( via the object [send camomile]. Here is an example (patch):

Parameter Receive

It is possible to ignore the saving of the parameters within a program by setting the entry autoprogram to false (default is autoprogram true). In this case, if necessary, saving and reading parameters' values ​​should be done manually during the saving and reading of additional data.

The VST2 and Audio Unit plugin formats support factory programs, integrated directly into the plugin. To create factory a program, add the entry program followed by the name of the program in the plugin properties file, for example program Soft Distortion or program Hard Distortion.The programs will appear within the station. At their selection, the index of the program selected is sent to the patch via the object [receive program], programs are ordered from top to bottom starting by 1 as the parameters. When receiving this notification, it is possible for example to initialize the values of the parameters according to the method defined previously. Here is an example (patch):

Parameter Receive

The VST3 plugin format does not support factory programs. However, most digital audio workstations offer the ability to save programs to files. So just provide these files with the distribution of your plugin.


Graphical Interface

Basic

By default, a Camomile plugin will display the area of the patch which is marked as "graph-on-parent". In order to define the area of your patch which you want to be displayed, right-click on any empty space in your patch and click "Properties". Check the box labelled "Graph-On-Parent", and click "Apply". A red rectangle should appear on your patch; the contents within the rectangle's bounds will be displayed. Changing the values under "Range and size" allows you to adjust the size and shape of the rectangle.

Array Support

array-gui

But the version also offers a mechanism to display the content of the non-graphical array define object in a floating window like when the user clicks on it. From the patch, send the message array followed by the name of the array via the symbol camomile to open a window that displays the content of the array:

array-window

If the user changes the content of the array by clicking on it, the plugin sends back to the patch the name of the array that has been modified via the symbol array. Thus, the patch can use the information to retrieve the content of the plugin via the object array get:

array-mec

Openpanel and Savepanel Support

From the patch, send the message [openpanel( or [savepanel( to the plugin via the object [send camomile]. The plugin will show a dialogue window and once a file has been selected, it will send back a message containing the path of the file selected that can be received via respectively the objects [receive openpanel] and [receive savepanel]. In the message, you can use an optional argument to use a default path, for example [openpanel ~/Desktop/(. Here is an example: patch.

Panel

A small patching trick can be used to route the path received depending on which graphical object is clicked. These can be useful if the openpanel or savepanel functionalities must be used for several purposes, for example, to load several sound files. Here is an example: patch.

PanelDouble

Important: Loading or saving a file from or to your hard drive is time expensive and the operation can generate crackles and glitches and even crashes because the audio doesn't wait! To avoid such problem, you should use the flag -s in the message sent to camomile to suspend the processing when the plugin sends back the results: [openpanel -s( or [savepanel -s(. Here is an example: patch.

PanelSuspend

Dynamic modification

After changing the size of the visible area the patch, you can notify the plugin to resize the graphical interface by sending the messages [gui resize( via the symbol camomile. The operation will also redraw all the content of the graphical interface. If you just want to redraw the graphical interface because the margins of the patch changes, the objects changed or whatever, you can use the [gui redraw( via the symbol camomile.

Here is an example:

ezgif com-optimizebam


Dynamic Reload

The future version 1.0.4 will allow to dynamically reload the patch.

As explained previously, some characteristics of the plugin can be changed after the loading like the audio configurations supported, the parameters, etc. But as all these static characteristics are defined in the text file, nothing prevents to dynamically reload the patch. To do so, you only have to click on the fourth icon of the console (and now the console uses the icons set created by Gregor Cresnar because they are simple and pretty while mine were complicated and ugly).

reloadicon2

The patch can also be reloaded automatically each time you save it - each time the operating system assumes that the patch has been modified - by adding the entry autoreload to true in the text file.

autoreload true;

This feature can slow a bit the plugin so don't forget to set the value to false (or to remove the line) when you don't need it and especially when the plugin is ready to be released.


All Plugin Properties

The properties file is used to define the behaviour of the plugin, here are the properties:

type

effect or instrument.

Defines the type of plugin. It is important because the user must know which binary to use to generate the plugin. If the binary used is different from the type defined in the text, you will receive a warning in the console.

code

A 4 character string with at least 1 upper case used by the DAW to identify the plugin. It must be unique per plugin.

compatibility

The version of the plugin with which the patch has been created (for example v1.0.2). The plugin uses this to ensure that its compatibility with the patch. If the version of the plugin is inferior to the compatibility version then plugin posts a warning.

midiin

true or false.

Defines if the plugin accepts MIDI events. It can be used for effect and instrument plugin.

midiout

true or false.

Defines if the plugin generates MIDI events. It can be used for effect and instrument plugin.

midionly

true or false.

Defines if the plugin generates and receives only MIDI events without audio. It can be used for effect and instrument plugin.

playhead

true or false.

Defines if the plugin wants the play head information. It can be used for effect and instrument plugin.

key

true or false.

Defines if the plugin wants to receive key event via the objects key, keyup and keyname.

latency

Defines the latency of the plugin in samples (can also be changed dynamically).

taillength

Defines the tail length of the plugin is seconds.

bus

Defines a new bus for the plugin and must be followed by the number of inputs and the number of output. For example bus 2 2 if you want to add the support for 2 channels in and 2 channels out and bus 4 8 if you want to add the support for 4 channels in and 8 channels out. Important: multi-buses and side chain are still experimental, it could change in the future

program

Defines a new program (preset) for the plugin and must be followed the name of the program. For example program zozo. The order of the programs corresponds to their order in the text file from top to bottom.

param

Defines a new parameter for the plugin and can be followed by a set of optional arguments:

  • -name [name]: the parameter name
  • -label [label]: the label
  • -min [val]: the minimum value
  • -max [val]: the maximum value
  • -default [val]: the default value
  • -nsteps [steps]: the number of discrete steps between the minimum and maximum value
  • -auto [true/false]: whether the parameter is automatable; true by default
  • -meta [true/false]: whether the parameter is a meta-parameter; false by default
  • -list [option1]/[option2]/...: This will create a parameter that displays a list of possible values. The possible values are specified in a slash-separated list. In this case, the minimum is 0, the maximum is the number of elements in the list minus one and the number of steps is the number of elements in the list (if you set one of these options with the list option you will receive a warning in the console).

Example: param -name Frequency -label Hz -min 0 -max 5000 -default 500;

Example: param -name Waveform -list Triangular/Sawtooth/Square; creates a parameter to choose between three types of waveform.

Note: if you provide a default for a list parameter, the argument to -default must be the index of the default option in the list, and not the option string. For example, if you wanted a sawtooth wave to be the default option in the second example above, you must write param -name Waveform -list Triangular/Sawtooth/Square -default 1 and NOT -default Sawtooth.

image

Can be the name of an image file (for example myimage.png). The plugin displays the image as the background of the graphical interface.

description

Can be a short sentence (for example A chorus effect) or the name of a text file (for example Infos.txt). The plugin displays the text in the second tab of the auxiliary window. The text can be used to describe what the plugin does and how to use it but also the author, the credits and the version.