Using JUCE to wrap older VST 2.4 plugins


#1

Hi

I’m currently using symbiosis-vst-au to wrap a VST 2.4 plugin to AU. The wrapper loads the VST into a plugin that identifies as the wrapped client plugin, opens up a client window and embeds the wrapped VST’s UI into it, and of course forwards all audio/midi/parameter data.

I am considering a more generic wrapping approach using JUCE. This would seem to have a number of benefits such as support for other formats as they are introduced and change (VST3, AUv3, AUv2 without having to use Component Manager, etc). It seems fairly easy to keep VST 2.4/VSTGUI plugins compiling under Windows and OS X on the modern compilers / SDKs, but rearchitecting them to keep up with changing plugin APIs is perhaps less simple. I’m a little anxious about upcoming deprecation of AUs using Component Manager (seems to be a warning at the moment but could become worse soon I am guessing) and maybe even VST 2.4 plugins in Steinberg products, so want to kick something off to safeguard asap.

Whilst I wouldn’t advocate or even want to do this for new projects, for plugins that would take too long to re-write into JUCE natively to really justify the activity, or for plugins that are no longer very well maintained, this would seem to be a great way to keep them alive.

What would be the best way to approach this in terms of JUCE APIs, and is JUCE a suitable mechanism for doing it? Instinctively I would expect embedding the wrapped UI into the host provided window to be one of the most challenging aspects, and wonder if this would be needing changes to JUCE to support this sort of thing. Since this is a task that new projects probably mean I don’t have time to undertake myself, I would be interested if anybody thinks they could rattle it out fairly quickly as a funded project (feel free to PM me to discuss that kind of thing).

Thanks
Matt


#2

I created such a wrapper, and it’s easy using juce. You can just embed the plugin editor inside your own, and it will work. There’s some caveats though. If you use runtime-resolving, most hosts will not ever update parameter tables, names etc. so you won’t have that available.

I went for a dual approach with being able to emulate something run-time, and save ‘emulated’ plugins as ‘new plugins’. The program will then defer all calls statically to the hosted plugin instead, making everything work - and the emulated plugin will show up in all hosts.
It requires editing of .plist and .rsrc files, though.

If you’re interested I can publish the project, however it’s kinda old and still a WIP.


#3

Pretty great news :slight_smile: I’m happy that this would be a statically compiled activity to ensure parameters can be advertised to the host properly. If you’re willing to share this work it would be fantastic. Would you be interested in contuining development?


#4

Yea sure, it’s open source anyway, but it might take a couple of days before I get it uploaded in a repository. I just never got around finishing it (would probably require a fair bit of testing to get it working properly), but iirc it worked cross-emulating vst, vst3 and au on OS X.


#5

It would be interesting to see if it also fires up well in AAX too, and maybe even AUv3!

What bits need extra work other than some thorough testing and work to get it into the latest JUCE?

I have access to a wide range of hosts so can certainly help out there.


#6

Haven’t tested AAX.
I must admit I haven’t checked out what happened with the never versions of juce and breaking the polyphonic builds… Could possibly be a gamestopper (or at least make things more difficult).


#7

Have you had the chance to take a look at the the wrapper code to see if we can get it up and running in newer Juce in the last few days?


#8

Sorry. I’ll throw it up tomorrow.

e: here it is, shittier than I remembered, but perhaps you can use something from it:

It depends on the cpl library as well:


#9

have a lot of trouble compiling this on OSX 10.11/Xcode 7.3 - similar errors to what I reported about CPL in signalizer


#10

What, you dont like my juce v. 3.x modules? I thought I fixed up the stdatomic - and the thread_local just compiled on both 10.8 xcode 5 targeting 10.7 as well as 10.11 newest xcode targeting 10.11.

On the other hand, just tried Signalizer on the new platform, it was a nightmare. Could not get past the rez errors


#11

Thanks for posting :slight_smile: I’ve not started to digest yet though see it is on a GPLv3 license. Could it be made available under something more permissive to enable use in commercial products?


#12

Well I guess that depends on the specific parts you want to use. Most of the plugin itself is boilerplate code taken from JUCE itself.


#13

