Delphi ASIO & VST sourceforge project

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

Post

Hello,
a little problem with Sysex ? :(
The following example shall send a sysex message to MidiOut.
Program:
MiniHost (Examples => standalone => MinihostCore)
Insert: A button for test.

Code


Var
XG_ON: array[0..9] of byte =(9,$F0,$43,$10,$4c,$00,$00,$7e,$00,$F7);

procedure TFmMiniHost.Button1Click(Sender: TObject);
Var S : String;
i : integer;
begin
SetLength(S,9);
for i := 1 to 9 do S := char(XG_On);
MidiOutput.SendSysEx(CurrentMidiOut-1,S);
end;



I think this code is ok. The sysex message is ok, too.
But midioutput sends not a sysex message, but a wrong midi message.
That means, this button sends a message, but not a sysex message.

Is here a problem in the sysex function ?
Christian explains, this code is not testet.

with best regards

Norbert

Post

Try something like this:

Code: Select all

const
  XG_ON: array[0..8] of byte = ($F0,$43,$10,$4C,$00,$00,$7E,$00,$F7);

procedure TFmMiniHost.Button1Click(Sender: TObject);
Var S : Array of Byte;
begin
  SetLength(S, Lentgh(XG_ON));
  Move(XG_ON[0], S[0], Lentgh(XG_ON));
  MidiOutput.SendSysEx(CurrentMidiOut-1, S);
end;
Or change the s := ... to s := ...

Christian

Post

Hello Christian,

I have made a lot of tests. But allways the same problem. MiniHostCore sends
a message, but not a sysex message.

with best regards

Norbert
Last edited by Norbert Stellberg on Thu Sep 06, 2007 8:18 am, edited 1 time in total.

Post

Norbert Stellberg wrote:
Christian Budde wrote:Try something like this:

Code: Select all

const
  XG_ON: array[0..8] of byte = ($F0,$43,$10,$4C,$00,$00,$7E,$00,$F7);

procedure TFmMiniHost.Button1Click(Sender: TObject);
Var S : Array of Byte;
begin
  SetLength(S, Lentgh(XG_ON));
  Move(XG_ON[0], S[0], Lentgh(XG_ON));
  MidiOutput.SendSysEx(CurrentMidiOut-1, S);
end;
Hello Christian,
I not saw, you had declared S as Array of Byte.
This is ok:

Code: Select all

  SetLength(S, Length(XG_ON));
  Move(XG_ON[0], S[0], Lentgh(XG_ON));
But the procedure

Code: Select all

MidiOutput.SendSysEx(CurrentMidiOut-1, S);
required a string.

Code: Select all

procedure TMidiOutput.SendSysEx(const aDeviceIndex: integer; const aString: string);
Is here the problem ?
I have allways send a string to MidiOutput.SendSysEx

What is right ?

with best regards

Norbert

Post

Hello Christian,
another change request: (You see, I am work with it) :)

VSTHost.VstPlugIns[Index].ShowEdit
shows the plugin window allways at screen center.
Often I have open more then 2 plugins.
I an earlier version I saved the position and the size
of the plugin in an inifile.
Sometimes is the GUI of plugin greater then the host.
Therfore it is practicable, to set the size and the
position of this plugins.

My idea is
in procedure TVstPlugin.ShowEdit
to made this change:

Code: Select all

     
BorderStyle := bsSizeable;
BorderWidth := 0;
Position := poDesigned;

I hope, you can do that.
Thanks a lot.
with best regards

Norbert

Post

Hello Christian,
sorry, I am again.

Perhaps it is better you does made 3 properties for it.

Code: Select all

FBorderStyle        : TFormBorderStyle;
FPosition           : TPosition;
FBorderIcons        : TorderIcons;

Procedure SetBorderStyle(BorderStyle: TFormBorderStyle);
Procedure SetPosition(Position : TPosition);
Procedure SetBorderIcons(BorderIcons :TBorderIcons);

