BSE Plugin Development

= BSE Plugin Development = Authors: Tim Janik, Stefan Westerfeld

In this document, the basic concepts necessary for Bse plugin development are established, and an example plugin is being developed.

Getting Started
You don't need a full fledged software development environment in order build a Bse plugin, a proper Beast installation, your favourite text editor and a recent version of GCC will do fine on the technical side. Writing a synthesis plugin also requires at least some basic knowledge about digital audio mathematics (digital signal processing) and for Bse also a reasonably good idea of OO and C++. If you are new to digital audio processing in general you can find a link list to tutorials and DSP programming tips at the Beast Website. A good foundation in maths helps a lot in understanding the materiels covered.

Basic Concepts
The most central element realised in Bse for the creation and modification of audio signals is the "Module". A module almost always has one or more output signals and may have any amount of input signals. In addition, modules can define a variety of "Properties" which represent settings used to alter behaviour of a module.



Bse supports multiple dynamically allocated voices internally which are represented by a single object in GUIs like the one provided by Beast. For this reason, a distinction is made in module implementations between the "Module Object" corresponding to the object displayed at the GUI, and the "Engine Modules" of which there may be many per module object, used for audio processing within the dynamic voices. Properties and channels are registered with the module object class and individual property changes are sent to the module object. The module object then passes on property changes to the engine modules, taking care of syncronization issues which arise because engine modules live and process audio data in seperate threads.

To summarize, in order to write a Bse module, we need to:


 * define the module object
 * add properties to the module object
 * add input and output channels to the module object
 * define an engine module in the module object
 * implement the data processing function of the engine module.

Defining the module
In the past, the Bse plugin API went through quite a few major rewrites to make plugin implementations as easy as possible. At this point, a standard module needs three files for a full implementation:


 * 1) an .idl file containing class, property and channel definitions
 * 2) a .cc file containing the data processing and property handling logic
 * 3) an icon, 64 by 64 pixels RGBA format, saved as .png file.

Writing the IDL file
Bse provides an IDL compiler for object definitions to eliminate any mechanic code fragments the programmer would need to create manually otherwise and to generate binding and glue code between various Bse components, the IDL compiler is described in detail in the Sfidl Manual.

To make use of the BSE object tree, every plugin IDL file starts out with an include statement. Then a namespace is opened within which the desired classes, structures or choices (a type of enumeration) can be defined.

For most plugins, an ordinary class definition with properties and input and output channels suffices:

Here we have a small NotchFilter object, defined in the namespace Bse::Contrib which is the standard namespace for third-party Bse plugins, more on namespaces can be found in the Sfidl Namespaces. The object has a "frequency" property of type Real and an output channel "audio_out".

Input and output channels
In order for a module to effectively process audio, it needs input and output signal channels, unless its purely a producer for which signal inputs would be useless. Often a module needs "audio" channels and "control" channels, that is, some channels are used for audible signals, and other are used for non-audible control signals, such as volume. It is not entirely clear for every signal whether it is an audio or a control signal. Often it is its use that would best suit to provide a classification into either category but advanced knowledge of all possible uses is not available to the ordinary plugin writer. To fully support every kind of signal use, Bse doesn't make a technical distinction between so decided "audio" and "control" signals.

It is recommended however, that in order to improve usability, signals envisioned to be used primarily as audio signals are placed before other signals envisioned to be used as control signals in a class definition and therefore in generated GUIs. A good example for a module meeting this requirement is the BseAmplifier:

For classes such as :mix1: or :mix2:</tt> which indicate multi channel mixer effects (see class options), the audio channels put to actual use are also the first channels provided by a module.

A Bse module may have three different types of channels:


 * 1) IStream</tt> - an input stream, to be connected to a single output.
 * 2) JStream</tt> - an input stream, to be connected to many outputs (to join).
 * 3) OStream</tt> - an output stream, may be connected to any number of input streams.

