/ VST Home / Technical Documentation

VST 3 API Documentation

On this page:

Related pages:


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 (VST-MA). Please read 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 the folder "pluginterfaces/vst".

If you are looking for the C API of VST 3
see https://github.com/steinbergmedia/vst3_c_api.

Basic Concept

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

Tech_doc_1

Tech_doc_2

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 implementation efforts. However, this separation enables the host to run each component in a different context, even 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 cannot be moved easily 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 the 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.

Initialization

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 should implement the interface Steinberg::Vst::IHostApplication. Hosts should not call other functions before initialize is called, with the sole exception of Steinberg::Vst::IComponent::setIoMode which must be called before initialize. Steinberg::Vst::IComponent::getControllerClassId can also be called before (See VST 3 Workflow Diagrams).

How can the plug-in access IHostApplication?

//-----------------------------------------------------------------------
tresult PLUGIN_API MyPluginProcessor::initialize (FUnknown*context)
{
    if (auto hostApp = Steinberg::U::cast<IHostApplication> (hostContext))
    {
        String128 name;
        if (hostApp->getName (name) == kResultTrue)
        {
            //...
        }
    }
    //...
}
//-----------------------------------------------------------------------

Creation and initialization from Host point of view

Here an example of a 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 in separating component from controller :-(
    if (processorComponent->queryInterface (Vst::IEditController::iid, (void**)&editController) != kResultTrue)
    {
        // This is the normal case, where the controller is separated from the component.

        // ask for the associated controller class ID (could be called before processorComponent->initialize ())
        FUID controllerCID;
        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))
            {
                // editController is now created, we have the ownership, 
                // which means that we have to release it when not used anymore.

                // initialize the component with our context
                res = (editController->initialize (gStandardPluginContext) == kResultOk);

                // now processorComponent and editController are initialized... :-)
            }
        }
    }
    else
    {
        // editController is now created, we have the ownership, 
        // which means that we have to release it when not used anymore.
    }
}
//-----------------------------------------------------------------------

Note
Please be aware that IPluginBase::initialize and IPluginBase::terminate must only be called once per object instance. So if an object implements both IAudioProcessor and IEditController, take care to only call them once, as in the example above.

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.

Persistence

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

Tech_doc_3

Tech_doc_4

See also

The Processing Part

Tech_doc_5

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

IComponent

The Steinberg::Vst::IComponent interface allows the host to get information about the plug-in:

  1. Edit controller association: In order to enable the host to create the associated edit controller part, the processing component has to provide the matching class-ID. The host uses the module's class factory to create the controller component. See Steinberg::Vst::IComponent::getControllerClassId

  2. The host can ask for bus configurations (Steinberg::Vst::BusInfo). See Steinberg::Vst::IComponent::getBusInfo

  3. The host can ask for routing information (Steinberg::Vst::RoutingInfo).

  4. The host can activate or deactivate a specific bus like side-chain. A deactivated bus should be not processed by the plug-in. See Steinberg::Vst::IComponent::activateBus

  5. The host can activate or deactivate the plug-in (On/Off button). See Steinberg::Vst::IComponent::setActive

  6. The host can store and restore the state of the plug-in (preset and project persistence). See Steinberg::Vst::IComponent::getState, Steinberg::Vst::IComponent::setState

IAudioProcessor

The Steinberg::Vst::IAudioProcessor interface extends Steinberg::Vst::IComponent by providing a state machine for processing and the process method:

  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 cannot 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 using Steinberg::Vst::IAudioProcessor::setBusArrangements
  2. When the processor is configured, it has to be activated (Steinberg::Vst::IComponent::setActive). The activation call signals that all configurations have been finished.
  3. In addition to that, the processor has a 'processing state'. Before a host begins to perform processing calls, it has to signal this by calling IAudioProcessor::setProcessing (true). When the host stops processing, it must call IAudioProcessor::setProcessing (false) after the last processing call. Please see also: VST 3 Workflow Diagrams
  4. Process: Steinberg::Vst::IAudioProcessor::process is the method that implements the actual processing. Any data needed for processing is passed to it as a parameter Steinberg::Vst::ProcessData. This is necessary because processing is often performed in a separate thread and this is a simple way to avoid thread synchronization problems.

Tech_doc_6

The Editing Part

Tech_doc_7

The edit controller is responsible for the GUI aspects ofthe plug-in. Its standard interface is Steinberg::Vst::IEditController. The host has to provide acallback interface for the edit controller named Steinberg::Vst::IComponentHandler. The handler is essentialfor the communication with both the host and the processor.

Tech_doc_8

VST 3 threading model

The threading model used by VST 3 is quite simple and requires that:

  • All initialisation/de-initialisation are done in the UI Thread
  • All function exported by the plug-in are called by the host in the UI Thread with the exception of:
    • IAudioProcessor→process: which could be called in a Audio Thread (realtime thread), avoid any memory allocation!
    • IAudioProcessor→setProcessing: which could be called in a Audio Thread (realtime thread), avoid any memory allocation!
  • All function exported by the host are called by the plug-in in the UI Thread

Check the Audio Processor Call Sequence and the Edit Controller Call Sequence.

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.

Tech_doc_9

Private communication

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

Please note that messages from the processor to the controller must not be sent during the process call, as this would not be fast enough and would break the real time processing. Such tasks should be handled in a separate timer thread.

Tech_doc_10

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 becreated 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...:-)
}

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