Need advice for debugging plugin

DSP, Plugin and Host development discussion.
RELATED
PRODUCTS

Post

Hi Martin,

not sure if you can need it. Christian Budde's "VST-Plugin Unit Test":
http://www.savioursofsoul.de/Christian/programs/tools/
It`s not a bug... it`s a feature!

Post

RexXx wrote:Hi Martin,

not sure if you can need it. Christian Budde's "VST-Plugin Unit Test":
http://www.savioursofsoul.de/Christian/programs/tools/

Yes, I got that one. Only tried it briefly, and it was freezing somewhere during the tests. I will run it through all the tests one by one later.


Thanks,
Martin

Post

mystran wrote:(I'm still using MSVC2008 because 2010 IDE is just horrid)
+1
"Until you spread your wings, you'll have no idea how far you can walk." Image

Post

arakula wrote: You might want to look into the PlugIn's Info window. If it produced a crash, and VSTHost is able to notice it, it stops the PlugIn from processing. The fact is reported in the Info window.
No, nothing reported there. I will start digging into the different routines now...

Martin

Post

arakula wrote:
mystran wrote:(I'm still using MSVC2008 because 2010 IDE is just horrid)
+1
And another +1 from me.

2008 was _great_. 2010 is ...
Cakewalk by Bandlab / FL Studio
Squire Stratocaster / Chapman ML3 Modern V2 / Fender Precision Bass

Formerly known as arke, VladimirDimitrievich, bslf, and ctmg. Yep, those bans were deserved.

Post

martin_l wrote:
RexXx wrote:Hi Martin,

not sure if you can need it. Christian Budde's "VST-Plugin Unit Test":
http://www.savioursofsoul.de/Christian/programs/tools/

Yes, I got that one. Only tried it briefly, and it was freezing somewhere during the tests. I will run it through all the tests one by one later.


Thanks,
Martin
OK. I got some more details on this one:

It freezes for the following reason:

In the test for Parameter Sweeps and SetProgramme, the Unit test is sending one event after the other one and calls processEvents() without ever calling process() or processReplacing().

So far, in processEvents I added events to an internal event queue, which then was processed during the processReplacing() call. This never happened in the unit test, and at some point it must have crashed (did not get that far with the debugger...)


Here is the question:

Is my approach safe, or should I always process all events in the processEvents() routine, and not assume that processReplacing() will _normally_ be called afterwards?


Cheers,
Martin

Post

martin_l wrote:
martin_l wrote:
RexXx wrote:Hi Martin,

not sure if you can need it. Christian Budde's "VST-Plugin Unit Test":
http://www.savioursofsoul.de/Christian/programs/tools/

Yes, I got that one. Only tried it briefly, and it was freezing somewhere during the tests. I will run it through all the tests one by one later.


Thanks,
Martin
OK. I got some more details on this one:

It freezes for the following reason:

In the test for Parameter Sweeps and SetProgramme, the Unit test is sending one event after the other one and calls processEvents() without ever calling process() or processReplacing().

So far, in processEvents I added events to an internal event queue, which then was processed during the processReplacing() call. This never happened in the unit test, and at some point it must have crashed (did not get that far with the debugger...)


Here is the question:

Is my approach safe, or should I always process all events in the processEvents() routine, and not assume that processReplacing() will _normally_ be called afterwards?


Cheers,
Martin
Those events all come with deltaFrames timestamps, so they're only valid being interpreted within the next block of samples passed to processReplacing().

As to whether your approach is safe, hard to say without seeing what you're doing in processEvents. You should copy events to your own queue (don't expect the event pointers to remain valid after processEvents returns!) and of course you interpret these events in processReplacing, on the particular sample offset their deltaFrames value indicates, and remove them from your queue once they've been processed.

You also shouldn't assume processEvents will only be called once between each call to processReplacing. So it shouldn't blow up if your own copy of the events hasn't been emptied in processReplacing yet. It should just add more events to your queue.

I've built-in automatic protection to keep my event queue from growing too large, though this never happens.

Post

AdmiralQuality wrote: Those events all come with deltaFrames timestamps, so they're only valid being interpreted within the next block of samples passed to processReplacing().

As to whether your approach is safe, hard to say without seeing what you're doing in processEvents. You should copy events to your own queue (don't expect the event pointers to remain valid after processEvents returns!) and of course you interpret these events in processReplacing, on the particular sample offset their deltaFrames value indicates, and remove them from your queue once they've been processed.
Yes, in processEvents() I go through the list of events, read them out and then
add my own events to the corresponding queues of my MidiHandler, my SysexHandler and the VoiceAllocator. All of them have a their own queue.

