.net VST plugins & Cubase

Mar 17, 2010 at 11:59 PM

Hello Marc!

After quite a lot of effort I was not able to make my test plugin visible in Cubase. I did not start from framework but from Core according to Jacobi.Vst.Samples.CorePlugin. I must agree, starting from framework would be more efficient, but from my point of view I do not need more than just link audio samples to DSP object. I do not need any parameters, programs, etc. Jacobi.Vst.Samples.Delay project opens fine in cubase. My implementation and CorePlugin give the same plugin properties with Jacobi.Vst.Samples.Host.exe an VstHost also. It actually differs only in plugin ID, number programs, and physical parameter values.

Jacobi.Vst.Samples.CorePlugin is also not visible in Cubase. I think actual cubase version is not important, tried with two different, same behaviour.

I tried to attach to Jacobi.Vst.Samples.Host.exe using Jacobi.Vst.Samples.Delay and filled a lot of breakpoints in the code in the end with no success. Any help or comment to somehow make things Core-only possible?

tnx An

Coordinator
Mar 18, 2010 at 7:24 AM

Framework: If you only use the VST plugin standard as a wrapper around DSP object you've already written, you should only use the 'core' and not the Framework, The Framework is meant for plugin development from scratch or when you need Programs and Parameters. So I think you've made the right choice (it also depends on the abstraction level of your DSP objects).

