|
|
@@ -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 |
|
|
|