Hi

I’m also struggling to compile CPL, sounds like a similar problem. I’m on XCode 7.2, SDK 10.11 and targeting 10.11. I had the thread local issue on SDK 10.7, but that’s fine on 10.11, so it’s just he stdatomic stuff giving me problems. Any hints?

Thanks
Matt

Errors:
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:182:35: No type named ‘__c11_atomic_signal_fence’ in namespace ‘std’
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:182:60: Definition or redeclaration of ‘memory_order_acquire’ not allowed inside a function
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:183:35: No type named ‘__c11_atomic_signal_fence’ in namespace ‘std’
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:183:60: Definition or redeclaration of ‘memory_order_release’ not allowed inside a function
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:184:35: No type named ‘__c11_atomic_signal_fence’ in namespace ‘std’
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:184:60: Definition or redeclaration of ‘memory_order_acq_rel’ not allowed inside a function
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:185:35: No type named ‘__c11_atomic_signal_fence’ in namespace ‘std’
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:185:60: Definition or redeclaration of ‘memory_order_seq_cst’ not allowed inside a function
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:194:35: No type named ‘__c11_atomic_thread_fence’ in namespace ‘std’
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:194:60: Definition or redeclaration of ‘memory_order_acquire’ not allowed inside a function
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:195:35: No type named ‘__c11_atomic_thread_fence’ in namespace ‘std’
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:195:60: Definition or redeclaration of ‘memory_order_release’ not allowed inside a function
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:196:35: No type named ‘__c11_atomic_thread_fence’ in namespace ‘std’
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:196:60: Definition or redeclaration of ‘memory_order_acq_rel’ not allowed inside a function
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:197:35: No type named ‘__c11_atomic_thread_fence’ in namespace ‘std’
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:197:60: Definition or redeclaration of ‘memory_order_seq_cst’ not allowed inside a function
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:290:42: Reference to ‘memory_order_acquire’ is ambiguous
/Volumes/Data/Development/juce/SDKs/cpl/lib/readerwriterqueue/atomicops.h:295:42: Reference to ‘memory_order_release’ is ambiguous


#14

Regarding licensing, what I am concerned about is if there is going to be a GPL violation at the point of use when Chameleon loads a non-GPL plugin.

"Linking a GPL covered work statically or dynamically with other modules is making a combined work based on the GPL covered work. Thus, the terms and conditions of the GNU General Public License cover the whole combination."
http://www.gnu.org/licenses/gpl-faq.en.html#GPLStaticVsDynamic

Maybe this doesn’t quite apply because this doesn’t strictly happen at the linker stage, but one could see the GPLed Chameleon plus non-GPL target plugin as a combined work and hence covered.

JUCE gets around all of this with its commercial license, and I am happy to pay for that, but it’s the agreement between Chameleon’s author’s use of the GPL and the target plugin that becomes the challenge at that stage because Chameleon’s license is standard GPL (without ROLI’s commercial exception clause). As you derive from JUCE, I don’t see how you can offer a license any more permissive than you have done though (e.g. you can’t go and extend a GPL library without it also being GPL).

I see on your CPL page you are open to some discussion regarding appropriate licenses so I’d be interested to know if you have a solution to this one, otherwise I don’t think Chameleon could actually be used with closed source plugins without that commercial use caveat that JUCE uses also applying. So maybe this is something that needs adding to the Chameleon boilerplate, not that I would know the legal wording, but something like this might be appropriate: “This library inherits source code and all appropriate software license terms from JUCE. In the absence of a closed source license from ROLI with the users of this library this software is distributed under the terms of the GPL v2 or Affero GPL v3. Licensees of JUCE under closed source library terms should consider this software to be provided under the same terms they have agreed with ROLI.”


#15

Can you post the contents of the atomic file included at atomicops.h:174? I don’t know what to say, really, clang’s headers just seem to be completely broken with regards to C++11 compability, unless there’s something I’m missing. The standard requires the file atomic to define both memory_order, atomic_signal_fence and atomic_thread_fence in the namespace std.

