Interface Technology Basics  VST 3.6.14
SDK for developing VST Plug-in
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
VST Module Architecture

Introduction

VST-MA is a component model system which is used in any Steinberg host application as the basic layer for Plug-in support as well as for internal application components.
It is object-oriented, cross-platform and (almost) compiler-independent.
The basics are very much like Microsoft(R) COM, so if you are familiar with this technology, understanding VST-MA should be quite easy.

VST-MA currently is provided in C++ only. Interfaces in C++ are expressed as pure virtual class (which is a class with nothing but abstract methods). Unlike COM there is no support for C or other languages yet - simply because there has been no need for this so far. But all VST-MA interfaces can be transformed into different representations in case this should be inevitable some day.
It is currently available for Windows and Mac OS X.

The C++ files belonging to VST-MA are located in the following folders:

Note: The name 'VST Module Architecture' has only little relation to the 'Virtual Studio Technology' itself.
It describes the basic layer for any Plug-in category supported in Steinberg hosts. VST-MA has been existing long before it was used as base for VST 3 indeed. The 'VST'-part of the name was introduced only for reasons of misleading advertising...

Interfaces

FUnknown

Steinberg::FUnknown is the basic interface of VST-MA. All other interfaces are directly or indirectly derived from it.

IID/CID

Each interface has a unique identifier (IID) of type Steinberg::FUID. It is used to retrieve a new interface from another one (Steinberg::FUnknown::queryInterface). It is important to understand the difference between interface identifier and component identifier.
A component-ID or class-ID (CID) is used to identify a concrete implementation class and is usually passed to a class factory in order to create the accordant component.
So a lot of different classes (with different class identifiers) can implement the same interfaces.

Direction

An interface may have a direction, meaning that the interface is expected to be implemented either in the Plug-in or in the host. The nature of an interface is documented like this:

  • [host imp] : the host implements the interface
  • [plug imp] : the Plug-in implements the interface


When neither of them is specified, the interface can be used in both ways.

Versioning and inheritance

Unlike C++ classes, interfaces do not use inheritance to express specializations of objects. Inheritance is used for versioning only. One of the strict rules is, that once an interface has been released, it must never change again. Adding new functionality to an interface requires a new version (usually an ordinal number is added to its name in this case).
A new version inherits the old version(s) of the interface, so the old and the new methods are combined in one interface. This is why specializations need to be modelled as separate interface! If a specialized interface would inherit from the basic interface as well, an implementation class that needs to implement all these interfaces would inherit the base interface twice and the compiler will start to complain about ambiguities. So the specialization relation to a basic interface can only be expressed in the documentation:

  • ISpecialInterface [extends IBaseInterface] => means IBaseInterface::queryInterface (ISpecialInterface::iid, ...) can be used to retrieve the derived interface.


You can find some example code here: Interface Versions and Inheritance

COM Compatibility

The first layer of VST-MA is binary compatible to COM. The Vtable and Interface Identifier of FUnknown match with the corresponding COM interface IUnknown. The main difference is the organization and creation of components by a host application. VST-MA does not require any Microsoft(R) COM source file. You can find information about COM on pages like:

Basic Interfaces

Helper Classes



See also
How to derive a class from an interface

Plug-ins

Module Factory:

A module (Windows: Dynamic Link Library, MAC: Mach-O Bundle) contains the implementation of one or more components (e.g. VST Effects). A VST-MA module must contain a class factory where meta-data and create-methods for the components are registered.
The host has access to this factory through the Steinberg::IPluginFactory interface. This is the anchor point to the module and it is realized as C-style export function named GetPluginFactory. You can find an export definition file in folder - public.sdk/win/stdplug.def which can be used to export this function.
GetPluginFactory is declared as follows:

IPluginFactory* PLUGIN_API GetPluginFactory ()



Locations

Component modules don't require registration like DirectX. The host application expects component modules to be located in predefined folders of the file system. These folders and their subfolders are scanned at application startup for VST-MA modules. Each folder serves a special purpose:

  • The application's Components subfolder (e.g. "C:\Program Files\Steinberg\Cubase SX\Components") is used for components tightly bound to the application. No other application should use it.
  • Components that are shared between all Steinberg hosts are located at:
    • Win: "/Program Files/Common Files/Steinberg/shared components"
    • Mac: "/Library/Application Support/Steinberg/Components/"
  • For special purpose Plug-in types, additional locations can be defined. Please refer to the corresponding documentation to find out if additional folders are used and where they are.