Property types
Bse provides a large set of property constructors. By convention, all constructors expect a translatable label as first argument, a translatable description as second argument and an option string as last argument, as described in the Sfidl Properties. The various supported options are described in the next section. Here, we give an overview of the various constructors provided by Bse, grouped by types. Note that some property constructors already add extra options to the arguments given, for instance Trigger</tt> will automatically provide the options :trigger:</tt> and :skip-undo:</tt>.

Bool properties

 * Bool</tt> (label</tt>, description</tt>, default</tt>, options</tt>)
 * An ordinary property of type Bool</tt>. For GUIs and documentation generation, the translatable strings label</tt> and description</tt> are provided. The default value is given by default</tt>. Possible values (e.g. <tt>:trigger:</tt> which might be handy for some booleans) for <tt>options</tt> are discussed in the property options section.


 * <tt>Trigger</tt> (<tt>label</tt>, <tt>description</tt>, <tt>options</tt>)
 * This is a boolean property that's practically always <tt>FALSE</tt>. It may be set to <tt>TRUE</tt> in a trigger attempt though, so certain actions may be started in response. GUIs would usually display this option via a clickable button. All plugin's set_property methods automatically reset trigger property values to <tt>FALSE</tt> after invoking the property_changed method.

Real (IEEE-754 double precision floating point) properties

 * <tt>Real</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>minimum</tt>, <tt>maximum</tt>, <tt>stepping</tt>, <tt>options</tt>)
 * An ordinary property of type <tt>Real</tt>. For GUIs and documentation generation, the translatable strings <tt>label</tt> and <tt>description</tt> are provided. The default, minimum and maximum values for the <tt>Real</tt> property are given by the respective arguments. The <tt>stepping</tt> argument can be used by GUIs to provide increment/decrement editing abilities. Possible values for <tt>options</tt> are discussed in the property options section.


 * <tt>Perc</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>options</tt>)
 * Percentage property, the minimum and maximum are fixed 0 and 100 respectively, and additional options are provided for GUIs to recognize percentage properties as such.


 * <tt>DBVolume</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default_db</tt>, <tt>minimum_db</tt>, <tt>maximum_db</tt>, <tt>options</tt>)
 * A <tt>DBVolume</tt> property is used for <tt>Real</tt>-valued properties with values >0 which are used as volume factors and should be displayed and editable as values in decibel. The <tt>default_db</tt>, <tt>minimum_db</tt> and <tt>maximum_db</tt> parameters are converted into linear volume factors before being interpreted as property default and limits. For example, a <tt>DBVolume("", "", 0, -6, +6, STANDARD)</tt> property will actually default to 1.0 (0 decibel) and hold values in the range +0.501 (-6 decibel) .. +1.995 (+6 decibel).


 * <tt>Balance</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>options</tt>)
 * A <tt>Balance</tt> property is similar to a normal <tt>Real</tt> property, but has a fixed minimum and maximum of -100 and +100 and defaults to 0.


 * <tt>Gain</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>minimum</tt>, <tt>maximum</tt>, <tt>stepping</tt>, <tt>options</tt>)
 * <tt>Freq</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>options</tt>)
 * A <tt>Real</tt> property with logarithmic scale centered around A+2. The actual range of this property in Hertz goes from 0.00005=1/20000Hz to 20000Hz.


 * <tt>Frequency</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>minimum</tt>, <tt>maximum</tt>, <tt>options</tt>)
 * A <tt>Real</tt> property with logarithmic scale centered around A+2, setup with a <tt>stepping</tt> of 10. In order for the logarithmic scale to fit the parameter ranges, the <tt>minimum</tt> frequency must be smaller than 51.9Hz (corresponds to As-1) and the <tt>maximum</tt> frequency must be larger than 15053Hz (corresponds to Ais+6).


 * <tt>LogScale</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>minimum</tt>, <tt>maximum</tt>, <tt>stepping</tt>, <tt>center</tt>, <tt>base</tt>, <tt>n_steps</tt>, <tt>options</tt>)
 * Properties of this type can be displayed by using a logarithmic scale. The arguments <tt>base</tt> and <tt>n_steps</tt> determine the logarithmic scale which starts from <tt>center*base^(-n_steps)</tt> and reaches up to <tt>center*base^(+n_steps)</tt> with <tt>center*base^(0) = center</tt> in the middle. For example, a six octave scale with <tt>center=440</tt>, <tt>base=2</tt> and <tt>n_steps=3</tt> would look like this:
 * <tt>Minimum:     440 * 2^-3 = 440 * 0.125 =   55 (A-2)
 * Intermediate: 440 * 2^-2 = 440 * 0.25 =  110 (A-1)
 * Intermediate: 440 * 2^-1 = 440 * 0.5  =  220 (A)
 * Center:      440 * 2^0  = 440 * 1     =  440 (A+1)
 * Intermediate: 440 * 2^1 = 440 * 2     =  880 (A+2)
 * Intermediate: 440 * 2^2 = 440 * 4     = 1760 (A+3)
 * Maximum:     440 * 2^3  = 440 * 8     = 3520 (A+4)</tt>

