Having a bit of trouble getting started

Feb 19, 2009 at 5:44 AM

OK, I'll admit it. I am the kind of person that likes to understand how things work. That is making things difficult for me, because both VST.NET and VST are complex. And since VST is mostly not documented, except through source code for base classes which hide how things work, it is very difficult for me to understand how VST.NET things map onto VST things.

As I stumble around trying to gain greater understanding, I realize that one big stumbling block for me is understanding the functioning of and the necessity for the PluginInterfaceManager. Is this construct a reflection of VST plumbing, or is it a programming paradigm which I am unfamiliar with? I notice in the documentation reference section that Marc has a note to himself to explain this. For me at least, this it the biggest part of the sample code that I can't get my head around.

Could someone offer a few words to help me get my brain unstuck?

- Andy


Feb 19, 2009 at 6:57 AM
Edited Feb 19, 2009 at 7:05 AM
A native VST Host communicates through a handfull of callback function pointers with a Plugin. A Plugin in turn, receives one callback function pointer to the host. The 'dispatcher' callback function pointer that the Plugin exposes to the host is used for a lot of stuff. "Opcodes" are defined (in the VST SDK) that identify a specific 'message' and specific method parameter are expected to be filled and mean a certain thing.

The Interop assembly of VST.NET interacts with these callback functions (and implements them on behalf of the Plugin) and marshals any call to and from the managed Plugin. There are Core interfaces defined that list all the methods a Host can call on a Plugin and visa versa. So when the dispatcher function in Interop receives an opcode with its parameters it selects one of the structured and managed functions to call the managed plugin. This level of communication is called the Core-level for no Plugin Framework is in the picture yet. If you look at the CorePlugin sample you can see a very simple example of how you would write this. You basically have to implement 60+ methods in a public class and they will be called by the Interop.

On top of this Core level you can use a Plugin Framework. This Framework implements those 60+ methods for you and delegates their calls to other interfaces (defined in the framework). The idea of these Framework interfaces is that all methods of each logical feature are grouped into one interface. These Framework interfaces are all optional, except one (IVstPlugin). So when a Host makes a call to a Plugin that call is first intercepted by the Interop where its marshalled to the Core interfaces, where the Framework picks it up and maps it to the 'feature' interface. But how does the Framework know if you have choosen to implement that interface? It queries the Plugin root object (that implements IVstPlugin) for an implementation of that interface. If the plugin cannot return a reference (null) than the Framework will communicate that back to the Host (through Interop). If the Plugin returns an implementation of that specific interface, the Framework calls the appropriate method on it and handles the return value (if any).

The IExtensible interface is the mechanism with which the Framework queries the Plugin for interface implementations and its the base-interface of IVstPlugin. The InterfaceManager (and this what you have been waiting for ;-) is only a convenient way to implement the IExtensible interface (the PluginInterfaceManageBase class implements IExtensible itself, so you can just forward the calls from the Plugin root class). It is optional. The "Getting Started" section of the docs describes the options you have for implementing IExtensible the interface.

The Interface Manager stores two references for each optional Framework 'feature' interface. One is not thread-safe and the other is (or at least should be ;-) and will hand the Framework one or the other depending on which thread the call is made. Know that a typicall Host will use 3/4 threads to call into the Plugin (see ProcessLevel). Your PluginInterfaceManagerBase-derived class overrides the CreateXxxxx methods for the 'feature' interfaces you need or want to support.

So the Interface Manager class was only created to try to make it easier for the Plugin developer to manage its interface implementation references. It is optional. If you don't like it or think you can do better: don't use it. But you MUST implement the IExtensible interface on the Plugin root class, for its represents a vital mechanism the Framework uses to deliver calls from the Host to the Plugin.
Feb 19, 2009 at 3:25 PM
OK, that helps a ton, and maps well to what I've seen in the VST SDK source code. I'll go back and study your samples with that in mind. Just one quick question while you might still be up over there - the set of interfaces that Iextensible would be expected to return an interface to would be all the Ixxx interfaces in the Jacobi.VST.Framework namespace?
Feb 19, 2009 at 7:40 PM
Edited Feb 19, 2009 at 7:46 PM

Yes. All interfaces IExtensible returns for a plugin are the IVstPluginXxxx interfaces declared in the Jacobi.Vst.Framework namespace.

IExtensible is also used for the Framework implementation of the Host interfaces. In the call to the Open method of IVstPlugin you are passed a reference to the IVstHost interface (which also derives from IExtensible). This is the Host root object (as opposed to the Plugin root object implementing IVstPlugin). The IVstHost implementation of IExtensible will return all the IVstHostXxxx interfaces also defined in the Framework. All methods and properties on all the IVstHostXxxxx interfaces eventually map back to the callback function pointer the Host passed into the Plugin at startup (inside Interop).