Specific technical questions about VST3, and info for users

DSP, Plugin and Host development discussion.
Post Reply New Topic
RELATED
PRODUCTS

Post

Hi all,

As one of VST3's more vocal sceptics, I would like to invite Steinberg's engineers to answer some (fair, I hope) technical questions about VST3. If others have technical questions, feel free to add them to this thread. I've tried to translate some of these concerns in to likely end user impacts as well, as I know a lot of users are watching with interest. End-users:- these are _questions_, which _may_ be resolved just fine, so at least don't go spreading panic and flame wars until Arne & co. have had chance to answer. Thanks!

1:- The VST3 API is defined in C++. While on Windows, this is fine - the COM C++ ABI is a binary standard - on OS X using XCode and GCC, I believe that the C++ binary interface (ABI) is not guaranteed to be standardised between different point-versions of GCC, which are released roughly annually. This means that plug-ins compiled with one version of GCC may not work with hosts compiled with another. Is this something we should be worried about, or can you reassure developers that this will not be a problem?

(What this means for users:- Any time Apple changes to a new version of XCode - every couple of years - it may become increasingly difficult to build plug-ins that are cross-compatible. Basically more of those painful "bumps" in the compatibility road than would otherwise happen, more work for plugin devs, higher prices and less innovation).


2:- The VST3 Events API (IVstEvents.h) defines three types of event (Note On, Note Off, Data), and Data currently has a single type, SysEx. How are instrument plug-ins supposed to handle event messages like Pitch Bend, Sustain Pedal, Aftertouch etc.?