These queues are then processed during the processReplacing() call.
AdmiralQuality wrote: You also shouldn't assume processEvents will only be called once between each call to processReplacing. So it shouldn't blow up if your own copy of the events hasn't been emptied in processReplacing yet. It should just add more events to your queue.

I've built-in automatic protection to keep my event queue from growing too large, though this never happens.
The queues themselves are implemented as a STL list of "myEvent"'s, and also include locking when pushing and popping elements, so that there should be no conflicts when processEvents and processReplacing are running simultaneously.

Actually, it is the processEvents call which will wait until the queue is unlocked. If processReplacing finds the queue locked, it will simply ignore the event in the current frame, and try again in the next one.

So far, I did not limit the queue, as I was assuming, that there should not be an overflow problem.


Cheers,
Martin

Post

martin_l wrote: The queues themselves are implemented as a STL list of "myEvent"'s, and also include locking when pushing and popping elements, so that there should be no conflicts when processEvents and processReplacing are running simultaneously.
Any host that would call processEvents while processReplacing is running is truly evil. I doubt you'll find that ever happens, though it doesn't hurt to guard against it.

Actually, it is the processEvents call which will wait until the queue is unlocked. If processReplacing finds the queue locked, it will simply ignore the event in the current frame, and try again in the next one.
You shouldn't ignore the events in the current frame as their deltaFrames "timestamps" are only valid for that frame. (As to what happens if a deltaFrames value is longer than the next frame length, I'm not sure. A well behaved host shouldn't do this, but of course it's possible.)

Sorting events by their deltaFrames value is also not guaranteed, though again well behaved hosts will pass you the events already in sorted order. You will also find negative deltaFrames values from events that are performed live as opposed to played back from recording. MIDI events are always at least one frame late when played live.

Definitely put break points at any of these special conditions so you'll see if/when they happen. I doubt any of this is the problem though.

One thing I'd recommend is cutting out chunks of code to isolate where the problem is. Say, bypass event handling entirely, and bypass your synth process, and just make your processReplacing function output a sine wave or something simple like that. Then, piece by piece, re-enable your various components and see when the trouble starts.

Post

mystran wrote: I think that's just the C# version.

IIRC even in 2010 (I'm still using MSVC2008 because 2010 IDE is just horrid), in C++ version you press ctrl-alt-P and that's it.
Yeah, the shortcut seems to work, thanks for the tip. But the feature is not in any menu by default, and according to Microsoft docs it's not supported at all.

Post

AdmiralQuality wrote: Any host that would call processEvents while processReplacing is running is truly evil. I doubt you'll find that ever happens, though it doesn't hurt to guard against it.
Yes, I did not expect that. But, I use the same event queue for automation parameter changes, which can occur at the same time. The queues perform timeordering when appending (inserting) a new event. This is my attempt to make sure that automation data and MIDI events, addressing the same parameter (e.g. through MIDI Learn) do not conflict.
AdmiralQuality wrote:
You shouldn't ignore the events in the current frame as their deltaFrames "timestamps" are only valid for that frame. (As to what happens if a deltaFrames value is longer than the next frame length, I'm not sure. A well behaved host shouldn't do this, but of course it's possible.)
I hope that this event does not really occur. But in this case, I reset the timestamp to zero.
AdmiralQuality wrote: Sorting events by their deltaFrames value is also not guaranteed, though again well behaved hosts will pass you the events already in sorted order. You will also find negative deltaFrames values from events that are performed live as opposed to played back from recording. MIDI events are always at least one frame late when played live.

Definitely put break points at any of these special conditions so you'll see if/when they happen. I doubt any of this is the problem though.

One thing I'd recommend is cutting out chunks of code to isolate where the problem is. Say, bypass event handling entirely, and bypass your synth process, and just make your processReplacing function output a sine wave or something simple like that. Then, piece by piece, re-enable your various components and see when the trouble starts.
Yes, that's the next step: switching off portions of the code, one by one, until it behaves well. But that will have to wait, as I will be away from my development computer for the next week.

Cheers,
Martin

Post

martin_l wrote:
AdmiralQuality wrote: Any host that would call processEvents while processReplacing is running is truly evil. I doubt you'll find that ever happens, though it doesn't hurt to guard against it.
Yes, I did not expect that. But, I use the same event queue for automation parameter changes, which can occur at the same time.
Yes, but automation in VST 2.4 isn't sample accurate. So setParameter gets called for all parameters that need it (ideally) between processReplacing calls. All we know is it's supposed to happen some time in that next buffer of samples, but not exactly where. (Many plug-ins also apply smoothing to parameter changes, so a single change of parameter may actually take place over many samples.)

