From 4b1f10c2b1437c922a63966ef95f5235e5f77248 Mon Sep 17 00:00:00 2001 From: falkTX Date: Fri, 23 Feb 2024 16:06:40 +0100 Subject: [PATCH] Custom AU discovery code Signed-off-by: falkTX --- source/discovery/carla-discovery.cpp | 325 ++++++++++++++++++++++++++- 1 file changed, 320 insertions(+), 5 deletions(-) diff --git a/source/discovery/carla-discovery.cpp b/source/discovery/carla-discovery.cpp index 40c3b14d0..2e6b97fd1 100644 --- a/source/discovery/carla-discovery.cpp +++ b/source/discovery/carla-discovery.cpp @@ -22,6 +22,7 @@ #ifdef CARLA_OS_MAC # include "CarlaMacUtils.cpp" +# include # ifdef __aarch64__ # include # endif @@ -183,7 +184,7 @@ static void print_lib_error(const char* const filename) } // -------------------------------------------------------------------------------------------------------------------- -// Plugin Checks +// Carla Cached API #ifndef BUILD_BRIDGE static void print_cached_plugin(const CarlaCachedPluginInfo* const pinfo) @@ -251,6 +252,9 @@ static void do_cached_check(const PluginType type) } #endif // ! BUILD_BRIDGE +// -------------------------------------------------------------------------------------------------------------------- +// LADSPA + static void do_ladspa_check(lib_t& libHandle, const char* const filename, const bool doInit) { LADSPA_Descriptor_Function descFn = lib_symbol(libHandle, "ladspa_descriptor"); @@ -487,6 +491,9 @@ static void do_ladspa_check(lib_t& libHandle, const char* const filename, const } } +// -------------------------------------------------------------------------------------------------------------------- +// DSSI + static void do_dssi_check(lib_t& libHandle, const char* const filename, const bool doInit) { DSSI_Descriptor_Function descFn = lib_symbol(libHandle, "dssi_descriptor"); @@ -792,6 +799,9 @@ static void do_dssi_check(lib_t& libHandle, const char* const filename, const bo } } +// -------------------------------------------------------------------------------------------------------------------- +// LV2 + #ifndef BUILD_BRIDGE static void do_lv2_check(const char* const bundle, const bool doInit) { @@ -881,7 +891,7 @@ static void do_lv2_check(const char* const bundle, const bool doInit) #ifndef USING_JUCE_FOR_VST2 // -------------------------------------------------------------------------------------------------------------------- -// VST stuff +// VST2 // Check if plugin is currently processing static bool gVstIsProcessing = false; @@ -1444,6 +1454,9 @@ static bool do_vst2_check(lib_t& libHandle, const char* const filename, const bo } #endif // ! USING_JUCE_FOR_VST2 +// -------------------------------------------------------------------------------------------------------------------- +// VST3 + #ifndef USING_JUCE_FOR_VST3 struct carla_v3_host_application : v3_host_application_cpp { carla_v3_host_application() @@ -2022,6 +2035,302 @@ static bool do_vst3_check(lib_t& libHandle, const char* const filename, const bo } #endif // ! USING_JUCE_FOR_VST3 +// -------------------------------------------------------------------------------------------------------------------- +// AU + +#ifdef CARLA_OS_MAC +typedef AudioComponentPlugInInterface* (*FactoryFn)(const AudioComponentDescription*); + +typedef OSStatus (*InitializeFn)(void*); +typedef OSStatus (*UninitializeFn)(void*); +typedef OSStatus (*GetPropertyInfoFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, UInt32*, Boolean*); +typedef OSStatus (*GetPropertyFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, void*, UInt32*); +typedef OSStatus (*MIDIEventFn)(void*, UInt32, UInt32, UInt32, UInt32); + +static constexpr FourCharCode getFourCharCodeFromString(const char str[4]) +{ + return (str[0] << 24) + (str[1] << 16) + (str[2] << 8) + str[3]; +} + +static bool do_au_check(const char* const filename, const bool doInit) +{ + BundleLoader bundleLoader; + + if (! bundleLoader.load(filename)) + { + #ifdef __aarch64__ + return true; + #else + DISCOVERY_OUT("error", "Failed to load AU bundle executable"); + return false; + #endif + } + + const CFTypeRef componentsRef = CFBundleGetValueForInfoDictionaryKey(bundleLoader.getRef(), CFSTR("AudioComponents")); + + if (componentsRef == nullptr || CFGetTypeID(componentsRef) != CFArrayGetTypeID()) + { + DISCOVERY_OUT("error", "Not an AU component"); + return false; + } + + const CFArrayRef components = static_cast(componentsRef); + + for (uint32_t c = 0, count = CFArrayGetCount(components); c < count; ++c) + { + const CFTypeRef componentRef = CFArrayGetValueAtIndex(components, c); + CARLA_SAFE_ASSERT_CONTINUE(componentRef != nullptr); + CARLA_SAFE_ASSERT_CONTINUE(CFGetTypeID(componentRef) == CFDictionaryGetTypeID()); + + const CFDictionaryRef component = static_cast(componentRef); + + CFStringRef componentName = nullptr; + CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("name"), (const void **)&componentName)); + + CFStringRef componentFactoryFunction = nullptr; + CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("factoryFunction"), (const void **)&componentFactoryFunction)); + + CFStringRef componentType = nullptr; + CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("type"), (const void **)&componentType)); + CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentType) == 4); + + CFStringRef componentSubType = nullptr; + CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("subtype"), (const void **)&componentSubType)); + CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentSubType) == 4); + + CFStringRef componentManufacturer = nullptr; + CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("manufacturer"), (const void **)&componentManufacturer)); + CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentManufacturer) == 4); + + const FactoryFn factoryFn = bundleLoader.getSymbol(componentFactoryFunction); + CARLA_SAFE_ASSERT_CONTINUE(factoryFn != nullptr); + + char label[15] = {}; + CFStringGetCString(componentType, label, 5, kCFStringEncodingASCII); + CFStringGetCString(componentSubType, label + 5, 5, kCFStringEncodingASCII); + CFStringGetCString(componentManufacturer, label + 10, 5, kCFStringEncodingASCII); + + const AudioComponentDescription desc = { + getFourCharCodeFromString(label), + getFourCharCodeFromString(label + 5), + getFourCharCodeFromString(label + 10), + 0, 0 + }; + + CARLA_SAFE_ASSERT_CONTINUE(desc.componentType != 0); + CARLA_SAFE_ASSERT_CONTINUE(desc.componentSubType != 0); + CARLA_SAFE_ASSERT_CONTINUE(desc.componentManufacturer != 0); + + label[4] = label[9] = ','; + + AudioComponentPlugInInterface* const interface = factoryFn(&desc); + CARLA_SAFE_ASSERT_CONTINUE(interface != nullptr); + + const InitializeFn auInitialize = (InitializeFn)interface->Lookup(kAudioUnitInitializeSelect); + const UninitializeFn auUninitialize = (UninitializeFn)interface->Lookup(kAudioUnitUninitializeSelect); + const GetPropertyInfoFn auGetPropertyInfo = (GetPropertyInfoFn)interface->Lookup(kAudioUnitGetPropertyInfoSelect); + const GetPropertyFn auGetProperty = (GetPropertyFn)interface->Lookup(kAudioUnitGetPropertySelect); + const MIDIEventFn auMIDIEvent = (MIDIEventFn)interface->Lookup(kMusicDeviceMIDIEventSelect); + + if (auInitialize == nullptr || auUninitialize == nullptr) + continue; + if (auGetPropertyInfo == nullptr || auGetProperty == nullptr) + continue; + + if (interface->Open(interface, (AudioUnit)(void*)0x1) == noErr) + { + uint hints = 0x0; + uint audioIns = 0; + uint audioOuts = 0; + uint midiIns = 0; + uint midiOuts = 0; + uint parametersIns = 0; + uint parametersOuts = 0; + PluginCategory category; + + switch (desc.componentType) + { + case kAudioUnitType_Effect: + case kAudioUnitType_MusicEffect: + category = PLUGIN_CATEGORY_NONE; + break; + case kAudioUnitType_Generator: + case kAudioUnitType_MusicDevice: + category = PLUGIN_CATEGORY_SYNTH; + break; + case kAudioUnitType_MIDIProcessor: + case kAudioUnitType_Mixer: + case kAudioUnitType_Panner: + case kAudioUnitType_SpeechSynthesizer: + category = PLUGIN_CATEGORY_UTILITY; + break; + case kAudioUnitType_FormatConverter: + case kAudioUnitType_OfflineEffect: + case kAudioUnitType_Output: + category = PLUGIN_CATEGORY_OTHER; + break; + default: + category = PLUGIN_CATEGORY_NONE; + break; + } + + UInt32 outDataSize; + Boolean outWritable = false; + + // audio port count + outDataSize = 0; + if (auGetPropertyInfo(interface, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, &outDataSize, &outWritable) == noErr && outDataSize != 0 && outDataSize % sizeof(AUChannelInfo) == 0) + { + const uint32_t numChannels = outDataSize / sizeof(AUChannelInfo); + AUChannelInfo* const channelInfo = new AUChannelInfo[numChannels]; + + if (auGetProperty(interface, kAudioUnitProperty_SupportedNumChannels, kAudioUnitScope_Global, 0, channelInfo, &outDataSize) == noErr && outDataSize == numChannels * sizeof(AUChannelInfo)) + { + AUChannelInfo* highestInfo = &channelInfo[0]; + + for (uint32_t i=1; i highestInfo->inChannels && channelInfo[i].outChannels > highestInfo->outChannels) + highestInfo = &channelInfo[i]; + } + + audioIns = highestInfo->inChannels; + audioOuts = highestInfo->outChannels; + } + } + + // parameter count + outDataSize = 0; + if (auGetPropertyInfo(interface, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, &outDataSize, &outWritable) == noErr && outDataSize != 0 && outDataSize % sizeof(AudioUnitParameterID) == 0) + { + const uint32_t numParams = outDataSize / sizeof(AudioUnitParameterID); + AudioUnitParameterID* const paramIds = new AudioUnitParameterID[numParams]; + + if (auGetProperty(interface, kAudioUnitProperty_ParameterList, kAudioUnitScope_Global, 0, paramIds, &outDataSize) == noErr && outDataSize == numParams * sizeof(AudioUnitParameterID)) + { + AudioUnitParameterInfo info; + + for (uint32_t i=0; iClose(interface); + + DISCOVERY_OUT("init", "------------"); + DISCOVERY_OUT("build", BINARY_NATIVE); + DISCOVERY_OUT("hints", hints); + DISCOVERY_OUT("category", getPluginCategoryAsString(category)); + DISCOVERY_OUT("name", name); + DISCOVERY_OUT("label", label); + DISCOVERY_OUT("maker", maker); + DISCOVERY_OUT("audio.ins", audioIns); + DISCOVERY_OUT("audio.outs", audioOuts); + DISCOVERY_OUT("midi.ins", midiIns); + DISCOVERY_OUT("midi.outs", midiOuts); + DISCOVERY_OUT("parameters.ins", parametersIns); + DISCOVERY_OUT("parameters.outs", parametersOuts); + DISCOVERY_OUT("end", "------------"); + + delete[] nameBuffer; + } + } + + return false; +} +#endif + +// -------------------------------------------------------------------------------------------------------------------- +// CLAP + struct carla_clap_host : clap_host_t { carla_clap_host() { @@ -2289,8 +2598,9 @@ static bool do_clap_check(lib_t& libHandle, const char* const filename, const bo #ifdef USING_JUCE // -------------------------------------------------------------------------------------------------------------------- -// find all available plugin audio ports +// JUCE +// find all available plugin audio ports static void findMaxTotalChannels(juce::AudioProcessor* const filter, int& maxTotalIns, int& maxTotalOuts) { filter->enableAllBuses(); @@ -2315,8 +2625,6 @@ static void findMaxTotalChannels(juce::AudioProcessor* const filter, int& maxTot } } -// -------------------------------------------------------------------------------------------------------------------- - static bool do_juce_check(const char* const filename_, const char* const stype, const bool doInit) { CARLA_SAFE_ASSERT_RETURN(stype != nullptr && stype[0] != 0, false) // FIXME @@ -2454,6 +2762,9 @@ static bool do_juce_check(const char* const filename_, const char* const stype, } #endif // USING_JUCE_FOR_VST2 +// -------------------------------------------------------------------------------------------------------------------- +// fluidsynth (dls, sf2, sfz) + #ifdef HAVE_FLUIDSYNTH static void do_fluidsynth_check(const char* const filename, const PluginType type, const bool doInit) { @@ -2551,6 +2862,7 @@ static void do_fluidsynth_check(const char* const filename, const PluginType typ #endif // HAVE_FLUIDSYNTH // -------------------------------------------------------------------------------------------------------------------- +// JSFX #ifdef HAVE_YSFX static void do_jsfx_check(const char* const filename, bool doInit) @@ -2761,6 +3073,7 @@ int main(int argc, const char* argv[]) case PLUGIN_DSSI: case PLUGIN_VST2: case PLUGIN_VST3: + case PLUGIN_AU: case PLUGIN_CLAP: removeFileFromQuarantine(filename); break; @@ -2807,6 +3120,8 @@ int main(int argc, const char* argv[]) case PLUGIN_AU: #if defined(USING_JUCE) && JUCE_PLUGINHOST_AU do_juce_check(filename, "AU", doInit); + #elif defined(CARLA_OS_MAC) + retryAsX64lugin = do_au_check(filename, doInit); #else DISCOVERY_OUT("error", "AU support not available"); #endif