Wavelab and Juce 1.50 framework


#1

Hello, Jules, moderator(s) and the whole community.
First of all, let me thank you the oportunity to share questions in this forum, and, of course, congratulations to Jules for such huge project called JUCE.
Well I’d like to start with a known question: what about the future between Wavelab and VST wrapped by JUCE 1.50? Will they work together in the near future?

Thanks in advance,

Gabriel Solsona


#2

Thanks. I can’t remember the specifics about WaveLab, but are you saying it doesn’t work for some reason?


#3

Well, there are several topics in this forum talking about problems with Wavelab. On the other hand, the very first try I did with Demo plugin was hosted in Wavelab 5 and it didn’t work at all (Debug and Release dll’s). Furthermore, there is a note in amalgamated files about a problem with wavelab.
To be honest, I haven’t tried in Juce 1.46 or earlier but all my efforts in Juce 1.50 hasn’t succeded at all. Conversely, my attempts in Cubase, Nuendo and Tracktion worked flawlessly.

So, what is the status from your point of view?

Thank you Jules for your time,

Gabriel Solsona


#4

TBH I didn’t realise there was any problem with wavelab. I don’t think I even have wavelab installed, assuming that because it uses the steinberg engine, then as long as a plugin works in cubase, it’d also be fine in wavelab.

Anyone else tried it and found similar or different results?


#5

Same problems here, too. My Juce based plugins as well as the juce demo work fine in severeal versions of Cubase but crash the three last versions of Wavelab when loading the plugins. Juce based plugs and Wavelab don’t seem to like each other.


#6

Hi,
I woner if anybody has managed to get the demo plugin work with Wavelab.
I get a crash with the following setup:

WIN32
Wavelab 5 or 6
latest juce tip
JuceDemoPlugin
VST

It works as long as you don’t create an editor (return 0 in createEditor()).
If you try to create the JuceDemoEditor, Wavelab creates an instance, calls rezised() and deletes the editor afterwards bevor it gets painted. After the destructor of the DemoEditorComponent was called, Wavelab crashes at some point I can’t locate.

So it’s a gui problem and doesn’t seem to be related to the audio engine.

Has anybody found a solution for juce plugins crashing Wavelab?
Thanks


#7

Hi everyone,

Let me insist that v1.50 didn’t work in my IDE. It’s very important for me that my developement would work in Wavelab (as in other Hosts).
So, there could be an official statement about wether Juce 1.50 works or not under Wavelab. And if not, will it work in the near (planned) future?

Thank you to everyone.
Yours,
Gabriel Solsona
Madrid


#8

the “official” statement is that I’ve not tried it myself because I don’t have a copy of wavelab, but it certainly sounds like it doesn’t work. Will it work in the near future? Not imminently. Anyone want to send me a copy of wavelab?

It’s probably something very simple going wrong though, these things are generally very easy to fix if you can catch them. If you’re in a hurry, why not send me a stack trace or whatever clues you’ve seen - I might be able to just tell you how to fix it.


#9

Hi,
Wavelab crashes because it gets stuck in an endless loop in juce_MessageManager (MessageManagerLock::init(…) at line 307) which is called in juce_VST_Wrapper::dispatcher(…)):

[code]void MessageManagerLock::init (Thread* const threadToCheck, ThreadPoolJob* const job) throw()
{
if (MessageManager::instance != 0)
{
if (MessageManager::instance->currentThreadHasLockedMessageManager())
{
locked = true; // either we’re on the message thread, or this is a re-entrant call.
}
else
{
if (threadToCheck == 0 && job == 0)
{
MessageManager::instance->lockingLock.enter();
}
else
{
while (! MessageManager::instance->lockingLock.tryEnter())
{
if ((threadToCheck != 0 && threadToCheck->threadShouldExit())
|| (job != 0 && job->shouldExit()))
return;

                Thread::sleep (1);
            }
        }

        SharedLockingEvents* const events = new SharedLockingEvents();
        sharedEvents = events;
        events->incReferenceCount();

        (new MMLockMessage (events))->post();

        while (! events->lockedEvent.wait (50)) //!!!!!!!!!!!!!!!!!!!!!!!! ENDLESS LOOP !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        {
            if ((threadToCheck != 0 && threadToCheck->threadShouldExit())
                  || (job != 0 && job->shouldExit()))
            {
                events->releaseEvent.signal();
                events->decReferenceCount();
                MessageManager::instance->lockingLock.exit();
                return;
            }
        }

        jassert (MessageManager::instance->threadWithLock == 0);

        MessageManager::instance->threadWithLock = Thread::getCurrentThreadId();
        locked = true;
        needsUnlocking = true;
    }
}

}
[/code]