(What this means for users:- Currently there is no documented way that I can find for you to make VST3 plug-ins respond to those MIDI control changes and performance things that you're used to).

3:- Audio plug-in APIs are extremely dependent on a correct and well documented threading model to acheive high performance, low latency and stable operation. There does not seem to be any documentation at all on the threading model in the VST3 SDK. How are we supposed to get the threading model right without proper documentation?

(What this means for users:- Bugs, lots of bugs. Especially if other hosts like Sonar or FL decide to try and implement VST3 too).

4:- The VST3 API specifies a "ProcessContext" structure which is passed to plug-ins when an audio buffer is processed. This contains, among other things, sync information and transport state, and is very much equivalent to the old VSTTimeInfo structure. There are various problems with this approach:-

* When using a delay compensated audio engine, in a given process call different plug-ins will be processing different chunks of logical audio stream time. This means they are not really operating in the same time context. The host can either not recompute the time (and tell them the wrong one), or recompute for each plug-in (and waste a bunch of CPU).

* Similarly, when using a multi threaded audio engine, different plug-ins will be processing different chunks of stream time, and the same thing applies. For a multi-core, delay compensated system, multiply by N.

* When the host transport loops back to an earlier point in the stream mid-way through a buffer, the plug-in is always told too early or too late. Similarly with transport state change messages in a delay compensated engine with preroll.

How do you suggest we implement our plug-ins and hosts to work around these problems?

(What this means for users:- plug-ins that rely on sync for LFOs or onboard sequencers may not sync as tightly as they should; hosts may use up more CPU than you would expect).

5:- VST3 uses a new plug-in identification system (specifically, GUIDs), and a new state persistency mechanism. This is no bad thing in itself, though you can argue all day about the merits or lack of regarding GUIDs. However, what is not apparent from the API is any migration mechanism for loading settings from old plug-ins using FOURCC identifiers and the Parameters and Chunks mechanisms used by VST 1.x and 2.x. How does a VST3 plug-in declare that it can load data from older plug-ins?

(What this means for users:- when a host or plug-in goes VST3-only, currently there is no obvious way it will be able to load any of your old settings.)



Thanks,
Angus.
This account is dormant, I am no longer employed by FXpansion / ROLI.

Find me on LinkedIn or elsewhere if you need to get in touch.

Post

Just my observations about these issues based on the documentation and my experience in "code archeology" I've cumulated during about the 20 years I've been doing programming.

1. I have no experience on the Xcode COM model, but I've understood there's already compatible COM model being used in most core libraries. So I would assume this would be issue there also if it was the case you mention?

2. Program Change is atleast flag for parameter. So plugin can expose some special VST parameters which work as program change or bypass, etc. see Steinberg::Vst::ParameterInfo. Standard midi is sent thru Midi Input Bus. There exists also Steinberg::Vst::ControllerNumbers, but seem it's not really used anywhere. So CC automation is not atleast officially supported in the SDK now. I would guess Steinberg wants Events to be preferred way and let host decide where to send specific CCs. Or then midi data could be sent using the sysex data. I would guess the data is supposed to be as-is with the sysex prefix and suffix, so easy to pass just normal midi data too - but why to go lengths to define then Steinberg::Vst::ControllerNumbers.

3. I would guess this goes in par with COM model sort of and the way the API is designed. You can have multiple instances of the classes (the component and controller) if it's set in the Steinberg::PClassInfo::cardinality (and Steinberg::PClassInfo::cardinality). Right now there's only one flag kManyInstances. Without specifically saying it, host can call the object only from one thread at time (or have it synchronized). Now what the cardinality means here is that the plugin *must* be able to cope multiple instances from different threads. So please don't have any non-thread-safe things shared between instances. Which any VST2 would do anyhow to be multi-processor friendly.

4. Can't comment much on this, I have not noticed any problems with current VST2 way and this "ProcessContext" is basically same thing just wrapped to be sent to plugin in the process call, instead of plugin asking it specifically. From host point of view all this data is always calculate anyhow so why not put it there with flags telling which data host provides. Infact it's more efficient for host.

5. There's special GUID building from old FOURCC format so that host can detect it is upgraded version of old one. Check the FAQ "Q: How can I update my VST2 version of my plug-in to a VST3 version and be sure that Cubase will load it instead of my old one?". There's example how to generate such GUID. It's hosts place to check if such VST3 class exists from old FOURCC code.

The old chuck is loaded as state with IComponent::setState() and IEditController::setComponentState(). This is told in FAQ "Q: How does persistence work?". Because the component state is only one called for both component and controller, I would as host developer call old VST2 chunk for those and the IEditController::setState() with null pointer.

With old parameters "problem" you just have to be clever and use same old IDs in the VST3 too. Isn't that the case even now with VST2, easy to add new, but reorganizing them is bad?

Note, I saw the VST3 SDK first time just couple days ago too.

To add about the component and controller separation (MVC). I think it's very good, although for host it means a bit more extra work to make IMessage handling from the plugin's component and controller. There is Steinberg::Vst::ComponentFlags passed from SteinBerg::PClassInfo2::classFlags and there's kDistributable commented as "Component can be run on remote computer". What this means from plugin developers perspective is that ALL communication between component and controller is to be done through the peer connection point with IMessage. You can pass text and pointer to binary data there, but you must not send any pointers in that data and data should be 32/64-bit compatible. That way host can deliver the data to and from the component/controller through network or by other means. Basically, you must assume your IMessage destination is running in another process and possibly not even in the same computer (another could be 32-bit Windows and another 64-bit Mac).

Short put, the DSP code can run elsewhere hidden in some DSP box while the GUI is on the users host window.
jouni - www.markvera.net - Stardrive Studio - Orionology

Post

Hi Mark,

Thanks for the feedback. A few points:-

1:- I must say, I've not seen a COM model used in a dynamic runtime linked interface on an OS X system. OS X uses either the CF/Carbon APIs, which are straight C, or the NS/Cocoa APIs, which are Objective C++. COM and suchlike is fine internally to a library, or indeed as an interface to a statically linked library you are building from source (where you always have the choice to use the same compiler version as the rest of your code, so you can guarantee the same ABI), but for dynamic linking libraries from different vendors, *shudder*.

2:- What about pitch bend, sustain etc. which are vital for keyboard players and composers? And WHAT Midi Input Bus? I see only the EventLists, which don't have data types other than Note On, Note Off and SysEx.

Also, on a slightly more esoteric point.. there is a conceptual difference between persistent parameters, and volatile MIDI controller states, and VST3 does not make that distinction clear.

3:- That cannot be right - the audio process() call at least has to be on a different thread to some of the other calls in a specific instance. Without any documentation, it's impossible to say much more than that, and getting this stuff right is far from trivial. The problem is that there is no documentation about this, and it's absolutely central to any realtime audio plugin API in this day and age.

4:- It's true that this is the same as the VST2 way, except that in VST2 the host only had to calculate for plug-ins that specifically asked for time info. Also, when VST2 was designed in '98 and '99, VST DSP accelerator cards and multi core audio engines didn't exist, so that part of the design was fine in its day, but I maintain that it's broken now, and I can point you at real world examples where it causes problems for users as well as devs.

5:- That does clear some of that issue up, but there's no documentation of exactly how to pack the info in to the IStream stuff. Presumably the host also has to unpack the parameter blobs, but again not clear.. and what of state chunks which incorporate multiple VST1.x/2.x programs? (not that those were a good idea in the first place, but never mind!)


Cheers,
Angus.
This account is dormant, I am no longer employed by FXpansion / ROLI.

Find me on LinkedIn or elsewhere if you need to get in touch.

Post

Yeah, the total absence of common MIDI events apart from NoteOn/Off and SysEx is a bummer... it's cool that we can have different streams, but this... hmmm... weird...

Post

3. Okay, you are right that some calls might be on different thread on Steinberg::Vst::IComponent and Steinberg::Vst::IAudioProcessor. However as the "Workflow Diagram" in the SDK says those calls must be done prior process call, and none of the calls can be done unless it's set to suspend state. And it would be hosts best intentions to be *synchronous* with the "Workflow Diagram" calls. As the diagram states that the IUnitInfo and IEditController and other methods besides the ones mentioned in "Workflow Diagram" can always be called. So to me it looks like constrains to host how to call certain functions in synchronous way. I doubt it matters from which thread the calls come, as long as it's synchronous - rest should (as Steiny puts it "can always be called") be thread-safe and I don't see anything what would be a problem for a plugin. UI in applications is almost always with no-exceptions in main thread and the program lists and such are mainly just read-only methods. Also the IMessage passing is just for the UI IEditController to pass data to host which delivers it for the plugin in the DSP process thread context.

4. I don't know what you mean it's problem. In Orion we always have all the VstTimeInfo data calculated - all is there in Orion, beside the VstTimeInfo specific formatting which is done separately. When VST asks for the timeinfo we just make copy of structure and set flags - this is done this way because of multi-threading needs. Now in VST3 it's more straight forward. We'd just have to copy all. Flags can be static, so we can basically pass pointer to same global structure instead of doing copies and setting flags. Also there's no question about threading here as the VstTimeInfo is passing only in the process call.

5. You mean the Steinberg::IBStream? It looks to me like basic streaming interface. So it's just the same binary blob you'd have in chunk when saving/loading chunk state in VST2. Or are you talking about old preset compability now? The new preset class seems to be work for host, not for plugin. The class is calling state save/load and other methods from the plugin's interfaces. Old preset format is different, so host needs similar loader for those and call the setState with the program chunk as it's done in VST2 already with the effSetChunk.

Parameter based presets are just sequence of program changes and Parameter events as it has been before too. As VST3 does it, I'd personally just sent with the process call and with 0 inputs and 0 outputs (gain example specifically tests for that). Basically emulate the old VST2 processEvents(). Sure here you can argue if the plugin should be able to process that in paraller with on going process calls in audio thread - but aren't setParameter() calls in VST2 already required to do that?
jouni - www.markvera.net - Stardrive Studio - Orionology

Post

1) MIDI Drivers under Mac OS X are implemented with a COM-based interface (CFPlugIn), although it's interesting to note that Apple don't rely on the C++ ABI at all, instead the low-level stuff is all done in pure C to ensure compatibility...