As for the licensing, that’s actually an angle I haven’t considered yet. However, if this were to be true, no open-source host would be able to load plugins, which is obviously not true. Something like Audacity, that very strictly adheres to licenses still loads any vst. Refer to this answer: http://opensource.stackexchange.com/a/2051/4517

As the loading of the plugins is optional and not in any way required, it would be the user’s responsibility anyway (I think?).

Another grey area for plugins is, that technically any open-source plugin (besides AU) is in violation of their respective licenses in combination with the GPL license from JUCE (as GPL requires all source code to be included). If these rules were enforced, there would (probably) be no open source audio plugins at all. That doesn’t seem like an interesting perspective from the FSF.

= I wouldn’t worry about it. The GPL automatically relieves you from user responsibility anyway.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.


#16

Hi

Sure I’ve attached that file. It was here:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/atomic

atomic.txt (58.6 KB) (renamed to allow me to upload it)

Regarding GPL, in the scenario where a developer wishes to distribute a wrapper binary that specifically loads a non GPL binary, why doesn’t that count as a combined work? The situation where closed source hosts don’t distribute open source plugins but provide the ability to load them is different from this as it’s not a combined work (and as you say, entirely optional, like Audacity). Providing a Chameleon that dynamically wrapped all the VSTs on a system but as an independent entity would also be different.

http://www.gnu.org/licenses/old-licenses/gpl-2.0-faq.en.html#TOCGPLInProprietarySystem

However, in many cases you can distribute the GPL-covered software alongside your proprietary system. To do this validly, you must make sure that the free and non-free programs communicate at arms length, that they are not combined in a way that would make them effectively a single program.

The difference between this and “incorporating” the GPL-covered software is partly a matter of substance and partly form. The substantive part is this: if the two programs are combined so that they become effectively two parts of one program, then you can’t treat them as two separate programs. So the GPL has to cover the whole thing.

So in the scenario I suggest where a developer provides a wrapper that goes right for the other binary and makes it look as though it’s one program, this suggests that the GPL applies to both.

Matt


#17

Hi

I pushed on with this and have it mostly working under JUCE 4.1, I can get a VST plugin wrapped and audio processing as an AU without the Carbon Component Manager working, basically what I wanted. To get there I had to:

  1. cpl/gui/CView.h:273-276
    Comment all of this out, thread local doesn’t compile for me.

  2. cpl/lib/readerwriterqueue/atomicops.h:182-185,194-197
    Comment out the non-default case statements to remove these atomic bits that won’t compile

Up to here, I was running on the JUCE that’s included in your package, and it worked with the exception that stereo plugins didn’t work as AU as it reported as {1,1} only. The Jucer file didn’t work for me, so I recreated it under 4.1 and added {1,2},{2,2} topologies. To continue to compile…

  1. Source/ChameleonEngine.cpp:265
    silenceInProducesSilenceOut is deprecated, so I removed it.

  2. Source/ChameleonEngine.cpp:359
    getSampleData now needs to use getWritePointer in processBlock

  3. Source/CLoader.cpp:37
    Including AudioUnit/AudioUnit.h so late seemed to cause problems, on forum recommendations I moved it before the other includes. However, I clearly have not fixed the real problem because on line 212 I can’t use JucePlugin_AUMainType and bodged it to ‘aufx’ (I was getting impatient and started hacking)

  4. Source/CLoader.cpp:268
    std::string root does not need to redeclare the variable name

  5. Source/MainView.h:43,54
    PluginListComponent does not seem to have a PluginComponentListener anymore, and hence no onPluginSelection. I’ve removed it and this breaks file selection of course. I am clearly missing something obvious that I can’t see how now you’re supposed to respond to clicking on a plugin list item. But since I was hacking away, what the hell. I’ve just started messing with the stdplugin.xml within the binary to fix up which VST I want to use.

  6. cpl/gui/Controls/CColourControl.cpp
    References to getARGB don’t seem to be ok anymore, getNativeARGB seems to work, but I have no idea what damage I actually have done to the code at this point to make it compile here so I won’t go into details.

…and then it basically worked, but I may well have broken cpl in many places.

