VST 3 Interfaces VST 3.6.11 SDK for developing VST Plug-in

- Main Page
- Related Pages
- Modules
- Namespaces
- Classes
- Files

All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Groups Pages
VST 3 API Documentation

                                                                  BusInfo:
    This is the structure used with getBusInfo, informing the host about what is a specific given bus.

The VST 3 API is an interface collection designed for realtime audio processing components. Such a component can be an audio effect or an audio instrument.

VST 3 is based on a technology called VST Module Architecture. Please have a look at the VST-MA documentation to find out more about how the Plug-in system works in general.

The API files belonging to VST 3 are located in folder pluginterfaces/vst

# Contents

Basics
  - Basic Conception
  - The Processing Part
  - The Editing Part
  - Communication between the components

Details
  - Parameters and Automation
  - Units
  - Presets & Program Lists
  - Complex Plug-in Structures / Multi-timbral Instruments
  - VST 3 Locations / Format
  - VST 3 Workflow Diagrams

New in VST 3.6.11:
  - NoteExpression Physical UI Mapping Support

New in VST 3.6.8:
  - Request Bus Activation

New in VST 3.6.5:
  - Channel Context Info Support
  - Prefetchable Support

New in VST 3.6:
  - iOS Inter-App Audio support

New in VST 3.5
  - Note Expression Support
  - Key Switch Support
  - Remote Representation of Parameters Support
  - Context Menu Support

# Basic Conception

A VST 3 audio effect or instrument basically consists of two parts: a processing part and an edit controller part.
The corresponding interfaces are:

- Processor : Steinberg::Vst::IAudioProcessor + Steinberg::Vst::IComponent
- Controller : Steinberg::Vst::IEditController

The design of VST 3 suggests a complete separation of processor and edit controller by implementing two components. Splitting up an effect into these two parts requires some extra efforts for an implementation of course.
But this separation enables the host to run each component in a different context. It can even run them on different computers. Another benefit is that parameter changes can be separated when it comes to automation. While for processing these changes need to be transmitted in a sample accurate way, the GUI part can be updated with a much lower frequency and it can be shifted by the amount that results from any delay compensation or other processing offset.
A Plug-in that supports this separation has to set the Steinberg::Vst::kDistributable flag in the class info of the processor component (Steinberg::PClassInfo2::classFlags). Of course not every Plug-in can support this, for example if it depends deeply on resources that can not be easily moved to another computer. So when this flag is not set, the host must not try to separate the components in any way.
Although it is not recommended, it is possible to implement both, the processing part and the controller part in one component class. The host tries to query the Steinberg::Vst::IEditController interface after creating an Steinberg::Vst::IAudioProcessor and on success uses it as controller.

Note
  A host does not need to instantiate the Controller part of a Plug-in for processing it. The Plug-in should be prepared for processing without having the controller part instantiated.

## Initialize

Both Steinberg::Vst::IComponent and Steinberg::Vst::IEditController derive from Steinberg::IPluginBase. The purpose of this basic interface is to initialize the component and to terminate it before it is destroyed.
The context parameter passed to Steinberg::IPluginBase::initialize is Steinberg::Vst::IHostApplication. Hosts should not call others functions before initialize is called!, except Steinberg::Vst::IComponent::setIoMode which need to be called before or Steinberg::Vst::IComponent::getControllerClassId which could be called before.

## Creation and Initialize from Host point of view

Here an example of host implementation creating the component and its associated controller of a Plug-in with a given classID:

//------------------------------------------------------------------------
...
Vst::IComponent* processorComponent;
Vst::IEditController* editController;
IPluginFactory* factory;
...
// factory already initialized (after the library is loaded, see validator for example)
...
// create its component part
tresult result = factory->createInstance (classID, Vst::IComponent::iid, (void**)&processorComponent);
if (processorComponent && (result == kResultOk))
{
// initialize the component with our host context (note: initialize called just after creatInstance)
res = (processorComponent->initialize (gStandardPluginContext) == kResultOk);
// try to create the controller part from the component
// for Plug-ins which did not succeed to separate component from controller :-(
if (processorComponent->queryInterface (Vst::IEditController::iid, (void**)&editController) != kResultTrue)
{
FUID controllerCID;
// ask for the associated controller class ID (could be called before processorComponent->initialize ())
if (processorComponent->getControllerClassId (controllerCID) == kResultTrue && controllerCID.isValid ())
{
// create its controller part created from the factory
result = factory->createInstance (controllerCID, Vst::IEditController::iid, (void**)&editController);
if (editController && (result == kResultOk))
{
// initialize the component with our context
res = (editController->initialize (gStandardPluginContext) == kResultOk);
// now processorComponent and editController are initialized... :-)
}
}
}
}
//------------------------------------------------------------------------

## Extensions

The functionality of the components implementing these basic interfaces can be extended by a number of optional interfaces, that only need to be implemented if this extension is required.

