Wavelab and Juce 1.50 framework

Wow. Ok, this is a tough one.

What it really comes down to is that initialiseJuce_GUI() really has to be called on the main thread, so we’ve got to find something that gets called by that.

How about the DllMain call? Does that happen on the gui thread?

I was too buisy the last week, but now I had some time to get back to this one:

DllMain is called from a background thread and not from the MainThread.

The “DWORD threadId = GetWindowThreadProcessId (hwnd, &processId);” you suggested finds a threadId but the WindowThread isn’t the MainThread.

In the entire “// Win32 startup code…” section is nothing that gets called from the MainThread.

No good news so far…

Agh… How do other plugins possibly work?? In windows it’s just not possible to send or receive messages unless you get at least a brief chance to run on the thread that’s going to be running the dispatch loop.

I don’t think we can make wavelab create the gui from its main thread.
I found a discussion of others who had the same problem using wxWidgets for the gui here:

http://www.kvraudio.com/forum/printview.php?t=107184&start=0

And since I assume, making juce deal with the situation that a host doesn’t create and manipulate the gui from its mainthread would mean a huge effort, the chances are not very good to make juce and wavelab work together.

Just wondering if there is any other options since wavelab is a very important audio editor with a lot of users and not being able to support it is a major issue. It’s amazing that steinberg decided on different implementations of their own format across their own audio software.

I’m pretty stumped by this one, but am still pondering it. We can get a handle on the main window, and from that can get the main thread ID. It might be possible to create juce’s messaging window on a background thread but then attach it to their main thread somehow…

This won’t work, I think. I did a lot of testing and it was possible to get a handle on the main window, but this is not handled by the main thread. To be more specific, when I call
"DWORD threadId = GetWindowThreadProcessId (hwnd, &processId);"
and hwnd is the main window handle, then the returned threadId is definetly not the main thread…

The strange thing is, that when Wavelab starts up and recognizes a new plugin for the first time, the plugin gets checked and the complete WIN32 startup section in the juce_VST_Wrapper gets called from Wavelabs main thread. But when you open the plugin, the WIN32 startup section is called from a background thread. It will never be called from the main thread again, untill the plugin was recompiled and therefore Wavelab does its initial check again.

Well, I’m flummoxed then. I wonder if there’s a way to iterate the active threads and figure out which one is the main thread?

Any headway with the wavelab issue? There must be a way as it stands no Juce plugin is working… Please jules if you have the time it would be greatly appreciated if you can have a look in to this again.

I did find an old demo version of Wavelab to try but haven’t had chance to try debugging it yet.

It seems that this issue only affects version >= 1.50 of the juce framework. I tried the juce VST plugin demo(juce_vst.dll) of a 1.46 release and it worked without any problems in wavelab. The new one(JuceDemoPlugin.dll) of 1.50 doesnt work anymore.

Hope this helps finding the issue.

[quote=“patrickkunz”]It seems that this issue only affects version >= 1.50 of the juce framework. I tried the juce VST plugin demo(juce_vst.dll) of a 1.46 release and it worked without any problems in wavelab. The new one(JuceDemoPlugin.dll) of 1.50 doesnt work anymore.

Hope this helps finding the issue.[/quote]

Sorry, I think that 1.46 just appeared to work because it wasn’t correctly locking the message manager, so never caused a deadlock. It might have worked, but only by chance - it certainly wasn’t thread-safe.

Yep, the difference to 1.46 is that the dispatcher didn’t use the MessageManagerLocks then. Without the locks there’s no issues in Wavelab. Now admittedly I’m not quite “threadsafe” myself :smiley: but couldn’t it be that the MessageManagerLocks are not needed when the dispatcher is called from a background thread like in Wavelab? I.e. could one check for that and only create the MessageManagerLocks when actually needed? If this is completely stupid then beat me… but please tell me why it’s stupid before. :slight_smile:

It might work in your special case, but this is definetly no solution. I tried two of my plugins without that lock and although they where loaded into wavelab, there was a lot of strange behaviour and after a while some moreless random crashes, especially when working with timers and threads (maybe coincidence, but it seemed to me like this).

So there is still no solution to load juce plugins into wavelab.

What about using win32 “AttachThreadInput” http://msdn.microsoft.com/en-us/library/ms681956(VS.85).aspx ?

Basically, it merges the inputs/dispatch loop of 2 threads, so your plugin will receive the same message as the main thread.
Or you could use the GetThreadTimes (returns the thread creation time) to figure out the first thread (it’s the main thread).

Hmm, maybe using GetThreadTimes would be the answer…

I’ve managed to find the ID of the main thread in wavelab in a pretty hacky way by assuming that the main thread owns most of the windows for the respective process ID.

HWND h = ::GetTopWindow(0 );
Array<int> ThreadIds,Counts;
int i,maxcount=0;
DWORD pid,dwThreadId;
while ( h )
{
	dwThreadId = ::GetWindowThreadProcessId( h,&pid);
	if ( pid == GetCurrentProcessId())
	{
		if (ThreadIds.contains((int) dwThreadId)) 
		{
			i=ThreadIds.indexOf((int) dwThreadId);
			Counts.set(i,Counts[i]+1);
		}			
		else
		{
			ThreadIds.add((int) dwThreadId);
			Counts.add(1);
		}
	}
	h = ::GetNextWindow( h , GW_HWNDNEXT);
	if (h== ::GetTopWindow(0)) break;
}
for (i=Counts.size();--i>=0;)
	{
		if (Counts[i]>maxcount)
		{
			maxcount=Counts[i];
			dwThreadId=ThreadIds[i];
		}
	}

After this, dwThreadId contains the main thread id (of course it COULD go wrong if a worker thread has more windows than the main thread but practically it seems to work).
However, even if I assign the current message thread in the messager to this ID I still get stuck in the endless loop that friscokid figured out. At this point I realized that there’s probably a problem with the loop itself when there’s no thread or no job to check, as in our case. So I changed it from

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

to the logically equivalent version

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

and got rid of all the stuff with the thread ID again. Seems to work… any objections? Do I still need to change the current message thread ID? Doesn’t seem to make a difference but I have tested it only briefly.

I think I can find a couple of objections!

The GetThreadTimes idea mentioned above sounds to me like the best way to do it rather than looking at the windows…

But more importantly, your change to the MM code is bonkers! It’d just stop the locking class working altogether!

Haha, you’re right… after a few hours of searching for a way to get the main thread ID my mind probably shut down. But ok, suppose I have the main thread ID (be it by checking the windows or the thread times), is there anything else except setting the message thread ID that I’d have to take care of (in the way you outlined a few posts earlier) ? Because that didn’t work here…

Well, the tricky bit would be ensuring the MessageManager::doPlatformSpecificInitialisation() is first called by the thread that you find. Probably the only way to do that would be with a SendAsyncProc call.