This is following a chat with @reuk on the discord channel:
With our product, we’d like to distribute multiple AUv3 plugins with a single app purchase.
This is supported by Apple, and commercial plugins already do that. For example:
Reports three different .appex files: A synth, a MIDI FX an an audio FX.
For this to work, the bundle ID that Apple requires has to start with the standalone app id, and then end with a single extension, that needs to be unique.
ATM, this is isn’t possible with JUCE/CMake, as JUCE hard codes the extension. It would be great to add another property to juce_add_plugin that will allow customizing the extension.
I did it with my own (very trivial) fork of JUCE where I added it to juceutils.cmake, as well as JUCEAide.
Is it possible to add that feature to the main branch? I’m happy to send a PR as well if you feel this would make things easier, but the changes are really trivial.
Great work - thanks for taking the time to inform those of us who are also working on AUV3 builds. I’ll be very interested in any other gotcha’s you run into - I am preparing something similar for our (Austrian Audio) free plugins, too …
For an AUv3 plugin, it looks like the BUNDLE_ID set in the CMake file is only used to set the CFBundleIdentifier key in the appex bundle’s plist. Given that keys in the PLIST_TO_MERGE take precedence over keys specified in other ways, I believe that it’s possible to set a different ID by adding something like this to the juce_add_plugin call:
Note that this will set the same ID on the AUv3 and its standalone, so the generated AUv3 will need to be copied into a different Standalone with a compatible ID in order for the appex to be discovered and loaded.
Thanks @reuk, but No. We need a single standalone with multiple AUv3 components similar to Mela 3.
Changing the CMake was only half the process indeed: I needed to tweak JUCEAide to also write the plist file correctly for each AUv3 component. Will a full PR help you see the full changes required?
Are you trying to do this with a single juce_add_plugin call?
I’d imagined that you’d call juce_add_plugin once for each AUv3, and optionally once more for the Standalone, and then copy the AUv3s all into the same Standalone as a post-build step.
Is the problem that you are building each of the inner plugins in other formats too, and that you need the bundle ID for the AUv3 version to differ from the bundleID for the VST3 version (for example)?
No, I’m totally fine with multiple calls! (see below)
Yes, but Apple has a strict requirement for the Bundle ID. It has to start with the one from the standalone, and then only differ in the extension with no additional dots, as seen in the Mela IDs, otherwise this will fail.
Our AUv3 deployment is not related to the VST3, AAX ones, etc.
So after forking JUCE CMake + JUCEAide, I’m able to do:
#Instrument plugin: has the standalone:
juce_add_plugin(BeatScholar-MAS
${BeatScholarCommonDefs}
${MasCommonDef}
FORMATS Standalone AUv3
PLUGIN_CODE BTSM
PRODUCT_NAME "Beat Scholar-MAS")
#MIDI FX plugin: no standalone, has a custom extension:
juce_add_plugin(BeatScholar-MAS-MIDI
${BeatScholarMidiDefs}
${MasCommonDef}
FORMATS AUv3
PLUGIN_CODE BTam
BUNDLE_ID com.Modalics.BeatScholar-MAS
AUv3_BUNDLE_EXT MIDI
PRODUCT_NAME "Beat Scholar MIDI-MAS")
#add the midi component as a dependency of the main plugin
add_dependencies(BeatScholar-MAS_Standalone
BeatScholar-MAS_AUv3
BeatScholar-MAS-MIDI_AUv3)
set_target_properties(BeatScholar-MAS-MIDI PROPERTIES
XCODE_ATTRIBUTE_SKIP_INSTALL "YES")
set_target_properties(BeatScholar-MAS-MIDI_AUv3 PROPERTIES
XCODE_ATTRIBUTE_SKIP_INSTALL "YES")
copy_full_dir(BeatScholar-MAS_Standalone
"$<TARGET_BUNDLE_DIR:BeatScholar-MAS-MIDI_AUv3>"
"$<TARGET_BUNDLE_CONTENT_DIR:BeatScholar-MAS_Standalone>/PlugIns")
#(copy_full_dir is similar to juce_copy_dir but also follows symlinks).
After doing the above, I see a single standalone with two .appex files, one is:
com.modalics.beatscholar.auv3
And the other is:
com.modalics.beatscholar.midi
Both can be archived just fine and pass the app store validation.
Unless I’m missing something, I think this can be achieved without forking JUCE. I can successfully archive the AudioPluginExample with two inner AUv3s after building it like this:
That looks good! I was able to remove the fork.
It’s possible you might want to document it somewhere, as it isn’t very clear one will be able to do that.
It would be great to have more cmake template in the examples/cmake folder. It would help a lot of people to start with it and show trickier use case like app with resource and other stuff like this Auv3 stuff.
@reuk , while I think it works for now, looks like xcode also throws a warning during archiving:
/Users/eyalamir/Code/Modalics/build/Modalics.xcodeproj
User-supplied CFBundleIdentifier value 'com.Modalics.BeatScholar-MAS.MIDIFX'
in the Info.plist must be the same
as the PRODUCT_BUNDLE_IDENTIFIER build setting value
'com.Modalics.BeatScholar-MAS-MIDI.BeatScholar-MAS-MIDIAUv3'.
Edit: Never mind, fixed with an explicit bundle ID set:
I’m following this with great interest as I’m also bundling AUV3 at the moment – so if you get the chance to write those details up a little more formally, I’d be happy to provide feedback.
Are you linking juce_audio_utils, and if not, does that help?
From the first couple of errors you’re seeing, it looks like CoreAudioKit isn’t being linked. That might be an oversight on JUCE’s part, although I’d be surprised we haven’t noticed it before now!
The final error is saying that createPluginFilter() isn’t defined anywhere, but that’s up to you as a plugin author. You should check that you’ve defined a function like the following somewhere (normally at the end of your PluginProcessor.cpp).
juce::AudioProcessor* JUCE_CALLTYPE createPluginFilter()
{
return new MyAudioProcessor();
}
I have been looking how to get this to upload to the app store for most of the day. I had a local version working using the example given by reuk. I had previously replaced the copy_full_dir with _juce_copy_dir but this resulted in invalid bundles and unhelpful emails from app store connect stating
" ITMS-90018: This bundle is invalid - The file extension must be .zip."