property GUIBorderStyle: TFormBorderStyle read FBorderStyle write SetBorderStyle default bsSizeable;
property GUIPosition : TPosition read FPosition write SetPosition default poScreenCenter;
property GUIBorderIcons : TBorderIcons read FBorderIcons write SetBorderIcons default [biSystemMenu];

procedure TVstPlugin.ShowEdit;
.............
BorderStyle := GUIBorderStyle;
Position := GUIPosition;
BorderIcons := FBorderIcons;
...........


Procedure TVstPlugin.SetBorderStyle(BorderStyle: TFormBorderStyle);
begin
  FBorderStyle := BorderStyle;
end;

Procedure TVstPlugin.SetPosition(Position : TPosition);
begin
  FPosition := Position;
end;

Procedure TVstPlugin.SetBorderIcons(BorderIcons :TBorderIcons);
begin
  FBorderIcons:=BorderIcons;
end;


with best regards
Norbert

Post

Dear Norbert,

you can also use the ShowEdit(Form : TForm) option with an empty form. So you will have all possibilities you need.
Or even use ShowEdit(TForm(SomePanel)). to have the VST Plugin inside a panel. This would allow you to add a preset selector above the plugin.

Just some thoughts,

Christian

Post

Hello Christian,
Christian Budde wrote:you can also use the ShowEdit(Form : TForm) option with an empty form. So you will have all possibilities you need.
Or even use ShowEdit(TForm(SomePanel)). to have the VST Plugin inside a panel. This would allow you to add a preset selector above the plugin.
Can you demonstrate this with 2-3 lines ?

with best regards

Norbert

Post

Absolutely an excellent piece of work, these ASIO/VST components, they are a great way to get started easily. I want to give a tip to people that there is a small incompleteness in the sineSynth Delphi sample for people who may be struggling with this. The issue is that the VSTModuleProcess path is a multithreaded path (which gets called by multiple real-time priority threads). This means that any data you access in there must protected against concurrency to make it thread-safe.

For instance the following code from the sineSynth sample accesses the voice List:

for j:=0 to sampleframes-1 do
for i:=0 to Voices.Count-1
do outputs[0,j]:=outputs[0,j]+Voices.Process;

What is not taken into consideration here is thread safety, voice are concurrently added and deleted as a response to midi events. This creates a race condition. It is possible that a voice gets deleted just while in the process path. This means your are calling voices.Process for a just deleted voice object.

It is not likely to appear quickly if you do not run on a multicore/multiprocessor machine and even then it will not appear quickly but you will notice a crash occasionally if you play polyphonically and intensively.

The solution to this is to use a synchronization object such as a mutex and wrap it around every access (both read and write) to the voiceList. VST documentation advices against using lock because this is a time critical path but I see no other solution to assure thread safety. Of course the lock should be placed outside of the loop so:

WaitForSingleObject(mutexHandle,INFINITE); // acquire mutex outside any loop
for j:=0 to sampleframes-1 do
for i:=0 to Voices.Count-1
do outputs[0,j]:=outputs[0,j]+Voices.Process;
.....
.....
ReleaseMutex(mutexHandle); // release lock


I think is a good solution to the problem. This lock will be needed every time you read or write to the voice list. There may be other issues which need consideration for thread safety that I will reflect on later.


/Daniel
http://www.resplendence.com

Post

hmm
Daniel, I get what you are saying, but this isn't the case with VST. A VST plug is multithreaded, but the divide is between DSP and UI. If the UI is adding or deleting voices then you could queue the changes rather than deal with them directly, but outside of process.

DSP
Last edited by duncanparsons on Mon Oct 08, 2007 9:47 am, edited 1 time in total.
Image

Post

Yes you could cue them but how do you then after synchronize between the UI and the VST process path ? Or better, how do you assure that the voice list (or any data accessed in the process path) is not being written to at exactly the same moment ? All data that is being read and written in a multithreaded fashion needs to be protected with an appropriate lock.

/Daniel