This gets called in juce_VST_Wrapper at:

[code]VstIntPtr dispatcher (VstInt32 opCode, VstInt32 index, VstIntPtr value, void* ptr, float opt)
{
if (hasShutdown)
return 0;

    if (opCode == effEditIdle)
    {
        doIdleCallback();
        return 0;
    }
    else if (opCode == effEditOpen)
    {
        const MessageManagerLock mmLock;
        jassert (! recursionCheck);

        deleteEditor (true);
        createEditorComp();

        if (editorComp != 0)
        {
            editorComp->setOpaque (true);
            editorComp->setVisible (false);

#if JUCE_WIN32
editorComp->addToDesktop (0);
hostWindow = (HWND) ptr;
HWND editorWnd = (HWND) editorComp->getWindowHandle();
SetParent (editorWnd, hostWindow);

            DWORD val = GetWindowLong (editorWnd, GWL_STYLE);
            val = (val & ~WS_POPUP) | WS_CHILD;
            SetWindowLong (editorWnd, GWL_STYLE, val);

#elif JUCE_LINUX
editorComp->addToDesktop (0);
hostWindow = (Window) ptr;
Window editorWnd = (Window) editorComp->getWindowHandle();
XReparentWindow (display, editorWnd, hostWindow, 0, 0);
#else
hostWindow = attachComponentToWindowRef (editorComp, (WindowRef) ptr);
#endif
editorComp->setVisible (true);

            return 1;
        }
    }
    else if (opCode == effEditClose)
    {
        const MessageManagerLock mmLock;
        deleteEditor (true);
        return 0;
    }
    else if (opCode == effEditGetRect)
    {
        const MessageManagerLock mmLock; !!!!!!!!!!!!!!!!!!!!!!!!!!!!! LEADS TO ENDLESS LOOP IN MESSAGE MANAGER !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        createEditorComp();

        if (editorComp != 0)
        {
            editorSize.left = 0;
            editorSize.top = 0;
            editorSize.right = editorComp->getWidth();
            editorSize.bottom = editorComp->getHeight();

            *((ERect**) ptr) = &editorSize;

            return (VstIntPtr) (pointer_sized_int) &editorSize;
        }
        else
        {
            return 0;
        }
    }

    return AudioEffectX::dispatcher (opCode, index, value, ptr, opt);
}[/code]

I hope with this info it’s not that hard to fix the Wavelab crashes in the near future.

EDIT:
It works in other Steinberg hosts because it never gets to this point because

if (MessageManager::instance->currentThreadHasLockedMessageManager()) line 279 in juce_MessageManager returns true and in Wavelab it returns false.


#10

Good exposition friscokid!
AFAIC thank you!

Gabriel Solsona.


#11

Excellent work!

Ok, I need to know one vital fact to give a diagnosis. Can you set a breakpoint when initialiseJuce_GUI() is called, and have a look at the stack trace to see if it’s being called from the message thread or a background thread.

If it gets called from a background thread, then that’ll mess up the message manager, which will now have the wrong idea about which thread is the message thread, and hence will be unable to make the MM locks work… If that’s the case, we’ll need to find some sort of cunning way of correcting it…


#12

Here are the debugging results of Wavelab compared to the results of Cubase, when initialiseJuce_GUI() is called. I hope this is what you meant (sorry it’s the German version of MSVS but I think that doesn’t matter in this case).
From what I can see in Wavelab it gets called from a Workerthread and in Cubase it gets called from the Mainthread (Hauptthread), so maybe your guess was pretty good:

Wavelab:

