#include "rack_au.h"
#include <AudioToolbox/AudioToolbox.h>
#include <CoreFoundation/CoreFoundation.h>
#include <new>
#include <cstring>
struct RackAUScanner {
};
static bool CFStringToCString(CFStringRef cfstr, char* buffer, size_t buffer_size) {
if (!cfstr || !buffer || buffer_size == 0) {
return false;
}
return CFStringGetCString(cfstr, buffer, buffer_size, kCFStringEncodingUTF8);
}
static RackAUPluginType AudioUnitTypeToPluginType(OSType type) {
switch (type) {
case kAudioUnitType_Effect:
case kAudioUnitType_MusicEffect:
return RACK_AU_TYPE_EFFECT;
case kAudioUnitType_MusicDevice:
return RACK_AU_TYPE_INSTRUMENT;
case kAudioUnitType_Mixer:
return RACK_AU_TYPE_MIXER;
case kAudioUnitType_FormatConverter:
return RACK_AU_TYPE_FORMAT_CONVERTER;
default:
return RACK_AU_TYPE_OTHER;
}
}
static void CreateUniqueID(const AudioComponentDescription& desc, char* buffer, size_t buffer_size) {
if (buffer_size < 27) {
if (buffer_size > 0) {
buffer[0] = '\0';
}
return;
}
snprintf(buffer, buffer_size, "%08X-%08X-%08X",
(unsigned int)desc.componentType,
(unsigned int)desc.componentSubType,
(unsigned int)desc.componentManufacturer);
}
RackAUScanner* rack_au_scanner_new(void) {
return new(std::nothrow) RackAUScanner();
}
void rack_au_scanner_free(RackAUScanner* scanner) {
delete scanner;
}
int rack_au_scanner_scan(RackAUScanner* scanner, RackAUPluginInfo* plugins, size_t max_plugins) {
if (!scanner) {
return RACK_AU_ERROR_INVALID_PARAM;
}
bool count_only = (plugins == nullptr);
size_t count = 0;
AudioComponentDescription desc = {0};
desc.componentType = 0; desc.componentSubType = 0;
desc.componentManufacturer = 0;
AudioComponent comp = nullptr;
while ((comp = AudioComponentFindNext(comp, &desc)) != nullptr) {
AudioComponentDescription foundDesc;
OSStatus status = AudioComponentGetDescription(comp, &foundDesc);
if (status != noErr) {
continue;
}
CFStringRef name = nullptr;
status = AudioComponentCopyName(comp, &name);
if (status != noErr || !name) {
continue;
}
if (count_only) {
CFRelease(name);
count++;
continue;
}
if (count >= max_plugins) {
CFRelease(name);
count++;
continue;
}
RackAUPluginInfo& info = plugins[count];
info.name[0] = '\0';
if (!CFStringToCString(name, info.name, sizeof(info.name))) {
snprintf(info.name, sizeof(info.name), "<unknown>");
}
CFRelease(name);
OSType mfg = foundDesc.componentManufacturer;
if (mfg == kAudioUnitManufacturer_Apple) {
snprintf(info.manufacturer, sizeof(info.manufacturer), "Apple");
} else {
char mfgStr[5] = {0};
unsigned char bytes[4] = {
static_cast<unsigned char>((mfg >> 24) & 0xFF),
static_cast<unsigned char>((mfg >> 16) & 0xFF),
static_cast<unsigned char>((mfg >> 8) & 0xFF),
static_cast<unsigned char>(mfg & 0xFF)
};
for (int i = 0; i < 4; ++i) {
mfgStr[i] = (bytes[i] >= 0x20 && bytes[i] <= 0x7E) ? bytes[i] : '?';
}
snprintf(info.manufacturer, sizeof(info.manufacturer), "%s", mfgStr);
}
snprintf(info.path, sizeof(info.path), "<system>");
CreateUniqueID(foundDesc, info.unique_id, sizeof(info.unique_id));
UInt32 version = 0;
if (AudioComponentGetVersion(comp, &version) == noErr) {
info.version = version;
} else {
info.version = 0;
}
info.plugin_type = AudioUnitTypeToPluginType(foundDesc.componentType);
count++;
}
return static_cast<int>(count);
}