- Processor Extensions:
  - Steinberg::Vst::IConnectionPoint
  - Steinberg::Vst::IUnitData
  - Steinberg::Vst::IProgramListData
- Edit Controller Extensions:
  - Steinberg::Vst::IConnectionPoint
  - Steinberg::Vst::IMidiMapping
  - Steinberg::Vst::IUnitInfo

## Persistence

The host stores and restores the complete state of the processor and of the controller in project files and in preset files:

- Steinberg::Vst::IComponent::getState + Steinberg::Vst::IComponent::setState
  stores and restores the DSP model
- Steinberg::Vst::IEditController::getState + Steinberg::Vst::IEditController::setState
  stores and restores any GUI settings that are not related to the processor (like scroll positions etc)
- Restore: When the states are restored, the host passes the processor state to both the processor and the controller (Steinberg::Vst::IEditController::setComponentState). A host must always pass that state to the processor first. The controller then has to synchronize its parameters to this state (but must not perform any IComponentHandler callbacks).
  After restoring a state, the host will rescan the parameters (asking the controller) in order to update its intern representation.
  See also Steinberg::IBStream
  See also
- VST 3 Interfaces to be implemented by the Plug-in
- VST 3 Interfaces to be implemented by the Host

Back to Contents

# The Processing Part

The processing part consists of two related interfaces. The reason for the split-up is the idea to use the basic interfaces Vst::IComponent not only for audio Plug-ins but also for other kinds of media as well (e.g. video processing in the future). Hence the Vst::IAudioProcessor interface represents the audio specific part of a processing component. Let's have a closer look at the concepts.

## Steinberg::Vst::IComponent

Steinberg::Vst::IComponent:

1. Edit controller association: In order to enable the host to create a corresponding edit controller the processing component has to provide the matching class-ID. The host uses the module's class factory to create the controller component. (Steinberg::Vst::IComponent::getControllerClassId)
2. Steinberg::Vst::BusInfo -
   See also: IComponent::getBusInfo
3. Routing Information: When the Plug-in supports multiple I/O buses, a host may want to know how the buses are related. The relation of an event-input-channel to an audio-output-bus in particular is of interest to the host (in order to relate MIDI-tracks to audio-channels)
   See also: IComponent::getRoutingInfo, Routing

## Steinberg::Vst::IAudioProcessor

Steinberg::Vst::IAudioProcessor:

1. Setup: The processor must be configured before processing can start. Configurations are only allowed when the processor is inactive (Steinberg::Vst::IComponent::setActive).
   - Process setup: The processor is informed about the parameters that can not be changed while processing is active. (Steinberg::Vst::ProcessSetup).
   - Dynamic Speaker Arrangements: The host can try to change the number of channels of an audio bus. By default the speaker arrangement is defined by the Plug-in. In order to adjust the Plug-in to a context where a different speaker arrangement is used, the host can try to change it. (Steinberg::Vst::IAudioProcessor::setBusArrangements)
   When the processor has been configured, it has to be activated. The activation call signals that all configurations have been finished. In addition to that, the processor has got a 'processing state'. Before a host actually begins to perform processing calls, it has to signal this by calling IAudioProcessor::setProcessing(true). When the host will perform processing no longer, it must call IAudioProcessor::setProcessing(false) after the last processing call. Please see also: VST 3 Workflow Diagrams
2. Process: Steinberg::Vst::IAudioProcessor::process is the method that implements the actual processing. Any data needed for processing is passed to it as parameter Steinberg::Vst::ProcessData. This is necessary because processing often is performed in a separate thread and this is a simple way to avoid thread synchronization problems.
   - Block Size: Processing is done in blocks. The maximum number of samples to be processed in one block is set in Steinberg::Vst::IAudioProcessor::setupProcessing. The actual number of samples in a processing block is transmitted in the process call and can be different from call to call, but it must be a value between 1 and maxSamplesPerBlock.
   - Audio Buffers: For any audio bus defined by the Plug-in the host must provide buffer data - even for inactive buses. Buses are addressed by index, so leaving out inactive buses will mix-up these indices. The actual data buffer can be null though. (see Steinberg::Vst::AudioBusBuffers).
     Note that channelBuffers32 (or channelBuffers64) buffers pointers could be the same or different for input and output: this has to be take into account in the process function (for example not reseting the output before processing if input and output buffers are the same!). It could be the same for multiple inputs or multiple outputs (case of instrument Plug-ins) all outputs (or inputs) could share the same buffer!
     Important : the host could call Steinberg::Vst::IAudioProcessor::process without buffers (numInputs and numOutputs of Steinberg::Vst::AudioBusBuffers are zeroed, numSamples too), in order to flush parameters (from host to Plug-in). Parameters flush could happen only when the host needs to send parameter changes and no processing is called.
   - Parameters & Automation: Any parameter changes are transmitted in the process call through the interfaces Steinberg::Vst::IParameterChanges and Steinberg::Vst::IParamValueQueue. Simple parameter changes as result of GUI interaction are transmitted exactly in the same way as automation. (see Parameters and Automation).
   - Context: For each processing block the host should provide information about its state.
     See Steinberg::Vst::ProcessContext
   - Events: Steinberg::Vst::IEventList