Post

For a year, CC and other non-notes events from plug to plug was broken in C4.
Now that they fixed it, it's sysex from host to plug (2.4) that is broken.
So far VST3 has been a nightmare for me :roll:

Post

Urs wrote:Yeah, the total absence of common MIDI events apart from NoteOn/Off and SysEx is a bummer... it's cool that we can have different streams, but this... hmmm... weird...
Yes I agree about that. Especially as there are some definitions about those in the SDK header files! :o
jouni - www.markvera.net - Stardrive Studio - Orionology

Post

nixim wrote:1) MIDI Drivers under Mac OS X are implemented with a COM-based interface (CFPlugIn), although it's interesting to note that Apple don't rely on the C++ ABI at all, instead the low-level stuff is all done in pure C to ensure compatibility...
Yes it must have been that CFPlugIn stuff I was reading and noted the COM-based interface in OSX. I'm a bit technology eater so I read such things even though I don't use those. :lol:
jouni - www.markvera.net - Stardrive Studio - Orionology

Post

nixim - you are right, they use COM but with a "C" interface, as GCC does not generate COM-compatible C++ vtables (but can generate COM-compatible "C" glue). It is likely that defining "C" versions of the COM interfaces would be an adequate fix for that, however that would presumably break binary compatibility with Steinberg's existing VST3 plugs.
This account is dormant, I am no longer employed by FXpansion / ROLI.

Find me on LinkedIn or elsewhere if you need to get in touch.

Post

If I read the gcc FAQ answer correctly than gcc has a stable C++ ABI :
http://gcc.gnu.org/onlinedocs/libstdc++ ... x.html#5_8