Post

..or just don't let the UI alter notes!!

MIDI handling is done from host to DSP code in the same thread. I guess the simplest lock-free method of adding notes is to get the UI to stack the notes, then in an idle call [when both threads have free access to each other], have the DSP add the notes. You won't be sample accurate, but then you don't expect that from a UI - it's a cheap and cheerful process...

In the normal process cycle, a call is made from the host to processevents, telling the DSP code what MIDI events are associated with the coming block of samples, then the call is made to process/replacing and that code should deal with the MIDI events in the correct places.

I know what you're talking about here, the multithreaded stuff, but there is little to worry about with most of VST. You're more likely to have issues with multiple instances and poor management of globals. If the VST spec performed poorly in this area there would have been more of an outcry before now!

Having said that, it's some time sice I've looked at the sine synth to see how it's adding the notes from the UI! But it is and example, and just a starting point as you have pointed out ;)

DSP
Image

Post

What I am seeing is that exceptions such as these are being handled and ignored by the host application. This might be understandable because at the end of the day what we want to do is make music and the show must go on. It is possible that the VST host does not want to run the risk of being blamed for a malfunctioning third party plug in. But the downside of all this is that it provokes and tolerates poor programming practices resulting in lower quality plugins. Windows for example blue screens as soon as there is any indication of a problem in the kernel, just in case, before data corruption can grow. Vst hosts could follow the same example and bark immediately on a faulty plugin instead. Obfuscation of problems is not a good path to stability. Because the VST and the host share the same process space, it has every chance of corrupting the host application. I just witnessed that in Minihost+sineSynth, when the title bar got corrupted and it still produced some but unrecognizable audio. This is also a good reason why you should test your plugin running under the debugger or under a VST host which does not handle and ignore exceptions.

/Daniel

Post

I haven't looked at the Sine Synth example either. Could you avoid a mutex and also a race, if the MIDI reception code only adds MIDI events to a lock-free FIFO, and the Render method processes any new events in the FIFO at the beginning of each Render?

That way, any MIDI events which arrive during a buffer render, will get picked up and processed in the next buffer render?

That would have the same effect (I think) as putting a lock on the render thread-- perhaps incurring some delay on the response to MIDI Input. But it would allow MIDI Input calls to return right away? If the process sending MIDI to the plugin has a lot of data to send, it might not like having to wait for a lock to go away?

It is just an idea, which I haven't thought about closely enough to pick out possible flaws.

From your description of the possible race condition, I don't think it would happen in my hosting code. I maintain separate Playback MIDI and Thru MIDI cueues for each hosted synth, and I 'spoon feed' the merged MIDI to a plugin immediately before calling Render.

So maybe there could be a lot of MIDI events waiting in my Playback Cueue, with stuff 'trickling in' on the MIDI Thru Cueue.

When it comes time to render the next slice, if the next slice is 2 milliseconds, I only send whatever MIDI is supposed to play in that next 2 ms (if any), right before rendering the buffer.

So even if my rendering is running on a different CPU than the MIDI handlers, the VST will never receive any new MIDI during a render. My code will ONLY send MIDI to the VST immediately before a render, and it ONLY sends notes/controllers which are relevant to the current render time boundaries.

OTOH, even if you are putting a lock on your render, it would still work the same for my host code, since my host code will never send midi while the plugin is rendering or vice-versa.

As usual, logic errors and wrong conclusions are quite possible and perhaps to be expected in the above.

Post

Theoretically speaking, this design is flawed if your FIFO does not have any lock synchronization meachanism. You must make sure that read and write access to your FIFO happens atomically, which means a lock. Otherwise any thread at any moment inbetween instructions may get preempted, for instance exactly while you are reading from or writing to your FIFO.

Fortunately, this consideration is only just theoretical, keys pressed on a keyboard,that's just once in so many billion instruction cycles so with some considerations your chances are getting very small.

/Daniel

Post Reply

Return to “DSP and Plugin Development”