Int (signed 32bit) properties

 * <tt>Int</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>minimum</tt>, <tt>maximum</tt>, <tt>stepping</tt>, <tt>options</tt>)
 * An ordinary property of type <tt>Int</tt>. For GUIs and documentation generation, the translatable strings <tt>label</tt> and <tt>description</tt> are provided. The default, minimum and maximum values for the <tt>Int</tt> property are given by the respective arguments. The <tt>stepping</tt> argument can be used by GUIs to provide increment/decrement editing abilities. Possible values for <tt>options</tt> are discussed in the property options section.


 * <tt>UInt</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>options</tt>)
 * Similar to <tt>Int</tt> where the minimum and maximum are fixed at 0 and 2147483647.


 * <tt>Octave</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>options</tt>)
 * An integer property ranging from -4 to +6 with variable default.


 * <tt>FineTune</tt> (<tt>label</tt>, <tt>description</tt>, <tt>options</tt>)
 * <tt>Note</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>options</tt>)
 * Int property within 0 and 131 (sometimes also uses 132 to denote no/unspecified/unparsable note). For the <tt>default</tt> value, Bse provides a constant <tt>KAMMER_NOTE</tt> and a set of macros taking an octave argument: <tt>NOTE_C(octave)</tt>, <tt>NOTE_Cis(octave)</tt>, <tt>NOTE_Des(octave)</tt>, <tt>NOTE_D(octave)</tt>, <tt>NOTE_Dis(octave)</tt>, <tt>NOTE_Es(octave)</tt>, <tt>NOTE_E(octave)</tt>, <tt>NOTE_F(octave)</tt>, <tt>NOTE_Fis(octave)</tt>, <tt>NOTE_Ges(octave)</tt>, <tt>NOTE_G(octave)</tt>, <tt>NOTE_Gis(octave)</tt>, <tt>NOTE_As(octave)</tt>, <tt>NOTE_A(octave)</tt>, <tt>NOTE_Ais(octave)</tt>, <tt>NOTE_Bes(octave)</tt>, <tt>NOTE_B(octave)</tt>.