And for the other answers, give me some time. I also don't like to answer stuff twice and would like to further discuss this on the vst mailinglist.

arne

Post

Angus_FX wrote: 4:- It's true that this is the same as the VST2 way, except that in VST2 the host only had to calculate for plug-ins that specifically asked for time info. Also, when VST2 was designed in '98 and '99, VST DSP accelerator cards and multi core audio engines didn't exist, so that part of the design was fine in its day, but I maintain that it's broken now, and I can point you at real world examples where it causes problems for users as well as devs.
Mm.. I haven't checked VST3, but VST2 allows a host to send a plugin any buffer size shorter than whatever it stated would be the maximum. This means a looping position discontinuity in time is trivially solved by splitting the buffer at the loop point, and calling process twice with the partial buffers. If the plugin gets the time info once per process call, this then allows you to pass the right info for each partial buffer.

Exactly the same approach that can be used to implement sample accurate automation in VST2. For a plugin that minimizes per-process-call calculations, lots of small buffers doesn't make much of a performance difference, at least until we go to the point of continuously using single-sample buffers, which ofcourse is wasteful (and hence an event mechanism for automation would be sensible addition to a VST2 like API).

But loops? I don't think sub-second loops in sequencer timeline are all that common, and for a loop of a full second, playing at 48kHz with 5ms buffers for sensible latency, splitting the buffer at loop point is something around one extra process call per 240 normal calls. If that's gonna cause a hickup it's probably time to freeze some stuff anyway for other reasons.

Does VST3 not allow this?

Post

Angus_FX wrote:nixim - you are right, they use COM but with a "C" interface, as GCC does not generate COM-compatible C++ vtables (but can generate COM-compatible "C" glue). It is likely that defining "C" versions of the COM interfaces would be an adequate fix for that, however that would presumably break binary compatibility with Steinberg's existing VST3 plugs.
The thing is, writing the interfaces in C is actually the only proper way to ensure binary compatibility. I really do not understand what the Steinberg folks had in mind with this SDK.... I don't see all plugin developpers having to rebuild all their plugins for every new GCC & Cubase release, or even worse: having to wait that Cubase is rebuilt before releasing new plugins when a new version of gcc appears to break the binary compatibility.

Post

Forget about my previous comment. It seems gcc broke the ABI between v3 and 4 to conform to the standard "Itanium" ABI, which is now guaranteed to be stable, from what you can see here:
Apple website wrote:Binary Compatibility
GCC 4.0 conforms to the Itanium C++ ABI, which is a standard for compiled C++ code. The specifications for this standard are maintained by a multi-vendor consortium and cover issues such as name mangling, class member layout, virtual method invocation protocols, exception handling, and runtime type information (RTTI) formats. You can find the latest version of this specification at http://www.codesourcery.com/cxx-abi/abi.html.

Because GCC 4.0 conforms to the Itanium C++ ABI, C++ objects are link-compatible with objects built by other Mac OS X compilers that conform to this specification. Apple guarantees that future releases of GCC for Mac OS X will also conform to the Itanium C++ ABI. This means that developers may safely ship dynamic shared libraries whose interfaces involve C++ classes, albeit with some caveats:

Apple guarantees ABI stability only for core language features. It does not guarantee stability for library classes, including std::string, std::map<T>, and std::ostream among others.

Apple does not guarantee binary compatibility between different major versions of libstdc++. GCC 4.0 ships with version 6.0.3 of the library. If a new major version (version 7.0.0) is released in the future, that library is not guaranteed to be compatible with 6.x versions.

Binary compatibility between different versions of a third-party dynamic shared library also depends on the design of the library, not just on the compiler support for a standard ABI. You must ensure that you do not introduce compatibility issues.

If you are designing a dynamic shared library for distribution, it is still your responsibility to ensure that you do not create binary compatibility problems. For example, you should not introduce member variables or virtual methods to a base class. Doing so causes a fragile base class problem and requires clients of the library to be recompiled.
(direct link: http://developer.apple.com/documentatio ... rview.html)
So I guess you can close this item in your list! :)

Post

arne wrote: And for the other answers, give me some time. I also don't like to answer stuff twice and would like to further discuss this on the vst mailinglist.
arne
Maybe a public FAQ available on a public website would be great. It would avoid the pain of searching the mailing list database in the future (when it works). I guess it could even end up in the SDK.

BTW thanks for taking time to answer these questions!

Post Reply

Return to “DSP and Plugin Development”