You will find setParameter getting called during processReplacing in many hosts (this remains shocking to me) but obviously any of those changes must apply to the next buffer, as there's no way for the host to know how far through the buffer the currently running processReplacing is.


The queues perform timeordering when appending (inserting) a new event. This is my attempt to make sure that automation data and MIDI events, addressing the same parameter (e.g. through MIDI Learn) do not conflict.
That should be a fairly rare case as well. Really, it's the users' fault if they're sending MIDI and automation messages to the same control at the same time. If the control jumps around like crazy, that's exactly what they've asked it to do. But the MIDI will usually "win" because it will usually have a deltaFrames value. Where automation has no timestamp at all, other than do it as soon as possible.
AdmiralQuality wrote:
You shouldn't ignore the events in the current frame as their deltaFrames "timestamps" are only valid for that frame. (As to what happens if a deltaFrames value is longer than the next frame length, I'm not sure. A well behaved host shouldn't do this, but of course it's possible.)
I hope that this event does not really occur. But in this case, I reset the timestamp to zero.
The really smart way to handle this would be to decrement the deltaFrames value for every queue element at every sample. When the count reaches zero, the event fires. Then the events could easily overlap into the next buffer.

This might sound crazy, but there are some crazy hosts out there. I'm not sure offhand if it ever happens, but I wouldn't be surprised as there are hosts that do other wacko (you might even say "fruity") things like changing the sampleFrames size in successive calls to processReplacing.
AdmiralQuality wrote: Sorting events by their deltaFrames value is also not guaranteed, though again well behaved hosts will pass you the events already in sorted order. You will also find negative deltaFrames values from events that are performed live as opposed to played back from recording. MIDI events are always at least one frame late when played live.

Definitely put break points at any of these special conditions so you'll see if/when they happen. I doubt any of this is the problem though.

One thing I'd recommend is cutting out chunks of code to isolate where the problem is. Say, bypass event handling entirely, and bypass your synth process, and just make your processReplacing function output a sine wave or something simple like that. Then, piece by piece, re-enable your various components and see when the trouble starts.
Yes, that's the next step: switching off portions of the code, one by one, until it behaves well. But that will have to wait, as I will be away from my development computer for the next week.

Cheers,
Martin
Oh no! (I'm one of the people who can't wait to see this released. I think you did an amazing job on the SE-based one.)

Post

AdmiralQuality wrote:there are hosts that do other wacko (you might even say "fruity") things like changing the sampleFrames size in successive calls to processReplacing.
Variable block size is, like, the least wacko behavior in any host. That's why it's a parameter to process/processReplacing() instead of a parameter to resume().
Image
Don't do it my way.

Post

Borogove wrote:
AdmiralQuality wrote:there are hosts that do other wacko (you might even say "fruity") things like changing the sampleFrames size in successive calls to processReplacing.
Variable block size is, like, the least wacko behavior in any host. That's why it's a parameter to process/processReplacing() instead of a parameter to resume().
Right. But if they're daring to change block size, are they also checking the deltaFrames of the pending events to make sure that they don't overshoot the next block? (Not that that's technically illegal, but I bet you'll cause a bunch of instruments to break if you do that in a host.)

Also, block size often has audible artifacts in many plug-ins. (In particular, the update rate for automation.) So to change it on the fly is, in my opinion, not a great idea.

IMHO, sampleFrames should either be the size of the hardware buffer, which makes sense for a whole bunch of reasons. Or 1, which makes sense for a bunch of other reasons. But that's just me...

Post

AdmiralQuality wrote:
Borogove wrote:
AdmiralQuality wrote:there are hosts that do other wacko (you might even say "fruity") things like changing the sampleFrames size in successive calls to processReplacing.
Variable block size is, like, the least wacko behavior in any host. That's why it's a parameter to process/processReplacing() instead of a parameter to resume().
Right. But if they're daring to change block size, are they also checking the deltaFrames of the pending events to make sure that they don't overshoot the next block? (Not that that's technically illegal, but I bet you'll cause a bunch of instruments to break if you do that in a host.)

Also, block size often has audible artifacts in many plug-ins. (In particular, the update rate for automation.) So to change it on the fly is, in my opinion, not a great idea.
Unless, of course, they're matching the block size to the actual varying number of samples between recorded automation events.
Image
Don't do it my way.

Post Reply

Return to “DSP and Plugin Development”