#include <AudioEffect.hpp> class AudioEffect;
Needs to be defined by the audio effect and is called to create the audio effect object instance.
There is a basic set of methods that your plug should support (with *). These are all found in the AudioEffect class.
Create an AudioEffect
object. The constructor of your class is
passed a parameter of the type audioMasterCallback. The actual mechanism in
which your class gets constructed is not important right now, but effectively
your class is constructed by the hosting application, and the host passes an
object of type audioMasterCallback that handles the interaction with the
plug-in. You pass this on to the base class's constructor and then can forget
about it.
A few flags, and identifiers must be set and you declare your class's input & output requirements at construction time.
audioMaster |
|
---|---|
numPrograms |
The number of programs. |
numParams |
The number of parameters. |
MyPlug::MyPlug (audioMasterCallback audioMaster)
: AudioEffectX (audioMaster, 1, 1)
// 1 program, 1 parameter only
{
myParameter = 1.0;
// stereo in :
setNumInputs (2);
// stereo out :
setNumOutputs (2);
// identify
// (you must change this for other plugs!) :
setUniqueID ('MyPl');
// makes sense to feed both inputs
// with the same signal :
canMono ();
// supports both accumulating
// and replacing output :
canProcessReplacing ();
// default program name :
strcpy (programName, "Default");
}
Destroy an AudioEffect
object.
MyPlug::~MyPlug ()
{
// nothing to do here
}
Never call any Mac OS 9 functions (or other functions which call into the OS)
inside your audio process function; this will crash the system when your plug
is run in MP (multiprocessor) mode. if you must call into the OS, you must use
MPRemoteCall
()
(see Apples' documentation), or explicitly use functions which
are documented by Apple to be MP safe.
On Mac OS X read the system header files to be sure that you only call thread safe functions.
Audio processing in the plug is accomplished by one of 2 methods, namely process
,
and processReplacing
. The process
method must be
provided, while the second one is optional (but it is highly recommended to
always implement both methods). While process
takes input data,
applies its pocessing algorithm, and then adds the result to the output
(accumulating), processReplacing
overwrites the output buffer. The
host provides both input and output buffers for either process method.
The reasoning behind the accumulating version (process) is that it is much more efficient in the case where many processors operate on the same output (aux-send bus), while for simple chained connection schemes (inserts), the replacing method is more efficient.
The plug has to over-ride both these two possible member functions that actually
do the work. These are repeatedly called by the host application, each time
with a new block of data.
inputs |
An array of pointers to the data. |
---|---|
outputsuts |
An array of pointers to where the datacan be written to. |
sampleFrames |
How big the block is. |
void MyPlug::process (float **inputs, float **outputs, long sampleFrames)
{
float *in1 = inputs[0];
float *in2 = inputs[1];
float *out1 = outputs[0];
float *out2 = outputs[1];
while (--sampleFrames >= 0)
{
(*out1++) += (*in1++);
(*out2++) += (*in2++);
}
}
Audio processing in the plug is accomplished by one of 2 methods, namely process
,
and processReplacing
. The process
method must be
provided, while the second one is optional (but it is highly recommended to
always implement both methods). While process
takes input data,
applies its pocessing algorithm, and then adds the result to the output
(accumulating), processReplacing
overwrites the output buffer. The
host provides both input and output buffers for either process method.
The reasoning behind the accumulating version (process
) is that it
is much more efficient in the case where many processors operate on the same
output (aux-send bus), while for simple chained connection schemes (inserts),
the replacing method is more efficient.
inputs |
An array of pointers to the data. |
---|---|
outputsuts |
An array of pointers to where the datacan be written to. |
sampleFrames |
How big the block is. |
void MyPlug::processReplacing (float **inputs, float **outputs, long sampleFrames)
{
float *in1 = inputs[0];
float *in2 = inputs[1];
float *out1 = outputs[0];
float *out2 = outputs[1];
while (--sampleFrames >= 0)
{
(*out1++) = (*in1++);
(*out2++) = (*in2++);
}
}
Parameters are the individual parameter settings the user can adjust. A VST host can automate these parameters.
Set parameter <index>
to <value>
.
index |
The index of the parameter. |
---|---|
value |
A float between 0.0 and 1.0 inclusive. |
Parameter values, like all VST parameters, are declared as floats with an inclusive range of 0.0 to 1.0. How data is presented to the user is merely in the user-interface handling. This is a convention, but still worth regarding. Maybe the VST-host's automation system depends on this range.
void MyPlug::setParameter (long index, float value)
{
switch (index) {
case kMyTag :
myParameter = value;
break;
default :
break;
}
}
index |
The index to the parameter. |
---|---|
return |
The value of the parameter. Should be a float between
0.0 and 1.0 inclusive. |
Return the value of parameter <index>
.
float MyPlug::getParameter (long index)
{
float value = 0.0;
switch (index)
{
case kMyTag :
value = myParameter;
break;
default :
break;
}
return (value);
}
Called after a control has changed in the editor, as from CControlListener
's
valueChanged
method.
index |
The index of the parameter. |
---|---|
value |
A float between 0.0 and 1.0 inclusive. |
An important thing to notice is that if the user changes a parameter in your
editor (which is out of the host's control Ðbecause we are not using the
default string based interface), you should call... setParameterAutomated
(index, (float) newValue);
. This ensures that the host is notified
of the parameter change, which allows it to record these changes for
automation.
Note that setParameterAutomated
calls the plug's setParameter
,
and in the case of an AEffGuiEditor
, the editor's setparameter
will also be called after setParameterAutomated
.
void MyPlugEdit::setParameter (long index, float value)
{
MyPlug::setParameter (index, value);
if (editor)
(editor)->setParameter (index, value);
}
Stuff <text>
with a string representation ("0.5", "-3",
"PLATE", etc...) of the value of parameter <index>
.
index |
The index of the parameter. |
---|---|
text |
A string. |
void MyPlug::getParameterDisplay (long index, char *text)
{
float2string (fMyParameter, text);
}
Stuff <text>
with the name ("Time", "Gain", "RoomType",
etc...) of parameter <index>
.
index |
The index of the parameter |
---|---|
text |
A string up to 8 chars. |
void MyPlug::getParameterName (long index, char *label) { if (index == 0) strcpy (label, "my name0"); else if (index == 1) strcpy (label, "my name1"); }
Stuff <label>
with the units in which parameter <index>
is displayed (i.e. "sec", "dB", "type", etc...).
index |
The index of the parameter |
---|---|
text |
A string up to 8 chars. |
void MyPlug::getParameterLabel (long index, char *label)
{
if (index == 0)
strcpy (label, "my label0");
else if (index == 1)
strcpy (label, "my label1");
}
Programs are a complete set of parameters that refer to the current state of the plug-in. Banks are a collection of Program objects.
Return the index to the current program.
return |
The number of the current program. |
---|
Set the current program to <program>
.
program |
The number of the program. |
---|
Don't forget to call this from the constructor.
Stuff the name field of the current program with <name>
. The
program name is displayed in the rack, and can be edited by the user.
name |
A string up to 24 chars. |
---|
Please be aware that the string lengths supported by the default VST interface are normally limited to 24 characters. If you copy too much data into the buffers provided, you will break the host application.
void MyPlug::setProgramName (char *name)
{
strcpy (myProgramName, name);
}
Stuff name
with the name of the current program.
name |
A string at least 24 chars. |
---|
void MyPlug::getProgramName (char *name)
{
strcpy (name, programName);
}
virtual long dispatcher (long opCode, long index, long value, void *ptr, float opt);
Returns byteSize.
virtual long setChunk (void* data, long byteSize, bool isPreset = false);
This is called by the host, and tells the plug that the maximum
blocksize passed to either process
method will be <blockSize>
.
process (float** ins, float** outs, long sampleFrames)
means you must process exactly sampleFrames
number of samples, and not more! for instance
void MyPlug::setBlockSize (long blockSize)
{
// assume blockSize is 1024
myBlockSize = blockSize;
}
void MyPlug::process (float** ins,
float** outs, long sampleFrames)
{
// assume sampleFrames is 1
float* in = ins[0];
float* out = outs[0];
// long frames = myBlockSize; // KILLER!
long frames = sampleFrames; // OK: you should always use the passed sampleFrames value
while (--frames >= 0)
*out++ += *in++ * gain;
// boom on 2nd sample... if "long frames = myBlockSize;" is used
}
Using myBlockSize
instead of sampleFrames
can write
wildly into memory because the host may provide buffers of smaller size (e.g.
Peak when reaching the end of a file) than stated in setBlockSize
.
Again, setBlockSize
only tells you the maximum that
you will have to expect in process
(or processReplacing
),
but the ampleFrames
argument passed to the process function, as
well as the buffers provided there, might be only 1 sample, for instance.
This method is called when the effect is turned off by the user.
void MyPlug::suspend ()
{
memset (myBuffer, 0, myBufferSize * sizeof(float));
}
The buffer gets flushed here because otherwise pending data would sound again when the effect is switched on next time.
This method is called when the effect is turned on by the user.
Method that are called at construction time.
Must call this! Set the plug's unique identifier. The host uses this to identify the plug-in, for instance when it is loading effect programs and banks. On Steinberg Web Page you can find an UniqueID Database where you can record your UniqueID, it will check if the ID is already used by an another vendor. You can use CCONST('a','b','c','d') (define in VST 2.0) to be platform independent to initialize an UniqueID.
iD |
The plug's identifier. |
---|
#define kUniqueID 'MyPl'
// or (with VST 2.0 include file)
#define kUniqueID CCONST('M','y','P','l')
MyPlug::MyPlug (audioMasterCallback audioMaster)
: AudioEffectX (audioMaster, 1, 1)
{
...
setUniqueID (kUniqueID);
...
}
Set the number of inputs the plug will handle.
inputs |
The number of inputs. |
---|
This number is fixed at construction time and can't change until the plug is destroyed.
setNumInputs (2);
In case of stereo input.
Set the number of outputs the plug will handle.
outputs |
The number of outputs. |
---|
This number is fixed at construction time and can't change until the plug is destroyed.
In case of stereo output.
return vu value in getVu ()
; > 1. means clipped
return > 1. in getVu () if clipped
Tells the host that it makes sense to use this plug-in as a 1 in / 2 out device. When the host has mono effects sends and has a stereo bus for the effect returns, then the host can check this flag to add it to the list of plug-ins that could be used in this way.
Plug supports in place output (the processReplacing
method is
implemented).
Program data are handled in formatless chunks.
number of realtime qualities (0: realtime).
number of offline qualities (0: realtime only)
For algorithms which need input in the first place.
There is no negotiation currently, thus numOutputs
(or numInputs
)
must not change at any time. A vst plug is taken to be a device with a fixed
number of i/o pins. How these are beeing used depends on the application. In
cubase/nuendo :
canMono
which hints to the application that it makes sense to feed a mono signal.
It might be useful if makers of other hosts want to document connection
schemes differing from these.
The plug can inquire isInputConnected
and isOutputConnected
in order to suit the routing. Also the speakerArrangement
information
can be useful. Later vst implementations may support dynamic pin connection and
negotiation.
input |
The index of the input, starting at 0 (e.g. left=0, right=1). |
---|
This method should be called from resume
rather
than from the constructor.
Stuffes <text>
with an amplitude on the [0.0, 1.0] scale
converted to its value in decibels.
1.0 maps to 0 dB and 0.0 to -oo dB.
Stuffes <text>
with the frequency in Hertz that has a period
of <samples>
.
44100 will map to 1.0 if sampleRate
is set to 44.1 kHz.
Stuffes <text>
with the duration in milliseconds of <smaples>
frames.
Stuffes <text>
with a string representation on the floating
point <value>
.
Stuffes <text>
with a string representation on the integer <value>
.
float sampleRate;
AEffEditor *editor;
audioMasterCallback audioMaster;
long numPrograms;
long numParams;
long curProgram;
long blockSize;
AEffect cEffect;