REAPER supports VST plug-ins (up to version 2.4). VST is a standard defined by Steinberg Media Technologies GMBH. To get the VST SDK (which you will need to implement VST plug-ins), you will need to download it from Steinberg, as we cannot distribute it.
It is worthwhile noting that while VST is a standard, it is neither an open standard (the fact that you cannot easily distribute the SDK or things derived from it), nor is it a well defined standard.
This document will describe some REAPER-specific implementation notes for VST, as well as list some REAPER-specific VST extensions that plug-in developers are encouraged to use to acheive great integration with REAPER. Additionally, we encourage other VST host developers to add support for these extensions, as we believe they are useful.
Extensions
A REAPER aware VST plug-in can respond to effCanDo "hasCockosExtensions", with 0xbeef0000 (returning this value), which will inform the host that it can call certain extended calls.
case effCanDo:
if (ptr && !strcmp((char *)ptr,"hasCockosExtensions")) return 0xbeef0000;
Extended vendor specific calls the plug-in can implement include:
- effVendorSpecific(effGetParamDisplay, parm, buf, val)
Gets the formatted display of a particular value for a parameter. This REAPER uses for example when displaying the value of a VST's automated parameter on an envelope tooltip. Example implementation on host:
char buf[256]="";
if (effect->dispatcher(effect,effVendorSpecific,effGetParamDisplay,parm_idx,buf,val)>=0xbeef && *buf)
{
printf("parm %d, value=%f is %s\n",parm_idx,val,buf);
}
Example implementation on plug-in:
case effVendorSpecific:
if (index == effGetParamDisplay && ptr)
{
if (value>=0 && value<NUM_PARAMS)
{
sprintf(ptr,"%f",opt);
return 0xbeef;
}
}
- effVendorSpecific(0xdeadbef0, parm, rangeptr, 0.0)
Queries the range of a parameter (allowing the plug-in to use more than the 0.0...1.0 range that VST defines). The host does something like:
double range[2]={0,1};
if (effect->dispatcher(effect, effVendorSpecific, 0xdeadbef0, parm_index, range, 0.0)>=0xbeef)
{
// range[0]..range[1] is the range instead of 0..1
}
Or, to implement it on the plug-in side (in addition to responding to effCanDo/"hasCockosExtensions"):
case effVendorSpecific:
if (index == 0xdeadbef0 && ptr && value>=0 && value<NUM_PARAMS)
{
((double *)ptr)[0] = min_val;
((double *)ptr)[1] = max_val;
return 0xbeef;
}
Host Integration Functions
There are some additional functions to enable better integration with the host, the primary interface for accessing these extensions is via the audioMaster callback. Suppose your callback pointer is named "hostcb", then you would get each API with the following code:
void (*someFunction)();
*(long *)&someFunction = hostcb(NULL,0xdeadbeef,0xdeadf00d,0,"someFunction",0.0);
If an API is not implemented, hostcb() will return 0, and the resulting function shouldn't be called. Except when noted, all functions can be called from any context in any thread at any time. A list of functions defined:
- GetPlayPosition()
double (*GetPlayPosition)();
GetPlayPosition() returns the current playback position of the project. This is the time the user is hearing (and the transport shows etc). The value is in seconds.
- GetPlayPosition2()
double (*GetPlayPosition2)();
GetPlayPosition() returns the current playback decode position of the project. This is the time of the audio block that is being processed by the host. The value is in seconds. This may be behind where your plug-in is processing, due to anticipative processing and/or PDC.
- GetCursorPosition()
double (*GetCursorPosition)();
GetCursorPosition() returns the current edit cursor position (if any), in seconds.
- GetPlayState()
int (*GetPlayState)();
GetPlayState() returns an integer value representing the project play state. 1=play, 2=paused, 5=recording, 6=record paused.
- SetEditCurPos()
void (*SetEditCurPos)(double time, bool moveview, bool seekplay);
SetEditCurPos() repositions the edit cursor to "time" (in seconds), optionally moving the view if necessary, and optionally seeking playback (if playing back). This function should ONLY be called from a UI context (i.e. from the editor window, NOT from audio processing etc).
- GetSetRepeat()
int (*GetSetRepeat)(int parm);
GetSetRepeat() is used to change or query the transport "loop/repeat" state. Pass a parameter of -1 to query the repeat state, 0 to clear, 1 to set, and >1 to toggle. The return value is the new value of the repeat state. ONLY use this function to change the repeat state from the UI thread (however you can query safely at any time).
- GetProjectPath()
void (*GetProjectPath)(char *buf, int bufsz);
GetProjectPath() can be used to query the path that media files are stored in for the current project.
- OnPlayButton(), OnPauseButton(), OnStopButton()
void (*OnPlayButton)();
void (*OnStopButton)();
void (*OnPauseButton)();
These functions control the main transport for the host app. Only call these from the UI thread.
- IsInRealTimeAudio()
int (*IsInRealTimeAudio)();
Returns nonzero if in the main audio thread, or in a thread doing synchronous multiprocessing. In these instances low latency is key. If this is 0, and you are in processReplacing, then you are being called in an anticipative processing thread.
- Audio_IsRunning()
int (*Audio_IsRunning)();
Returns nonzero if the audio device is open and running.
- Additional API functions are listed in the REAPER Extension Plug-in SDK.