Pure MIDI VST Plugin win32 - have some questions (multiple instances crash)

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

Post

Hi. I'm looking for some help from other developers.
Currenlty my freeware MIDI plugin BlueARP
http://www.kvraudio.com/forum/viewtopic ... sc&start=0
has some stability issues I don't know how to fix.
It's all OK when I run single instance, but it sometimes crashes when using 2 or more instances.
I'm using Delphi7 for development, but I think this issues are general for win32 VST development.

My first suspiction was about concurrent calls of GetTimeInfo(). I call it frequently to obtain PPQ position from host. Then I created global critical section oblect (TCriticalSection) and surrounded calls to GetTimeInfo like this.

Code: Select all

CriticalSection.Enter;
GetTimeInfo();
CriticalSection.Leave;
It became better but didn't solve the problem completely.
Critical section is a global var, created in initialization part of a library.
Another suspiction is that I use global structure with GUI-related variables like colors, margins, fonts. I fill it also in initialization section and then just read. May it be the problem?

Post

graywolf2004 wrote:My first suspiction was about concurrent calls of GetTimeInfo(). I call it frequently to obtain PPQ position from host.
Once at the beginning of processReplacing is enough. The values don't change for this buffer.

And you don't specify WHEN the problem happens, in which hosts, ...

In general, don't use global variables where it can be avoided - and watch out for concurrency issues. Writing to a structure from one PlugIn while reading from another, or deleting an entry while the other PlugIn instance is still reading it, etc. - such things are normally the reason.
"Until you spread your wings, you'll have no idea how far you can walk." Image

Post

Problem happens randomly with FL Studio 10. Only when the transporort is running (when stopped, plugin just passes MIDI messages as is).
As I remember I call it only once per processReplacing() call, inside one of the nested functions. But it's a good idea to move it to the top - to keep it clear.
Thanks, I'll try to get rid of global variables.

Will it work if I'll try to read them from ini file inside Plugin.Create?
It's for sure possible to change them to constants, but I'm thinking of a more flexible way (for later support of skins or color themes).

Post

AUTO-ADMIN: Non-MP3, WAV, OGG, SoundCloud, YouTube, Vimeo, Twitter and Facebook links in this post have been protected automatically. Once the member reaches 5 posts the links will function as normal.
i've had these problems also when running multiple instances of my Delphi vst. I found it helps a great deal if you follow these two rules:

1. Avoid all global variables,
2. Limit all VCL calls to one thread

Don't know how relevant it is today but you can read about delphi dll memory management here : http://delphi.about.com/od/objectpascal ... 03003a.htm (http://delphi.about.com/od/objectpascalide/l/aa103003a.htm)

Could be that somehow these two instances of your dll are trying to access memory that isn't there's to access.

For number one you could define a class to hold these variables.

For number two, VCL is not thread safe, so best thing to limit access to it to one thread. If you are using Toby's template, that would be the "PluginEditorWindow". If you want to change something in the window from another thread, the windows message posting system should be used

I suspect your problem is in number 1.

Post

Thanks! Yeasterday I snatched an hour for debugging and found another clue.
I have TKeyList object which is a dynamic key array with variuos sorting options. I think concurrent calls to TKeyList.AddKey(), TKeyList.RemoveKey and TKeyList.GetList() may also be the reason.

Am I right that processReplacing() and ProcessMIDI() are called independently by host and may interfer? I tried to wrap this entire functions with critical section locks - number of crashes at least decreased. But it doesn't look like an elegant solution for me.
There's also an EditorIdle call which also reads TKeyList...

Thanks for tips about windows messages and memory management. Looks like I have plenty of things to learn for this weekend :)

Post

Don't know if you already tried it, but with Delphi7 you can debug your VST while it's running in a Host. In the Delphi IDE, menu Run/Parameters... you can specify a Host application. I know it works for Ableton and Cubase, probably also for FL studio.

So just set breakpoints at the procedures you want to examine and compile/run your vst in Delphi. The Host program will be started, then use your VST in the host and the debugger will stop at the breakpoints you've set.

You can learn a lot that way how your VST interacts with certain hosts.

The other thing with the keylist, if you want to share table data between different threads, I would use a table of fixed size. In stead of deleting records for a table you could add a field "deleted" to indicate the record is deleted in stead of actually deleting it. That way you less likely to run into access violation errors and it's also faster.

Post

Well, I'm not sure found my way through this multithreaded mess but I feel like I'm on the right way.
I got rid of the global varibales, creating a structire as a EditorForm attribute and passing it to all descendants.
Also I realized that it's better to separate things:
1. Ensure plugin stability when editor is closed (no EditorIdle calls)
2. When editor is open.

I have some problems event with p.1, despite the fact I surrounded entire process() and processEvents() calls with locks. Weird.

PS. Thanks for tips, BrunoV, I'll check this out.

Post

BrunoV wrote:Don't know if you already tried it, but with Delphi7 you can debug your VST while it's running in a Host. In the Delphi IDE, menu Run/Parameters... you can specify a Host application. I know it works for Ableton and Cubase, probably also for FL studio.
Did'n know about this. Just tried and it works perfect!

Post

Well, my current results and findings are:

1. Plugin (non-GUI) part seems to be stable now. I ran 2 instances for about 2 hours and no crashes so far (knock on wood).
The key thing for this was to surround GetTimeInfo() calls with critical section locks. Critical section should be global, cause concurrent calls to host come from different instances on plugin. Without this, it crashes during the first minute.

2. GUI crashes sometimes, especially when I change params during playback or open\close the editor window. So now it's all about GUI and VCL, I feel better :).

3. There are no concurrent calls to processReplacing() and processEvents() in FL Stidio. To check it, I placed the following code

Code: Select all

 procedure processReplacing(), processEvents()
begin
  Inc(FTestCnt);
  if FTestCnt = 2 then
    FTestStr := 'Concurrent call'; // breakpoint here

  main code ...

  Dec(FTestCnt)
end.
...it never reaches this breakpoint.

Post

Well, it looks more like a monologue, but at last I found the main reason for these annoying crashes! It's so stupid... I should have deleted my previous posts, but I'll leave them to give an idea how tricky multithreading can be and how easy it's to fall into a misconcept.
The reason was just in one f****n line of code:

Code: Select all

SVelocityValStr := IntToStr(data2)
Where 1st variable is a string.
After checking the link by BrunoV
http://delphi.about.com/od/objectpascal ... 03003a.htm
I got it - don't use strings in multithreaded Delphi apps.
The reason was just that simple.
After fixing this, I removed all critical section calls and in works anyway.

Post Reply

Return to “DSP and Plugin Development”