Cubase:


#13

Yep, just as I suspected - wavelab’s creating the plugin on a background thread. (Probably not a brilliant idea, as it’s not just juce plugins that might get messed up)

Bit tricky to find a good fix… This is my best guess:

        else if (opCode == effEditGetRect)
        {
#if JUCE_WIN32
            if (IsGUIThread (false))
                MessageManager::getInstance()->setCurrentMessageThread (Thread::getCurrentThreadId());
#endif

            const MessageManagerLock mmLock;

#14

…no, scrap that last idea, apparently IsGUIThread doesn’t work properly.

Try this instead - add this function somewhere in the vst file:

[code]#if JUCE_WIN32
static BOOL CALLBACK enumThreadWndProc (HWND, LPARAM)
{
MessageManager::getInstance()->setCurrentMessageThread (Thread::getCurrentThreadId());
return FALSE;
}

static bool checkCurrentThreadIsMainThread()
{
    EnumThreadWindows (GetCurrentThreadId(), enumThreadWndProc, 0);
}

#endif
[/code]

and then call it here:

[code] else if (opCode == effEditGetRect)
{
#if JUCE_WIN32
checkCurrentThreadIsMainThread();
#endif

        const MessageManagerLock mmLock;

[/code]


#15

yep, that was the right approach but there are two things that have to be adjusted:

static bool checkCurrentThreadIsMainThread() {...}
should be:

static void checkCurrentThreadIsMainThread() {...}(just a typo I think)

AND, this is important, since all functions in the dispatcher are called from the wrong thread, we have to do the check at the beginning of the dispatcher, so that the thread is correct for all function calls. Otherwise it opens but than behaves strange and crashes on closing the plugin:

VstIntPtr dispatcher (VstInt32 opCode, VstInt32 index, VstIntPtr value, void* ptr, float opt) { #if JUCE_WIN32 checkCurrentThreadIsMainThread(); #endif if (hasShutdown)...

This version works!
Cheers
Jan


#16

Sorry, I’ve been to hasty. There are still some problems left. I get an assertion at when I debug the plugin:

void Component::internalRepaint (int x, int y, int w, int h) { // if component methods are being called from threads other than the message // thread, you'll need to use a MessageManagerLock object to make sure it's thread-safe. checkMessageManagerIsLocked ... }

…?


#17

Well, if all the dispatch calls are made from this weird thread then we can’t use this approach.

Ok, here’s another plan:

In the startup code, we can iterate all the windows, find one that’s in our process, assume that it was created by the gui thread, and use that to get the GUI thread…

[code]// Win32 startup code…
#else

static BOOL CALLBACK enumWindowsToFindMainThreadProc (HWND hwnd, LPARAM lParam)
{
DWORD processId = 0;
DWORD threadId = GetWindowThreadProcessId (hwnd, &processId);

if (processId == GetCurrentProcessId())
{
    MessageManager::getInstance()->setCurrentMessageThread ((Thread::ThreadID) threadId);
    return FALSE;
}

return TRUE;

}

static void findMainThread()
{
EnumWindows (enumWindowsToFindMainThreadProc, 0);
}

extern “C” __declspec (dllexport) AEffect* VSTPluginMain (audioMasterCallback audioMaster)
{
initialiseJuce_GUI();
findMainThread();
return pluginEntryPoint (audioMaster);
}

#ifndef _WIN64 // (can’t compile this on win64, but it’s not needed anyway with VST2.4)
extern “C” __declspec (dllexport) void* main (audioMasterCallback audioMaster)
{
initialiseJuce_GUI();
findMainThread();
return (void*) pluginEntryPoint (audioMaster);
}
#endif
[/code]


#18

No, this one doesn’t do the trick. It leads to the same endless loop in the messanger as the original wrapper.


#19

Damn! Is it even finding a window and getting its thread id?


#20

Yes, it’s finding a window and changes the thread ID here:

if (processId == GetCurrentProcessId()) { MessageManager::getInstance()->setCurrentMessageThread ((Thread::ThreadID) threadId); return FALSE; }
but that doesn’t fix it. It still leads to the endless loop.