Other properties

 * <tt>Num</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>minimum</tt>, <tt>maximum</tt>, <tt>stepping</tt>, <tt>options</tt>)
 * A signed 64bit numeric property similar to <tt>Int</tt> covering the integer range -9223372036854775808 til 9223372036854775807.


 * <tt>String</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>options</tt>)
 * String property, the <tt>String</tt> constructor is used to provide a translatable label and translatable description and to define the default string value. For some strings it makes sense to provide additional options such as <tt>:searchpath:</tt>.


 * <tt>Choice</tt> (<tt>label</tt>, <tt>description</tt>, <tt>default</tt>, <tt>options</tt>)
 * Choice property, the <tt>Choice</tt> constructor is used to provide a translatable label and translatable description and to define the default choice value.


 * <tt>BBlock</tt> (<tt>label</tt>, <tt>description</tt>, <tt>options</tt>)
 * Byte block property, the <tt>BBlock</tt> constructor is used to provide a label and description.


 * <tt>FBlock</tt> (<tt>label</tt>, <tt>description</tt>, <tt>options</tt>)
 * Float value block property, the <tt>FBlock</tt> constructor is used to provide a label and description.


 * <tt>Record</tt> (<tt>label</tt>, <tt>description</tt>, <tt>options</tt>)
 * Record type property, the <tt>Record</tt> constructor is used to provide a translatable label and translatable description for properties that have the type of a previously defined record (see Sfidl Composite Types).


 * <tt>Sequence</tt> (<tt>label</tt>, <tt>description</tt>, <tt>options</tt>)
 * Sequence type property, the <tt>Sequence</tt> constructor is used to provide a translatable label and translatable description for properties that have the type of a previously defined sequence (see Sfidl Composite Types).


 * <tt>Object</tt> (<tt>label</tt>, <tt>description</tt>, <tt>options</tt>)
 * Object property, the <tt>Object</tt> constructor is used to provide a translatable label and translatable description for properties that have the type of a previously defined object (see Sfidl Classes).

Property options
A special cpability of IDL properties is behavioural adjustment through an added option string. Several options can be combined in such a string by concatenating them with ":", and options can be enabled or disabled by postfixing them with "+" or "-". The following lists describe the property options currently supported (defined) by Bse.

Property options from <tt>GParamSpec</tt>:


 * <tt>:r:</tt>
 * the property is readable (same as <tt>G_PARAM_READABLE</tt>)


 * <tt>:w:</tt>
 * the property is writable (same as <tt>G_PARAM_WRITABLE</tt>)


 * <tt>:construct:</tt>
 * the property is writable (same as <tt>G_PARAM_CONSTRUCT</tt>)


 * <tt>:construct-only:</tt>
 * the property is writable (same as <tt>G_PARAM_CONSTRUCT_ONLY</tt>)


 * <tt>:lax-validation:</tt>
 * the property is writable (same as <tt>G_PARAM_LAX_VALIDATION</tt>)

BSE core options:


 * <tt>:S:</tt>
 * the property is serializable


 * <tt>:f:</tt>
 * float indicator, reduce serialization precision to IEEE 754 Single Precision Floating Point


 * <tt>:skip-default:</tt>
 * do not serialize property if its set to its default value


 * <tt>:skip-undo:</tt>
 * do not record property changes to undo/redo mechanism


 * <tt>:unprepared:</tt>
 * the property is writable only for unprepared objects

GUI Options:


 * <tt>:G:</tt>
 * the property should be represented by GUIs


 * <tt>:ro:</tt>
 * for GUI representation purposes, the property should be considered read-only (non-editable)


 * <tt>:trigger:</tt>
 * a hint to display the property with a trigger button


 * <tt>:radio:</tt>
 * a hint to display the property with a radio button


 * <tt>:dial:</tt>
 * a hint to display the property with dial knob


 * <tt>:scale:</tt>
 * a hint to display the property with a scale adjustment


 * <tt>:log-scale:</tt>
 * a hint to display the property with a logarithmic scale adjustment


 * <tt>:db-value:</tt>
 * a hint indicating a decibel valued property (as opposed to a property that is real valued but for which dB values should be displayed)


 * <tt>:db-volume:</tt>
 * a hint indicating a dB volume scale for a dB or real valued property


 * <tt>:db-range:</tt>
 * a hint indicating a dB scale with smooth curvature for a dB or real valued property


 * <tt>:searchpath:</tt>
 * a hint indicating a property consisting of colon seperated directory list


 * <tt>:filename:</tt>
 * a hint indicating a property consisting of a filename


 * <tt>:rgb:</tt>
 * a hint indicating a property consisting of an RGB color value


 * <tt>:hex:</tt>
 * a hint indicating a preference for base 16 in numeric editing


 * <tt>:item-sequence:</tt>
 * the property type is a sequence of item objects


 * <tt>:note-sequence:</tt>
 * the property type is a sequence of notes