There seems to be an issue unloading the wrapped VST in Logic X, and Studio One 3 (an AU wrap failed as below, a VST3 wrap worked ok). In Logic X, if the wrapped plugin has been loaded, clicked (i.e. transferring focus to it as you would editing a parameter), and is then unloaded with the UI open I get a crash. In Studio One, it only needs to be open and visible for the crash. In Reaper, no crash at all.

Logic X 10.2.2:
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x00007fff87c09b26 search_method_list(method_list_t const*, objc_selector*) + 136
1 libobjc.A.dylib 0x00007fff87c0ad40 lookUpImpOrForward + 359
2 libobjc.A.dylib 0x00007fff87c04591 objc_msgSend + 209
3 com.apple.AppKit 0x00007fff9967d108 -[NSView _removeNextPointersToMe] + 797
4 com.apple.AppKit 0x00007fff9967cdd0 -[NSView _removeFromKeyViewLoop] + 198
5 com.apple.AppKit 0x00007fff9967c52a -[NSView _finalizeWithReferenceCounting] + 752
6 com.apple.AppKit 0x00007fff9967c212 -[NSView dealloc] + 151
7 libobjc.A.dylib 0x00007fff87c0c2f4 objc_object::sidetable_release(bool) + 242
8 libobjc.A.dylib 0x00007fff87c0aac4 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 476
9 com.apple.CoreFoundation 0x00007fff873bec12 _CFAutoreleasePoolPop + 50
10 com.apple.Foundation 0x00007fff989329f5 -[NSAutoreleasePool release] + 146
11 com.apple.logic10 0x000000010dedb5d0 0x10d861000 + 6792656
12 com.apple.logic10 0x000000010de3aa39 0x10d861000 + 6134329
13 com.apple.logic10 0x000000010de3a8c5 0x10d861000 + 6133957
14 com.apple.logic10 0x000000010dab36c5 0x10d861000 + 2434757
15 com.apple.logic10 0x000000010ded1491 0x10d861000 + 6751377
16 com.apple.logic10 0x000000010decd44e 0x10d861000 + 6734926
17 com.apple.Foundation 0x00007fff989b7d6c __NSFireTimer + 95
18 com.apple.CoreFoundation 0x00007fff87408b94 CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION + 20
19 com.apple.CoreFoundation 0x00007fff87408823 __CFRunLoopDoTimer + 1075
20 com.apple.CoreFoundation 0x00007fff8740837a __CFRunLoopDoTimers + 298
21 com.apple.CoreFoundation 0x00007fff873ff871 __CFRunLoopRun + 1841
22 com.apple.CoreFoundation 0x00007fff873feed8 CFRunLoopRunSpecific + 296
23 com.apple.HIToolbox 0x00007fff880e3935 RunCurrentEventLoopInMode + 235
24 com.apple.HIToolbox 0x00007fff880e3677 ReceiveNextEventCommon + 184
25 com.apple.HIToolbox 0x00007fff880e35af _BlockUntilNextEventMatchingListInModeWithFilter + 71
26 com.apple.AppKit 0x00007fff9968aefa _DPSNextEvent + 1067
27 com.apple.AppKit 0x00007fff9968a32a -[NSApplication _nextEventMatchingEventMask:untilDate:inMode:dequeue:] + 454
28 com.apple.AppKit 0x00007fff9967ee84 -[NSApplication run] + 682
29 com.apple.AppKit 0x00007fff9964846c NSApplicationMain + 1176
30 com.apple.logic10 0x000000010de9fc5e 0x10d861000 + 6548574
31 libdyld.dylib 0x00007fff90cdb5ad start + 1