Categories

Any class that the factory can create is assigned to a category. It is this category that tells the host the purpose of the class (and gives a hint of wich interfaces it might implement).
A class is also described with a name and it has a unique id.

  • The category for import/export filters is "Project Filter" and for VST 3 Audio Plug-ins "Audio Module Class" for example.
  • A special category is "Service". The purpose of a class of this category is completely unknown to the host. It will be loaded automatically at the program start (if the user did not deactivate it).
  • Since the factory can create any number of classes, one component library can contain multiple components of any type.

IPluginBase

The entry-point interface for any component class is Steinberg::IPluginBase. The host uses this interface to initialize and to terminate the Plug-in component. When the host initializes the Plug-in, it passes a so called context. This context contains any interface to the host that the Plug-in will need to work.

Purpose-specific interfaces

Each Plug-in category (VST 3 Effects, Project import/export Filters, Audio Codecs, etc...) defines its own set of purpose-specific interfaces. These are not part of the basic VST-MA layer.

See also
How the host will load a Plug-in

Unicode

Beginning with version 5 of Cubase and Nuendo, the internal structure of the host was modified to better support internationalization. Therefore the string handling was changed to utilize Unicode strings whenever strings are passed around. Consequently all the interfaces to Plug-ins have changed from using ASCI to Unicode strings as call and return parameters as well. So in turn also any Plug-in has to be adapted to support Unicode. This has major consequences in that:

  • Unicode hosts (Cubase5 or later) will only work with Unicode Plug-ins. While loading a Plug-in a Unicode host will check the Plug-in's type and refuse to load any Plug-in that is non-Unicode.
  • Unicode Plug-ins will not load into non-Unicode hosts. While loading a Unicode Plug-in will request information from the host and refuse to load itself, if no Unicode host is detected. Therefore, if a Plug-in is supposed to work on both older and newer hosts, it is best to provide two versions of the Plug-in.

Plug-ins for only Unicode hosts

Writing Plug-ins that are supposed to work only on Unicode hosts is easy. Use a current version of this SDK and develop a Plug-in as usual. Make sure that you only ever pass Unicode UTF16 strings to interfaces that have strings as call parameters and also be prepared that interface return strings are always UTF16. Therefore, to make things easier, it is recommended that throughout the Plug-in's implementation Unicode strings are used, in order to avoid back and forth conversions. To make things even more easy, use the Steinberg::String and Steinberg::ConstString classes from the Base module, they have been designed to work universially on both, Mac and Win.

Migrating from non-Unicode to Unicode

In Steinberg SDKs released before Cubase5 the interface functions were using pointers of type char for passing strings to and from the host. These have been changed now to using Steinberg's defined type tchar which is equivalent to char16 , i.e. 16 bit character. There are theoretically many ways of how characters could be represented in 16 bits, but we chose to use the industry standard Unicode, so strings are expected to be encoded in UTF16.
Accordingly also the implementation of a Plug-in needs to be adapted to deal correctly with Unicode encoded strings, as well as only ever passing Unicode strings to the host.

Technical note: Changing a function from using 8 bit to 16 bit character pointers seems to be only a minor modification, but in interface design this is a major intrusion, because an interface is a contract to the outer world that is never to be changed. Therefore, classes that were changed to use Unicode strings are distinguished and also received a new unique class id.

SDK backward compatibility

Even with the current SDK it is still possible to develop non-Unicode Plug-ins. In the file pluginterfaces/base/ftypes.h the line "#define UNICODE_OFF" is commented out, but if it gets uncommented, then all interfaces will revert back to using single byte ASCI strings. Alternatively you could also specify UNICODE_OFF as preprocessor definition in your project file.
Also the Plug-in's factory info will then not define the Unicode flag anymore, so a Unicode host will see the compiled Plug-in as non-Unicode. As a matter of course, when reverting back to single byte strings the Plug-in's implementation also has to be changed to behave correctly.

Technical note: When undefining Unicode also the class ids will revert back to the old ones.

Empty

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