Predefined options sets:


 * <tt>READWRITE</tt>
 * property is readable and writable (<tt>:r:w:</tt>)


 * <tt>GUI</tt>
 * property is READWRITE and GUI representable (<tt>:r:w:G:</tt>)


 * <tt>GUI_RDONLY</tt>
 * property is GUI but non-editable (<tt>:r:w:G:ro:</tt>)


 * <tt>GUI_READABLE</tt>
 * property is readable and GUI representable but not writable (<tt>:r:G:</tt>)


 * <tt>STORAGE</tt>
 * property is READWRITE and serializable (<tt>:r:w:S:</tt>)


 * <tt>STANDARD</tt>
 * property is READWRITE, STORAGE and GUI (<tt>:r:w:S:G:</tt>)


 * <tt>STANDARD_RDONLY</tt>
 * property is READWRITE, STORAGE, GUI_RDONLY (<tt>:r:w:S:G:ro:</tt>)

Extra option sets (mostly for convenience and readability):


 * <tt>SKIP_DEFAULT</tt>
 * skip default value serialization (<tt>:skip-default:</tt>)


 * <tt>SKIP_UNDO</tt>
 * property changes are not undo/redo recorded (<tt>:skip-undo:</tt>)

Class options
The property option mechanism described in the last section has been adapted for classes at some point. Currently, applications of this feature a very rare, but expected to increase in the future. As such, the recognized option set for classes also is very limited at the moment.

Predefined class options:


 * <tt>:unstable:</tt>
 * code is (still) unstable and enabled only by <tt>--devel</tt>


 * <tt>:deprecated:</tt>
 * code is deprecated and scheduled for removal, enabled only by <tt>--devel</tt>


 * <tt>:mix1:</tt>
 * this class implements a mono channel mixer effect (also see channels)


 * <tt>:mix2:</tt>
 * this class implements a stereo channel mixer effect


 * <tt>:mix5.1:</tt>
 * this class implements a 5.1 channel mixer effect

Adding a <tt>PNG</tt> icon
Every GUI representable object should have an icon eventually. Once an image file exists in the desired format, it is easily integrated into a class definition:

Images to be used as class icons should meet a few requirements:


 * 1) Size: the class icon should be 64 pixels wide and 64 pixels heigh.
 * 2) Colors: the image should be provided in RGBA format with transparent background.
 * 3) Format: the image should be provided in a common image format, <tt>PNG</tt> is fully supported by sfidl.1.

Compiling and installing a plugin
After writing a plugin, to use it in beast, it needs to be compiled and installed. Here is a brief example which assumes that the idl file is called bseportamento.idl, and the implementation is the C++ source bseportamento.cc.

You can get the source code of the plugin from the bugtracker, at http://bugzilla.gnome.org/show_bug.cgi?id=353137. The plugin wasn't designed for learning to write plugins, though, we'll publish a "real" example plugin at some later point in time.

Line by line walk through:


 * 1) The "sfidl" line is compiling your IDL file into a C++ header, that provides the necessary definitions for implementing the plugin. You need to repeat this step whenever you change the IDL file.
 * 2) The "g++" line is compiling the C++ source and the generated C++ header to a binary. Note that you need to have libbse installed, along with its pkg-config file, for this step to work. The pkg-config file might be provided by a separate package of your distribution, such as beast-dev.
 * 3) The "mkdir" line is creating a directory for the plugin.
 * 4) The final step copies the binary plugin to beasts plugin path.