Browse Source

Custom AU discovery code

Signed-off-by: falkTX <falktx@falktx.com>
pull/1658/merge
falkTX 8 months ago
parent
commit
4b1f10c2b1
Signed by: falkTX <falktx@falktx.com> GPG Key ID: CDBAA37ABC74FBA0
1 changed files with 320 additions and 5 deletions
  1. +320
    -5
      source/discovery/carla-discovery.cpp

+ 320
- 5
source/discovery/carla-discovery.cpp View File

@@ -22,6 +22,7 @@

#ifdef CARLA_OS_MAC
# include "CarlaMacUtils.cpp"
# include <AudioToolbox/AudioUnit.h>
# ifdef __aarch64__
# include <spawn.h>
# 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<LADSPA_Descriptor_Function>(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<DSSI_Descriptor_Function>(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<CFArrayRef>(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<CFDictionaryRef>(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<FactoryFn>(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<numChannels; ++i)
{
if (channelInfo[i].inChannels > 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; i<numParams; ++i)
{
carla_zeroStruct(info);

outDataSize = 0;
if (auGetPropertyInfo(interface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &outDataSize, &outWritable) != noErr)
break;
if (outDataSize != sizeof(AudioUnitParameterInfo))
break;
if (auGetProperty(interface, kAudioUnitProperty_ParameterInfo, kAudioUnitScope_Global, paramIds[i], &info, &outDataSize) != noErr)
break;

if ((info.flags & kAudioUnitParameterFlag_IsReadable) == 0)
continue;

if (info.flags & kAudioUnitParameterFlag_IsWritable)
++parametersIns;
else
++parametersOuts;
}
}

delete[] paramIds;
}

// MIDI input
if (auMIDIEvent != nullptr && auInitialize(interface) == noErr)
{
if (auMIDIEvent(interface, 0x90, 60, 64, 0) == noErr)
midiIns = 1;

auUninitialize(interface);
}

// MIDI output
outDataSize = 0;
outWritable = false;
if (auGetPropertyInfo(interface, kAudioUnitProperty_MIDIOutputCallback, kAudioUnitScope_Global, 0, &outDataSize, &outWritable) == noErr && outDataSize == sizeof(AUMIDIOutputCallbackStruct) && outWritable)
midiOuts = 1;

// hints
if (category == PLUGIN_CATEGORY_SYNTH)
hints |= PLUGIN_IS_SYNTH;

outDataSize = 0;
if (auGetPropertyInfo(interface, kAudioUnitProperty_CocoaUI, kAudioUnitScope_Global, 0, &outDataSize, &outWritable) == noErr && outDataSize == sizeof(AudioUnitCocoaViewInfo))
{
hints |= PLUGIN_HAS_CUSTOM_UI;
#ifndef BUILD_BRIDGE
hints |= PLUGIN_HAS_CUSTOM_EMBED_UI;
#endif
}

if (doInit)
{
// test valid scopes
outDataSize = 0;
if (auGetPropertyInfo(interface, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &outDataSize, &outWritable) == noErr && outDataSize == sizeof(UInt32))
{
UInt32 count = 0;
if (auGetProperty(interface, kAudioUnitProperty_ElementCount, kAudioUnitScope_Input, 0, &count, &outDataSize) == noErr && outDataSize == sizeof(UInt32) && count != 0)
{
}
}

// TODO
}

const CFIndex componentNameLen = CFStringGetLength(componentName);
char* const nameBuffer = new char[componentNameLen + 1];
const char* name;
const char* maker;

if (CFStringGetCString(componentName, nameBuffer, componentNameLen + 1, kCFStringEncodingUTF8))
{
if (char* const sep = std::strstr(nameBuffer, ": "))
{
sep[0] = sep[1] = '\0';
name = sep + 2;
maker = nameBuffer;
}
else
{
name = nameBuffer;
maker = nameBuffer + componentNameLen;
}
}
else
{
nameBuffer[0] = '\0';
name = maker = nameBuffer;
}

interface->Close(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


Loading…
Cancel
Save