Studio One 3:
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x00007fff87c0ac64 lookUpImpOrForward + 139
1 libobjc.A.dylib 0x00007fff87c04591 objc_msgSend + 209
2 libobjc.A.dylib 0x00007fff87c0b390 _class_initialize + 706
3 libobjc.A.dylib 0x00007fff87c0ac90 lookUpImpOrForward + 183
4 libobjc.A.dylib 0x00007fff87c04591 objc_msgSend + 209
5 libobjc.A.dylib 0x00007fff87c0c2f4 objc_object::sidetable_release(bool) + 242
6 libobjc.A.dylib 0x00007fff87c0aac4 (anonymous namespace)::AutoreleasePoolPage::pop(void*) + 476
7 com.apple.CoreFoundation 0x00007fff873bec12 _CFAutoreleasePoolPop + 50
8 com.apple.Foundation 0x00007fff9892684a -[NSAutoreleasePool drain] + 153
9 com.apple.AppKit 0x00007fff9967ef57 -[NSApplication run] + 893
10 com.apple.Foundation 0x00007fff98942f4e -[NSObject(NSThreadPerformAdditions) performSelector:onThread:withObject:waitUntilDone:modes:] + 1115
11 com.apple.Foundation 0x00007fff98942a75 -[NSObject(NSThreadPerformAdditions) performSelectorOnMainThread:withObject:waitUntilDone:] + 131
12 com.presonus.cclgui 0x0000000107e13228 0x107ca0000 + 1520168
13 com.presonus.studioone2 0x00000001062d40ef 0x1062d3000 + 4335
14 com.presonus.studioone2 0x00000001063a96f2 0x1062d3000 + 878322
15 libdyld.dylib 0x00007fff90cdb5ad start + 1

In these tests I used Lexicon PCM’s LexHall, Exponential Audio’s PheonixVerb and R2 which I believe to be well tested JUCE based plugins and had the crash. I tried 2CAudio’s Aether and FabFilter’s Pro Q 2 and they did not crash it. I also checked this behaviour on the simplest VSTGUI plugin I could make (which I am happy to share if you want it) and that crashed so seems fairly common but not universal.

Is this a project you are wanting to continue with making it robust over multiple hosts and wrapped plugins, using new JUCE versions, or do I need to push ahead on my own here effectively on a fork?

– Edited following further testing of plugins.


#18

Okay, I’ve added a linking exception to the license of Chameleon…

-I have no idea why you have to alter atomicops.h (I’ve never had problems, not even on your platform), but I’ll forward this to the original author.

As for #3, this will disable ability to wrap synths. In general, this would have to be changed throughout JUCE to support dynamic decisions about whether the plugin is a synth or a fx module. Currently, you would have to compile two versions of Chameleon, with different settings in the Introjucer afaik.

As for #5, this is due to you swapping out the bundled version of juce, that has at least these changes.

cpl, apparantly, doesn’t support newer versions of JUCE so, until I get that fixed you’ll have to fork/branch. I’ll see if I can reproduce your problems… But yes, it’s most likely not bug-free (it’s hacked together).

Yes, I’ll continue with this project some day but it has lower priority than others currently, so a branch/fork is probably what you want. Perhaps the project should be redesigned on more fundamental levels, even.


#19

Good to read about a linking exception, I didn’t see that on the bitbucket or jthorborg.com yet (perhaps I missed it).

Not sure how familiar with the VSTGUI SDK you are, but worth asking… so I found if I comment out the bundle release from aeffguieditor.cpp (which is called right at the end of a VSTGUI editor’s destruction) I don’t get the crash. I’m not too familiar with the window management going on here. Does this sound like a sane thing to do because the wrapper is cleaning up after itself anyway and releasing here is not desirable, or is doing this going to leave a leaky mess behind?

void ExitMachOLibrary ()
{
//	if (VSTGUI_BUNDLEREF)
//		CFRelease (VSTGUI_BUNDLEREF);
}

Wrapping a basic JUCE VST seemed to work ok, so am guessing this is more to do with VSTGUI.

Would it be simple to remove the dependency on CPL or is there a lot in there needing it?


#20

Yeah, I pushed it just after you posted. No that definitely doesn’t seem right. I’ve worked with VSTGUI before but haven’t been down in the guts. It also doesn’t make sense to program something that only supports altered VSTGUI libs…

Are your problems related to dynamic loading (using the UI) or the ‘static’ wrapping?

The only hard dependencies on cpl is the plist editor (it’s just a wrapper around XML parsing/writing, you can even use PListBudder for this) and the rsrc editor, that allows to rewrite carbon resource forks for older audio units… Not sure if you can figure out a way to not use that one. Perhaps invoking rez separately… ?

These dependencies are only for OS X.