Wavelab problems


#1

I know there have been problems between Wavelab and Juce and that those are fixed now, but I found a strange thing, reproducable in the Juce Demo plugin:

it seems that rendering with Wavelab causes the timer for gui updating to stop after the render. I have put some debug codes is the constructor and destructor of the Juce Editor class and it seems that during rendering a new instance of the Editor is created and afterwards destructed. I see this patern:

patch plugin:

construct
destruct
construct

rendering:

construct
destruct

unpatch plugin:

destruct

So, after rendering, the first editor is still there, but its timer stopped and the gui is not updated anymore.

Any idea why this happens and what to do about it?


#2

Sounds like wavelab doing something strange with threads, though can’t really see exactly what it might be doing…


#3

The timer seems to be stopped when creating a new instance of the editor during the rendering. The instance is created while another instance still exists. Is this legal? If not, is there a way to prevent a host from doing this?

The original editor is still up and running after rendering a file, but will not be updated anymore from the processor. I tried to start the timer again by force but this does not work, it just doesn’t start. Something is preventing it to run.

There must be more people that have this problem. Any ideas?


#4

It seems that Wavelab instantiates a whole new plugin for the rendering, not only an editor. The timer of the original instance stops as soon as the new instance is created.


#5

I removed this hack in the VST wrapper and the timers start working again…:

[code] void doIdleCallback()
{
// (wavelab calls this on a separate thread and causes a deadlock)…
// if (MessageManager::getInstance()->isThisTheMessageThread()
// && ! recursionCheck)
{
recursionCheck = true;

        juce_callAnyTimersSynchronously();

        for (int i = ComponentPeer::getNumPeers(); --i >= 0;)
            ComponentPeer::getPeer (i)->performAnyPendingRepaintsNow();

       recursionCheck = false;
    }
}

[/code]

Jules, do you think it will be safe if we move the juce_callAnyTimersSynchronously() out of the recursion check routine, leaving the for loop intact?


#6

You can’t remove that check - callAnyTimersSynchronously() can only safely be called by the message thread. It could be the case that it’s actually being called by the message thread, but I doubt it. If wavelab actually had a normal message thread running while it’s rendering, then there’d be no need for it to be called, anyway, as the timers would be triggered by events.

I’ve really no idea what they must be doing to get it into that kind of state. Perhaps there could be some kind of logic in there that says “if this is the message thread or if we’re in wavelab and the real message thread has stopped responding, then do a synchronous timer callback…”


#7

Understood.

I changed this:

if (opCode == effEditIdle) { checkWhetherWavelabHasChangedThread(); <<<<< added doIdleCallback(); return 0; }

forcing Wavelab to change to the MessageThread during the idle call and this seems to work. Any thoughts?


#8

As a sidenote, adding that line also allows me to use the sendChangeMessage() in Wavelab in another plug. PopupMenu starts working again too in Wavelab after a render.


#9

Sorry, that won’t work! The whole point of checkWhetherWavelabHasChangedThread() is that is should only be called in functions which are definitely only being called by the message thread. Since we know that the idle callback gets called by any old thread that wavelab decides to use, doing that would break the logic completely.


#10

So you are saying that the doIdleCallback() may only be called by the message thread, BUT, if Wavelab decides to call it in another thread we can’t force it to use the message thread? Then how is the code inside doIdleCallback() supposed to work? As soon as Wavelab starts rendering a file, is stops calling doIdleCallback() in the message thread and uses another one and leaves it that way, like you said.


#11

Bunch of hackers. How about this:

    void doIdleCallback()
    {
        // (wavelab calls this on a separate thread and causes a deadlock)..
        if ((MessageManager::getInstance()->isThisTheMessageThread()
              || (getCurrentProcessLevel() == 4 /* kVstProcessLevelOffline */ && getHostType().isWavelab()))
             && ! recursionCheck)
        {

#12

no good, because after the rendering Wavelab continues to call doIdleCallback() with the other thread, not the message thread.


#13

That should still work though, shouldn’t it?


#14

I added these debug lines in doIdleCallback(), because debugging Wavelab is a nightmare:

		Logger::outputDebugString(T("message thread? ") + String(MessageManager::getInstance()->isThisTheMessageThread()));
		Logger::outputDebugString(T("rendering? ") + String(getCurrentProcessLevel() == 4));

before rendering:
[3968] message thread? 1
[3968] rendering? 0

during and after rendering:
[3968] message thread? 0
[3968] rendering? 0

Are you sure that this getCurrentProcessLevel() == 4 is correct?


#15

I’m sure that that’s the correct way to check whether the host is rendering… But whether wavelab actually returns the value that it’s supposed to is a whole different question! Judging by the rest of their implementation, it’d be no surprise if this didn’t work!


#16

Just for the sake of testing I changed the if () statement to:

if (! recursionCheck)

Obviously the timer works now, even after rendering. But other things fail after the render, like a PopupMenu that stops working and the overall gui seems more sluggish, like the VU meters. It seems that there is more going on than just the timer. My earlier hack (forcing Wavelab to change to the message thread before calling doIdleCallback()) seemed to work without these drawbacks. Couldn’t we make a fix that does something like that, without upsetting WaveLab, as you suggested?


#17

btw, WaveLab output getCurrentProcessLevel() = 0 all the time, rendering or not. This is in WaveLab 5 Demo.


#18

Your hack didn’t force wavelab to change anything at all - it just stopped the Juce message manager from knowing that the wrong thread is being used.

Like I said, that’s ok as long as we know for sure that this thread will never run at the same time as the event thread, but if it did, then it’ll cause all kinds of trouble. So it’d only be something that could be done when you know for sure that wavelab is rendering, and not delivering events. If there’s no way to tell whether it’s rendering, then I don’t know how you could make this work safely.

Bunch of hackers…


#19

If both hacks don’t work is there any other thing we can try? I know it is a Wavelab problem, but Wavelab is used by many people out there and Juce based plugins should work in it too.

I have only tested Wavelab 5, maybe version 6 works better. Does anyone know? The Juce plugins (including commercial ones) I tried in Wavelab 5 all have the same problem or didn’t work at all.


#20

just noticed you commited a change to the AudioProcessor class:

Added a hasEditor() virtual method to the AudioProcessor class, which you’ll need to implement in your plugins so that we can work around non-standard threading behaviour in Wavelab.

Can you elaborate a bit? Does this cure the Wavelab threading problem?