How to use Jacobi.Vst.Samples.CorePlugin?

Jul 27, 2010 at 3:21 AM
Edited Jul 27, 2010 at 3:24 AM

I'm trying to open a Vst Editor window in WPF. I've followed the steps in CorePlugin project Readme.txt to compile the EditorControl.Xaml but I don't have a clue how to use it. I've run into many hurdles trying to make something happen out of this project.

The code in Samples.CorePlugin uses the classes in Jacobi.Core.Plugin. The code in VstHost use the classes in Jacobi.Core.Host. Yet both projects reference the same class names in different namespace, although VstHost seems complete and functional while CorePlugin seems empty and half-baked.

First I thought I needed to initialize EditorControl.Host but it uses IVstHostCommandStub of Core.Plugin which is not included in any examples, I can't initialize it with the Core.Host IVstHostCommandStub. PluginCommandStub in Samples.CorePlugin also seems to be a re-implementation that does nothing else than logging. Is it useful for something? Finally, OpenFileSelector doesn't seem to do anything except logging, shouldn't it open/load a plugin?

If possible, could you please explain the steps needed to initialize/open EditorControl.xaml properly?

I've read in discussion that you don't want to include a wpf project in the source code because of the DotNet 3.5 framework dependencies but I think it wouldn't hurt. It's not like the 2.0 project compiles out of the box, removing the 3.5 project from the solution shouldn't be too hard once you've figured out you need to remove missing assembly file references, remove code signing (because the key is not provided) and create/populate the '_sharedAssembly' folder in order to compile using 2.0 framework. Anyway, if there is a working implementation in WPF somewhere else please tell me.

Thanks

Jul 27, 2010 at 4:03 AM

On second thought, it seems Samples.CorePlugin is just an unused logging facility.

I'll try to implement the editor window in WindowsFormsHost like you suggested. Although I also had many problems with this solution while using Klaus Zerbe DotNet VstHost. The WindowsFormHost has 'Airspace' issues, it doesn't run well in WPF STA interface model and crashes repeatedly with fatal exception engine when doing heavy interop.

Coordinator
Jul 27, 2010 at 5:18 AM

The purpose of the Core Plugin Sample is to show you the difference of implementing a managed plugin without the VST.NET Framework. It is true that it is 'half-baked' in the sence that it is a bogus implementation (functional) that focuses mainly on the technical aspect.

The discussion about the .NET framework version (2.0 vs 3.0/3.5) has to do with the main binaries of VST.NET (Framework in particular). I would like to keep the dependency as low as possible so your end users (users that actually work with your managed plugin) shouldn't have to upgrade. There is nothing wrong with proving a sample that uses a higher version .NET framework and the CorePlugin sample was an attempt to do just that.

Once you have switched implementations to the WPF editor as described in the readme.txt you should be able to load the plugin into a VST 2.4 compatible host (VstHost.exe for example). If you would like to use an older host (< VST 2.4) you should also implement the methods of the Core.Deprecated.IPluginCommandsDeprecated20 on the CorePlugin.PluginCommandStub class. An example can be found in Framework.Plugin.StdPluginDeprecatedCommandStub (copy and paste should do the trick).

When the plugin is succesfully loaded in the host you should be able to call up the CorePlugin's editor window and see the WPF window. Note that the logging window in WPF does not work but the button should work.

If you have any troubles please be specific about the problems you encounter so I can assist you as best I can.

Hope it hepls.

PS: Is there a publilc download link for this 'Klaus Zerbe DotNet VstHost' you speak of? I haven't seen it before and would like to take a look at it.

Jul 27, 2010 at 3:30 PM

Thanks a lot for your quick answer,

Here is the link to Klaus Zerbe Vst Host article:

http://eternity.zerbe-net.org/doku.php?id=start:themen:devel_news

Direct Download:

http://eternity.zerbe-net.org/lib/exe/fetch.php?id=start%3Athemen%3Adevel_news&cache=cache&media=start:themen:vsthost.net-demo3.zip

It's a rather obscure find, it's in German but easy to translate/understand. The code is a VC++ wrapper that allows you to host a WinFormUserControl in C# and it also includes a simple sequencer that sends ProcessEvents to the Vst and a DirectSound engine to playback the Vst ProcessReplacing output.

___

Regarding what I was trying to achieve, I wanted to host a VstEditorWindow inside a Wpf application, preferably without the WindowsFormsHost wrapper. In case anybody is interested about the WindowsFormHost approach:

VstPluginContext ctx = OpenPlugin(PluginPathTxt.Text);

IVstPluginCommandStub PluginCommandStub = ctx.PluginCommandStub;

ctx.PluginCommandStub.MainsChanged(true);

System.Drawing.Rectangle wndRect = new System.Drawing.Rectangle();

// Window title
this.Title = PluginCommandStub.GetEffectName();

if (PluginCommandStub.EditorGetRect(out wndRect))
{
    System.Windows.Forms.UserControl uc = new System.Windows.Forms.UserControl();
    uc.Size = new System.Drawing.Size(wndRect.Width, wndRect.Height);
    PluginCommandStub.EditorOpen(uc.Handle);
    windowsFormsHost1.Child = uc;
}

ctx.PluginCommandStub.MainsChanged(false);
_____
Not sure what MainsChanged is for and how to handle it properly, maybe I need to call false just before plugin is disposed. I'll sort this out later.
Jul 27, 2010 at 3:42 PM

And I encountered a small bug in ASynth.dll plugin from Smartelectronix.

When calling PluginCommandStub.EditorGetRect(out wndRect) before opening the window, the size returned is too small.