Cubase: I find Cubase to be very particular about loading plugins (one of the reasons why the MidiNoteMapper has an AudioProcessor ;-). One thing I found though, is that Cubase (at least my SX 2.0 version) does require the plugin to respond to the 'Identify' opcode. Next to implementing the IVstPluginCommandStub, also implement the IVstPluginCommandDeprecated20 interface on your plugin command stub object. Look at the StdPluginDeprecatedCommandStub class in Framework.Plugin for 'inspiration' on how to implement the methods. You can leave most of the empty but do implement the Identify method, return the int value for 'NvEf' (use the FourCharacterCode class). That is the same value that is hard coded in the VST C++ SDK. The Core sample plugin is the only plugin that does not implement the deprecated methods and therefor does not have an Identify method and therfor Cubase will not load it (sorry I didn't think of this sooner).

Debugging: Start the host application. Load the plugin project you want to debug into VS. Choose the Attach to Process menu option (Debug) and select the running instance of the host application you started. Make sure you see the "Managed Code" in the control above the list. Otherwise use the Select... button to change it. Hit the Attach button. You should now be able to debug your plugin.

Hope it helps.

Mar 18, 2010 at 8:51 AM

Hello!

Tnx for reply, I'll take a look over IVstPluginCommandDeprecated20 interface.

I tried to debug things in the past allready, but i can only debug vst plugins when using managed vst host (your demo or mine). With unmanaged hosts no breakpoints are hit. Yesterday i also tried to trace to cubase using delay demo (according to instructions -> Cubasele.exe.config). In config file i defined probe paths, and inserted plugin name when required. (2 times I think). I deleted ...dll.config file from VST plugins directory not to interfere with .exe.config. I opened Cubase LE, attached VS to it, and opened delay plugin. Output window only showed that dll's were loaded. No function calls and return values. But plugin loaded OK. I checked both managed and native code type before attaching.

I must say I'm not familiar with tracing (and debugging) through attaching on a process so sorry if there is some obvious stupid mistake I made.

An

Coordinator
Mar 18, 2010 at 9:32 AM

When you attach to Cubase, does the dialog say 'Managed' (just above the process list in the Attach to: textbox)?

Tracing is sometimes dificult to get going. All the names must match exactly. The Plugin name is the name of the renamed interop assembly without the file extension. Also: the trace switch must be on Verbose in order to see all the interop method calls.

If you copy the app.config that is with the interop project (source code), you have to replace 4 place holders with the plugin name and set the switches from Off to Verbose. If you want to trace multiple plugins, just duplicate the settings with a different plugin name.

You can use DebugView (tool by Sysinternals/Microsoft) to view the trace output (generated by DefaultTraceListener - which is [to my knowlegde] always present until you remove it explicitly).

Hope it helps.

Mar 18, 2010 at 9:58 AM

Hello again!

Implemented IVstPluginCommandsDeprecated20 acording to StdPluginDeprecatedCommandStub class and things WORK now. Perfect.

Mar 18, 2010 at 10:34 AM

Seems I said it too quick. OK Plugin is loaded but as soon as audio gets started i get the following message:

---------------------------
VST.NET Error
---------------------------
System.ArgumentOutOfRangeException: The value should lie between '0' and '255'.

Parameter name: index

Actual value was 256.

   at Jacobi.Vst.Core.Throw.IfArgumentNotInRange[T](IComparable`1 argument, T minValue, T maxValue, String argumentName)

   at Jacobi.Vst.Core.VstAudioBuffer.get_Item(Int32 index)

   at VstKasFir.VSTKasFir.ProcessReplacing(VstAudioBuffer[] inputs, VstAudioBuffer[] outputs)

   at Jacobi.Vst.Interop.Plugin.PluginCommandProxy.Process(Single** inputs, Single** outputs, Int32 sampleFrames, Int32 numInputs, Int32 numOutputs)


---------------------------
OK  
---------------------------

Now I am able to debug attached to cubase. Maybe I did something wrong in the past... When I set breakpoint at SetBlockSize the input parameter is 512 (that value is also on device's CP) and SetSampleRate input value is 44100. My plugin is 2-in 2-out. If I set breakpoint at ProcessReplacing i see that each of input and output buffers have 256 elements instead of 512. So exception is logical as i list to invalid index. My plugin flags are next: VstPluginFlags.HasEditor | VstPluginFlags.CanReplacing | VstPluginFlags.ProgramChunks. Am i missing something here.

Mar 18, 2010 at 11:19 AM

With Vsthost things work as expected. SetBlockSize of 512 gives 512 element buffer per channel in processReplacing as input. I don't know anymore is it worth to get things working under Cubase as it has quite "strange" (to be polite) treatemnt to VST plugins. With all other hosts I can run things WP. I must say that ASIO implementation from Steinberg is far more granular and standard-like Even i was able to make custom wrapper around ASIO without any serious problems. It is difficult to understand that steinberg's products mess VST things around... Just my opinion.

Coordinator
Mar 18, 2010 at 11:26 AM

As I understand the VST specs is that SetBlockSize is used for pre-allocation to prepare for audio processing (for instance see the Delay sample: it uses that value to allocate the echo buffer).

You should always work with tThe SampleCount property on the VstAudioBuffer themselves for the processing itself. That property should indicate the right value.

Apperently Cubase has some 'feature' that causes this behavior. You will find, as you test with multiple hosts, that they all differ in some way or another. :-(

Hope it helps.

Mar 18, 2010 at 12:25 PM

Tnx Marc.

Obviously i will have to change my DSP as in some cases requires fixed-length packets of audio samples. No problem at all. As I can see in delay demo you rely on SampleCount of vstbuffer.

tnx.

Mar 18, 2010 at 5:34 PM

Hello.

I changed my code to non-fixed length packets. It works in Cubase now. Sometimes when closing VST plugins in cubase (and Vsthost also) i got the following error message:

---------------------------
VST.NET Error
---------------------------
System.NullReferenceException: Object reference not set to an instance of an object.

   at Jacobi.Vst.Interop.Plugin.PluginCommandProxy.Dispatch(Int32 opcode, Int32 index, Int32 value, Void* ptr, Single opt)


---------------------------
OK  
---------------------------

I do not now now whether error is on my side or not, it happens randomly. With Delay demo i got no such errors. But Delay demo is editorless plugin in contrast my plugins have editor with instant graphic refreshing (level meters). I timplemented synch (lock statements and bool switches) between processReplacing and closing methods but it did not get any better (or worse).

tnx

Coordinator
Mar 19, 2010 at 6:56 AM

Hmmm, this could be a bug in Interop.

Can you repeat the test case with the interop tracing enabled so we can see what opcode caused this exception (or you can debug)?

Thanx!

Mar 19, 2010 at 7:23 PM

Hello Marc!

Unfortunately i did not manage to trace this error. I'm obviously doing something wrong. However I made (changed the sample code) of the test project I sent to you few days ago. Both VSTTest and VSTTest2 vst plugin have GUI with some "cheap" level meter. They do nothing with the sound, they just display abs value of the first input sample of each processReplacing (when graphics refresh takes place).

If I load the first in vsthost "level" meter moves along with spl of actual sound input. And when I load the second one, the refreshing or processing of the first stops. When I then close vsthost I get error message from the previous post. Those two test plugins behave exactly the same as my regular (FAT) vst plugins I use. I sent the test project to you by email.

thnx An

Coordinator
Mar 21, 2010 at 9:29 AM

Hi An,

I took a quick look at the code you sent me. My guess is that the problem lies with the editors of the plugin, but I cannot put my finger on it. With some test runs I also got the NullReferenceException but I also got an ObjectDisposedException a couple of times.

Hope it helps (a bit ;-).

 

Mar 22, 2010 at 12:34 PM

Hello.

Editors of those two plugins are in fact very simple ones, on purpose, to minimize possible incorrect implementation. Editors (plugins) are disposed where they should be according to vst demo projects. If I take a look over my demo code I cannot see anything that could lead to instability of actual plugin. I've tested the code as a standalone proggy also, with no disposed exceptions thrown. I my be wrong... so let's leave those ObjectDisposedExcs. Null reference Exc bothers me more. Using vsthost, if I load the second .net plugin -> it interrupts the first one.

Mar 22, 2010 at 3:49 PM

Hello again.

In previous posts you mentioned you use some static stuff in Interop... With vsthost i can see one .net plugin interrupts the other. That can happen, when they share some data - static fields in this case i think. I understood interop assembly (renamed) gets loaded the first and then it loads .net.vstdll. When plugin gets unloaded, interop still persists (is not unloaded by implementation) in app namespace. At my opinion static data could be dangerous, as there are many vst hosting apps and obviously each has some 'special plugin handling fetures'. With nonstatic system there is no possibility (and no need by implementation itself) that any data gets shared. That is just my opinion, i did not get into details of interop coude (I should not :)), nor i'm an expert in plugin technology, so this comment may be completely irrelevant.

an

Coordinator
Mar 23, 2010 at 9:21 AM

Hi An,

Yep, you're completely right of course. static state is a "not so good" idea. ;-)

There is only one thing static in VST.NET which is the AssemblyLoader class (in Core.Plugin). That is the only state that is static (GlobalProbePaths). All other state is owned by a specific plugin instance and coupled to the AEffect structure that identifies it in the Host. There are a couple of static methods in Interop (like the function pointers specified in the AEffect struct) but those all "unpack" a 'this' pointer (of an object) and forward the call.

I'm a little short on time right now but I will try to look at your code further and see if I can determine what is causing the processing of one plugin to stop when the other is loaded.

Thanx!

Mar 23, 2010 at 1:45 PM

Tnx marc for reply!

I took a little closer look to demo projects (focused on CorePlugin). I found at my opinion incorrect implemetation of EditorClose method where you Close editor form instead of just leaving it alone. As i think, things get closed and disposed in Close method. I also added the deprecated stuff and valid plugin id to make thingie running in Cubase. With those "corrections" I can open as many CorePlugins I like, close them, reopen the again... stable. In cubase and vsthost. So there is obvious something wrong (very base) with my implementations. With working CorePlugin I think I have enough "debug headroom" left to find what is wrong. Sometimes a little time is needed to make the right decision... So do not waist your time with my code.

An

Coordinator
Mar 23, 2010 at 5:43 PM

Why do you feel that disposing the form on EditorClose is not correct?

The "pattern" that is implemented by VST.NET is that the UI is separate from the processing. So when the editor is created it connects to the processing values (like you Vu meter example) and displays the appropriate UI. When the Editor closes the UI detaches from the processing objects and closes (Dispose). Sometimes VST Paramater are used to communicate the processing info to the UI. You should realize that the thread that processes the audio (and/or) Midi in your plugin is NOT the thread that is used for the UI, so you need synchronization between the two. So you should never reference UI controls from within your plugin processing code.

Hope it helps.

Mar 24, 2010 at 2:39 PM

Hello.

In my DSP system I use DSP 'processors' that are derived from Control (in case of GUI), they have the Process method -> pushed by audio hardware and separate graphics thread (with synch to Process thread). Using my stuff with VST implementation around I got my thingie disposed when I closed  the plugin editor (on Hide). Exc was then logical. My architecture differs from VST.NET design pattern as graphics and processing live in the same object. There are at least two therads involved- processing and graphics, snched of course. Thnx for making things clear to me. I think now I have quite a chance to make a stable plugin.

an

Mar 24, 2010 at 7:10 PM

Hello again.

After quite a lot of effort I still did not manage things work correct.

---------------------------
VST.NET Error
---------------------------
System.NullReferenceException: Object reference not set to an instance of an object.

   at Jacobi.Vst.Interop.Plugin.PluginCommandProxy.Dispatch(Int32 opcode, Int32 index, Int32 value, Void* ptr, Single opt)


---------------------------
OK  
---------------------------

OR

---------------------------
VST.NET Error
---------------------------
System.NullReferenceException: Object reference not set to an instance of an object.

   at Jacobi.Vst.Interop.Plugin.PluginCommandProxy.Process(Single** inputs, Single** outputs, Int32 sampleFrames, Int32 numInputs, Int32 numOutputs)


---------------------------
OK  
---------------------------

where _commandStub is null reference.

I think I did things the same way as in core.plugin with only one difference; i use my DSP object to process audio, no user interface. Used try catch block inside process replacing method, with breakpoint in catch block. It was not entered at all. This DSP object gets created in GetPluginInfo from separate assembly. It is no difference whether config file is used, or plugin is started from bin/Debug without it. There are no ObjectDisposed excs wihich appeared in demo projects I sent to you. They were my fault. I don't know now what should I do or what am I doing wrong to make things running stable.

an

Coordinator
Mar 25, 2010 at 5:48 PM

It is unclear to me how the _commandStub can become null while the PluginCommandProxy is still being called (Process/Dispatch).

If you like you can send me your code and I can debug the problem. Please provide clear instructions how to reproduce the issue.

Thanx.

Mar 31, 2010 at 7:48 AM

Hello Marc,

I'm sorry, right now I have to finish one project and I don't have much time for anything else till next week.

Thanx an