|
BSE Plugin Development
Tim Janik
Document revised: Mon May 08 01:37:45 2006
In this document, the basic concepts necessary for BSE plugin development
are established, and an example plugin is being developed.
1 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.
2 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.
3 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:
- an .idl file containing class, property and channel definitions
- a .cc file containing the data processing and property
handling logic
- an icon, 64 by 64 pixels RGBA format, saved as .png file.
3.1 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:
#include <bse/bse.idl> // include BSE classes and definitions
namespace Bse { namespace Contrib { // enter the namespace Bse::Contrib
class NotchFilter : Effect { // derive Notchfilter from Bse::Effect
Real frequency; // a property
OStream audio_out; // output signal
};
} } // Bse::Contrib
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".
3.2 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:
class Amplifier : Effect {
IStream audio_in1 = (_("Audio In1"), _("First audio input"));
IStream audio_in2 = (_("Audio In2"), _("Second audio input"));
IStream ctrl_in1 = (_("Ctrl In1"), _("First control input"));
IStream ctrl_in2 = (_("Ctrl In2"), _("Second control input"));
OStream audio_out = (_("Audio Out"), _("Amplified audio output"));
};
For classes such as :mix1: or :mix2: 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:
- IStream - an input stream, to be connected to a single output.
- JStream - an input stream, to be connected to many outputs (to join).
- OStream - an output stream, may be connected to any number of input streams.
3.3 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() will automatically provide the options :trigger:
and :skip-undo:.
3.3.1 Bool properties
- Bool (label, description, default, options)
An ordinary property of type Bool. For GUIs and documentation generation, the
translatable strings label and description are provided. The default
value is given by default. Possible values (e.g. :trigger: which
might be handy for some booleans) for options are discussed in the
property options section.
- Trigger (label, description, options)
This is a boolean property that's practically always FALSE. It may be
set to TRUE 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 FALSE after invoking the property_changed() method.
3.3.2 Real (IEEE-754 double precision floating point) properties
3.3.3 Int (signed 32bit) properties
- Int (label, description, default, minimum, maximum, stepping, options)
An ordinary property of type Int. For GUIs and documentation generation, the
translatable strings label and description are provided. The default,
minimum and maximum values for the Int property are given by the respective arguments.
The stepping argument can be used by GUIs to provide increment/decrement
editing abilities. Possible values for options are discussed in the
property options section.
- UInt (label, description, default, options)
Similar to Int() where the minimum and maximum are fixed at 0 and 2147483647.
- Octave (label, description, default, options)
An integer property ranging from -4 to +6 with variable default.
- FineTune (label, description, options)
- Note (label, description, default, options)
Int property within 0 and 131 (sometimes also uses 132 to denote no/unspecified/unparsable note).
For the default value, BSE provides a constant KAMMER_NOTE and a set of macros taking
an octave argument:
NOTE_C(octave), NOTE_Cis(octave), NOTE_Des(octave), NOTE_D(octave),
NOTE_Dis(octave), NOTE_Es(octave), NOTE_E(octave), NOTE_F(octave),
NOTE_Fis(octave), NOTE_Ges(octave), NOTE_G(octave), NOTE_Gis(octave),
NOTE_As(octave), NOTE_A(octave), NOTE_Ais(octave), NOTE_Bes(octave),
NOTE_B(octave).
3.3.4 Other properties
- Num (label, description, default, minimum, maximum, stepping, options)
A signed 64bit numeric property similar to Int covering the integer
range -9223372036854775808 til 9223372036854775807.
- String (label, description, default, options)
String property, the String() 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 :searchpath:.
- Choice (label, description, default, options)
Choice property, the Choice() constructor is used to provide a
translatable label and translatable description and to define the default choice value.
- BBlock (label, description, options)
Byte block property, the BBlock() constructor is used to provide a label and description.
- FBlock (label, description, options)
Float value block property, the FBlock() constructor is used to provide a label and description.
- Record (label, description, options)
Record type property, the Record() 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).
- Sequence (label, description, options)
Sequence type property, the Sequence() 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).
- Object (label, description, options)
Object property, the Object() 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).
3.4 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 GParamSpec:
- :r: - the property is readable (same as G_PARAM_READABLE)
- :w: - the property is writable (same as G_PARAM_WRITABLE)
- :construct: - the property is writable (same as G_PARAM_CONSTRUCT)
- :construct-only: - the property is writable (same as G_PARAM_CONSTRUCT_ONLY)
- :lax-validation: - the property is writable (same as G_PARAM_LAX_VALIDATION)
BSE core options:
- :S: - the property is serializable
- :f: - float indicator, reduce serialization precision to IEEE 754 Single Precision Floating Point
- :skip-default: - do not serialize property if its set to its default value
- :skip-undo: - do not record property changes to undo/redo mechanism
- :unprepared: - the property is writable only for unprepared objects
GUI Options:
- :G: - the property should be represented by GUIs
- :ro: - for GUI representation purposes, the property should be considered read-only (non-editable)
- :trigger: - a hint to display the property with a trigger button
- :radio: - a hint to display the property with a radio button
- :dial: - a hint to display the property with dial knob
- :scale: - a hint to display the property with a scale adjustment
- :log-scale: - a hint to display the property with a logarithmic scale adjustment
- :db-value: - a hint indicating a decibel valued property
(as opposed to a property that is real valued but for which dB values should be displayed)
- :db-volume: - a hint indicating a dB volume scale for a dB or real valued property
- :db-range: - a hint indicating a dB scale with smooth curvature for a dB or real valued property
- :searchpath: - a hint indicating a property consisting of colon seperated directory list
- :filename: - a hint indicating a property consisting of a filename
- :rgb: - a hint indicating a property consisting of an RGB color value
- :hex: - a hint indicating a preference for base 16 in numeric editing
- :item-sequence: - the property type is a sequence of item objects
- :note-sequence: - the property type is a sequence of notes
Predefined options sets:
- READWRITE - property is readable and writable (:r:w:)
- GUI - property is READWRITE and GUI representable (:r:w:G:)
- GUI_RDONLY - property is GUI but non-editable (:r:w:G:ro:)
- GUI_READABLE - property is readable and GUI representable but not writable (:r:G:)
- STORAGE - property is READWRITE and serializable (:r:w:S:)
- STANDARD - property is READWRITE, STORAGE and GUI (:r:w:S:G:)
- STANDARD_RDONLY - property is READWRITE, STORAGE, GUI_RDONLY (:r:w:S:G:ro:)
Extra option sets (mostly for convenience and readability):
- SKIP_DEFAULT - skip default value serialization (:skip-default:)
- SKIP_UNDO - property changes are not undo/redo recorded (:skip-undo:)
3.5 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:
- :unstable: - code is (still) unstable and enabled only by --devel
- :deprecated: - code is deprecated and scheduled for removal, enabled only by --devel
- :mix1: - this class implements a mono channel mixer effect (also see channels)
- :mix2: - this class implements a stereo channel mixer effect
- :mix5.1: - this class implements a 5.1 channel mixer effect
3.6 Adding a PNG 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:
class NotchFilter : Effect {
Info icon = "notch.png"; // use the image notch.png as class icon
};
Images to be used as class icons should meet a few requirements:
- Size: the class icon should be 64 pixels wide and 64 pixels heigh.
- Colors: the image should be provided in RGBA format with transparent background.
- Format: the image should be provided in a common image format, PNG is fully supported by sfidl(1).
4.1 Module object methods
4.2 Engine module methods
5 An example notch filter
|
|