Back to Contents

# The Editing Part

The edit controller is responsible for the GUI aspects of the Plug-in. Its standard interface is Steinberg::Vst::IEditController. The host has to provide a callback interface for the edit controller named Steinberg::Vst::IComponentHandler. The handler is essential for the communication with both the host and the processor.

- GUI: The controller optionally can define an editor view. The method Steinberg::Vst::IEditController::createView allows the host to specify the type of the view by passing an id-string. Currently the only type defined is "editor" (Steinberg::Vst::ViewType::kEditor), but there might be variations in future versions (eg. "setup").
  See also Steinberg::IPlugView
- Parameters: The controller is responsible for the management of parameters. Any change to a parameter that is caused by user interaction in the Plug-in GUI must be properly reported to the Steinberg::Vst::IComponentHandler. The host is responsible for transmitting the change to the processor. In order to make recording of automation work accordingly it is necessary to call beginEdit, performEdit and endEdit in the expected order!
  With the new interface IComponentHandler2 (since VST 3.1), the Plug-in (from UI) could group parameters which should use the same timestamp in host when writing automation, by wrapping a set of beginEdit/performEdit/endEdit functions (see IComponentHandler) with startGroupEdit and finishGroupEdit.
  More details can be found on the page about Parameters.
- Plug-in structure: If the Plug-in is composed of discrete functional parts, the edit controller should publish this structure and the parameters belonging to each part by implementing the Steinberg::Vst::IUnitInfo interface. More details can be found on the page about Units.

Back to Contents

# Communication between the components

The two VST 3 components (processor and controller) need a way to communicate. It is the task of the host to handle this.

## Standard Communication

All standard data (like parameter changes) are transmitted between processor and controller using the basic interfaces listed above.

- After creation of processor and controller the host will set the controller component state from the processors state. [This has changed from the previous SDK, where it was assumed that after creation the controller and the processor are in sync]
- When the host sets a new processor state (Steinberg::Vst::IComponent::setState) this state is always transmitted to the controller as well (Steinberg::Vst::IEditController::setComponentState). The controller then has to synchronize to this state and adjust its parameters.
- When the controller transmits a parameter change to the host, the host synchronizes the processor by passing the new values as Steinberg::Vst::IParameterChanges to the process call.
- The processor can transmit outgoing parameter changes to the host as well (Steinberg::Vst::ProcessData::outputParameterChanges). These are transmitted to the edit controller by the call of Steinberg::Vst::IEditController::setParamNormalized.

## Private Communication

Data that is unknown to the host can be transmitted by the means of messages. The communication interfaces are

- Steinberg::Vst::IConnectionPoint : The host establishes a connection between processor and controller
- Steinberg::Vst::IMessage : Represents a message to send to the counterpart
- Steinberg::Vst::IAttributeList : A list of attributes belonging to a message

Please note that messages from the processor to the controller must not be sent during the process call! This sending could be not speed enough and then break the real time processing. Such tasks should be handled in a separate timer thread.

## Initialization of communication from Host point of view

Here an example of host implementation where the component and controller parts are connected and synchronized:

// ...
// the component and the controller parts are previously be created and initialized (see above)
// ...
if (editController)
{
// set the host handler
// the host set its handler to the controller
editController->setComponentHandler (myHostComponentHandler);
// connect the 2 components
Vst::IConnectionPoint* iConnectionPointComponent = nullPtr;
Vst::IConnectionPoint* iConnectionPointController = nullPtr;
processorComponent->queryInterface (Vst::IConnectionPoint::iid, (void**)&iConnectionPointComponent);
editController->queryInterface (Vst::IConnectionPoint::iid, (void**)&iConnectionPointController);
if (iConnectionPointComponent && iConnectionPointController)
{
iConnectionPointComponent->connect (iConnectionPointController);
iConnectionPointController->connect (iConnectionPointComponent);
}
// synchronize controller to component by using setComponentState
MemoryStream stream; // defined in "public.sdk/source/common/memorystream.h"
stream.setByteOrder (kLittleEndian);
if (processorComponent->getState (&stream) == kResultTrue)
{
stream.rewind ();
editController->setComponentState (&stream);
}
// now processorComponent and editController parts are connected and synchronized...:-)
}

Please note that you CANNOT rely on the implementation detail that the connection is done directly between the processor component and the edit controller!

Back to Contents

Empty

Copyright ©2018 Steinberg Media Technologies GmbH. All Rights Reserved. This documentation is under this license.