In my effort better to understand the architecture of VST.NET, I asked about the mechanisms used by the PlugInInterface, and got a very helpful answer. Now I've spent some more time looking at the code and thinking about it, and I have a couple more questions.
After poring over the example code and documentation, as well as reviewing the VST SDK from Steinberg, I found myself asking the following question:
"Rather than creating the whole IExtensible mechanism and the PluginInterfaceManager, why didn't Marc just create a VST plug in base class that implements default versions of the 60+ VST methods, and allow the user to overwrite the methods? If he wanted
to group the VST methods into interfaces, that could still be acheived with this approach, and the system would be much more approachable."
I think the answer to this question may lie in concerns about threading, especially given the many references to this issue in the documentation. What am I having a harder time understanding is which threading issues are solved by the approach taken. I certainly
understand that there are threading issues in VST, given that different parts of the plug in interface are called from different threads. What I haven't lived with first hand are the specific threading issues that come up. Maybe it would be helpful to get
an example of a typical VST thread hazard and how the existing architecture eliminates the hazard. I would like to understand this issue better, particularly because I would like to build plugins that have fairly tight integration between the realtime audio
thread and the UI thread (such as responsive "VU meters" in the plug-in editor).
Also, I am wondering about the VstProcessLevels enum. Are these levels defined in some way in the VST SDK, or are they arbitrarily chosen here? I remember the VST documentation talking about the real time processing thread and "other"
threads, but I don't remember this enum.
On a related question, I am wondering why the IExtensible.Supports method exists. What I mean is, wouldn't the same result be acheived by having GetInstance return null if an interface is not supported? I think I am missing something here. Also, I assume that
if one is not using PlugInInterfaceManager and if a certain VST interface is not supported, and if a method belonging to that interface is called by the VST host, then an exception is thrown?
But if one is using PlugInInterfaceManager, a default implementation of that interface is provided?
Feb 22, 2009 at 6:32 PM
"Rather than creating the whole IExtensible mechanism and the PluginInterfaceManager, [...]"
My aim for the Framework was to structure the VST features. I don't think a 60+ method class is a good design (that is also what the VST SDK does and I hate it). It does not sepparate concerns. I don't see how you can have the separation into interfaces and
still have one class (educate me ;-). I ended up with one anyway (Core), because I wanted to make the use of the Framework optional. So If you like a 60+ method class better, don't use the Framework, go for a Core-level Plugin implementation (see CorePlugin
sample). The Framework is meant to make things easier. Just implement the interfaces you really need and each interface is (should be) a logical unit of related functionality. Threading issues was not a factor for taking this route. Because the way you deal
with threading (issues) is so very plugin-specific, I had no intention of trying to find a generic way to deal with it, other than the facility that is provided by the Interface Manager. In your case, you should ask yourself, how big of a problem is it when
the VU inditcaion on the UI reads a value from the AudioProcessor that is not the most-up-to-date value?
The VstProcessLevels enum is a copy of the VST SDK enumeration. I would never have given it this stupid name if I made up one myself ;-). There is a property on the IVstHost interface that returns this value. That call is marshalled all the way to the (real)
Host and back.
The existence of the Supports method began with the notion of lifetime-scope of interface implementation classes. The implementation of Supports was supposed to report back if an interface was supported without actually creating the implemtation class for it. When
creating an interface implementation class is not cheap, this becomes important. I must admit, this notion has been lost in the current implementation, but the calling code was written with this in mind (I hope ;-).
When the framework queries for an interface that is not supported by the plugin (using the Interface Manager or something else), no exception is thrown but a meaningful return value is generated that indicates to the Host that the call is not supported by the
Plugin. This is done because almost all methods for a Plugin are optional.
I certainly sympathize with the desire not to have a 60+ method "flat" class. I would have to brush off my C# skills to propose a really clean alternative (I'm hoping that your wonderful efforts will provide me with the opportuntity to do more
C# programming rather than be stuck in C++/Matlab ghetto I am stuck in today). But one design idea that occurred to me was to break up the interfaces the way you have done, and then provide a base class for each interface which does reasonable things with
all the methods of that interface. The default top level VSTplugin base class could use aggregation and contain by default an instance of each of the base classes, it would forward the top level flat method calls to the appropriate subobject. (This would require
method forwarding overhead, but I am guessing that the current design doesn't avoid this overhead either). The user could override the aggregration in the top level VSTplugin object and provide his/her own subjobject which implements any of the interfaces.
I'm sure there are probably better designs, and I am really not an advanced .NET architect. And I really only brought it up because I figured I was missing something when I got confused. Usually when I get confused, I am missing something, and I suspect that
I probably still am missing something in this case. The important thing is that I now understand better what is going on, that was the main purpose of the inquiry. I hope you don't take my musings as criticism, they aren't meant that way. I'm just trying to
gain greater understanding.
I have to admit that I am still confused and think I am still missing something about thread hazards and how IExtensible might help eliminate them. In the absence of the facilities provided by the interface manager, I thought I'd have to be aware of threading
issues within each piece of code which might be called from more than one thread. In certain cases, one might have to do something like subclass a mutex and provide it with a thread priority level such that priority inversion could not occur and delay the
real time processing thread. One step in the direction of understanding thread issues in general and where this sort of thing might be necessary would be to understand if there are any rules or conventions concerning which VST methods should/can be called
from which threads. In addition, I might have to include code which uses IVstHostCommandStub.GetProcessLevel() to query the process level. Along these lines, I'll go back and see if the VST SDK gives any rules these things, as well as about any thread preemption
rules within a VST host. For instance, can there be thread preemption within a ProcessLevel (I sure hope not!). Given how poor their documentation is, I might not find much.
Anyway, thanks again. I think I might be about ready to write my first plug in using your tools. You are doing a great service not only by writing them but by being willing to answer questions from pesky folks such as myself.