The fix is easy, calling EditorGetRect again after EditorOpen returns the correct size.

Coordinator
Jul 28, 2010 at 4:49 AM

The MainsChanged functions power-on or -off the plugin. It like the power-on button on a real amp for instance, but then its virtual counterpart (I hope I dont make it more fuzzy ;-)

You need to 'power-on' (MainsChanged(true)) before you can let it process audio or midi and you have to power if off (MainsChanged(false)) before you can destroy it. For the plugin these calls translate to Suspend and Resume.

Hope it helps.

PS: Thanx for the link.

Jul 28, 2010 at 6:20 PM
Edited Jul 28, 2010 at 6:26 PM
Thanks for the explanation it sure helps, I understand exactly what you mean by: translate to Suspend() and Resume() calls to the Vst SDK ;-)

If it's not asking too much, I also have additional questions:
1. When should I call "PluginContext.PluginCommandStub.StartProcess()" and "PluginContext.PluginCommandStub.StopProcess()" ?
Each time before and after calling ProcessReplacing or once when I init the plugin and before I dispose it?
Should I call them before/after ProcessEvents?

2. I took a look at the VstHostCallback to find the OpCodes sent by the VstPlugin.
I noticed they are in a formated string passed in EventArgs.Message. Ex: ''SetParamAutomation(0, 1)''
Should I parse that String in order to interpret the Plugin calls to the host or is there another variable somewhere?
Basically, is there a preferred way to read the OpCodes and respond to them?

More broadly I'm interested to learn the proper way of building a Vst Host according to this sentence from your blog :
VST.NET also adds a Framework in an attempt to structure and clarify the posibilities of the VST interface.

I made a modular Midi sequencer in WPF and the modularity of your framework could speed up development of Vst support
compared to the usual monolithic C procedures of other Vst Host.

It's the first time I'm attempting audio programming and it's the most challenging part of my project... hopefully I'll be crazy enough to pull it off :D
Coordinator
Jul 29, 2010 at 5:11 AM
Edited Jul 29, 2010 at 5:12 AM

Questions are always good ;-)

1) The Start and Stop process methods indicate the starting and stopping of the audio engine of the host. With audio engine I mean the 'game loop' that pumps the audio buffers around across all plugins and eventually to the output. So the calling sequence would be something like (simplified).

MainsChanged(true);
StartProcess();

while(audioEngineIsActive)
{
    ProcessEvents(currentMidiEvents);
    ProcessReplacing(inBuffers, outBuffers);
}

StopProcess();
MainsChanged(false);

You should be able to see this calling sequence in the Host sample source code.

2) Could you be more specific where you saw this? VST.NET uses the opcodes defined in the VST 2.4 SDK internally in Interop. From that point on opcodes no longer 'exist'. Every opcode is represented by a method on a 'Commands' interface, both for methods that can be called on the plugin (IVstPluginCommands.cs in Core) as well as the methods that can be called on the Host -by the plugin (IVstHostCommands.cs in Core).

Hope it helps.

Jul 30, 2010 at 1:11 AM
Edited Jul 30, 2010 at 1:14 AM
Fantastic, I figured out how to process the host callback for one plugin.
Nice job wrapping the opcodes in dedicated virtual methods.
Your work and your help is very appreciated.

Of course... like ♥ boxxy ♥ I moved on to bigger better things and proceeded to host not 1 but 2 ( yeah 2! ) plugins.
I tried to implement UniqueIdentifiers in the callback methods so I can identify which plugin is calling back to the IVstHostCommandStub derived class.


I loaded two plugins and attached them with ID to one host like this:
PluginContext1.Set("HostCmdStub", hostCmdStub);
PluginContext1.Set<Guid>("UniqueID", Guid.NewGuid());

PluginContext2.Set("HostCmdStub", hostCmdStub);
PluginContext2.Set<Guid>("UniqueID", Guid.NewGuid());

Then in one of the callback methods, I'm trying to get the ID of the plugin who raised the call:
public void SetParameterAutomated(int index, float value)
{
Guid ID = PluginContext.Find<Guid>("UniqueID");
}

I thought the IVstPluginContext member variable declared in the IVstHostCommandStub interface would change depending on which plugin originated the call,
but it's always assigned to the last plugin attached to the host, in this case PluginContext2.

Do I need to create one IVstHostCommandStub derived class per plugin Hosted?
Or is there something obvious I don't understand?
Coordinator
Jul 30, 2010 at 5:18 AM

No, I think you understand just fine. ;-) Although you do not need to create a specific 'class' for each plugin. You can create one class and instantiate it for each plugin.

The design allows for both scenarios: a single IVstHostCommands instance for all plugins or one instance for each plugin. When you have context data that is specific to a plugin you 'have to' give them each their own IVstHostCommands instance too.

VstPluginContext pluginCtx = VstPluginContext.Create( "C:\\....", new MyVstHostCommands(someReferenceToHostSpecificInformation) );

This is a little over-simplified because you probably want to keep track of these VstHostCommand instances for they represent (the entry point of) each plugin.

Hope it helps.

Jul 30, 2010 at 4:03 PM
Edited Jul 30, 2010 at 4:11 PM

Yeah obviously I meaned create one instance of VstHostCommandStub per plugin loaded not one Class per plugin. :)

I did just that and adding custom data to the plugin context dictionary worked as expected :D

Also I changed the code to only keep references to one VstHostCommandStub per plugin and use it to access the PluginContext and PluginStub later on.

Now that the basic mechanics of the host are working, I think it's time to let this thread die and progress on my own.

Again, thank you very much for your assistance in getting me started.