/* This file is part of Darling. Copyright (C) 2020 Lubos Dolezel Darling is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Darling is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Darling. If not, see . */ #include "AudioComponentManager.h" #include #include #include AudioComponentManager::AudioComponentManager() { discoverComponents(); } void AudioComponentManager::discoverComponents() { discoverComponents("/System/Library/Components"); discoverComponents("/Library/Components"); const char* home = ::getenv("HOME"); if (home != NULL) { std::string userPath = home; userPath += "/Library/Audio/Plug-Ins/Components"; discoverComponents(userPath.c_str()); } } void AudioComponentManager::discoverComponents(const char* dir) { CFArrayRef componentBundles; CFURLRef urlDir; CFStringRef strDir; strDir = CFStringCreateWithCStringNoCopy(nullptr, dir, kCFStringEncodingUTF8, kCFAllocatorNull); urlDir = CFURLCreateWithFileSystemPath(nullptr, strDir, kCFURLPOSIXPathStyle, true); CFRelease(strDir); componentBundles = CFBundleCreateBundlesFromDirectory(nullptr, urlDir, CFSTR("component")); CFRelease(urlDir); for (CFIndex i = 0; i < CFArrayGetCount(componentBundles); i++) { CFBundleRef bundle = (CFBundleRef) CFArrayGetValueAtIndex(componentBundles, i); analyzeComponent(bundle); CFRelease(bundle); } CFRelease(componentBundles); } static bool isString(CFStringRef str) { if (!str) return false; if (CFGetTypeID(str) != CFStringGetTypeID()) return false; return true; } static UInt32 stringToFourCC(CFStringRef str) { union { char chars[5]; // CFStringGetCString requires space for the NUL character uint32_t code; } cc; if (CFStringGetLength(str) != 4) return 0; if (!CFStringGetCString(str, cc.chars, 5, kCFStringEncodingUTF8)) return 0; #if __LITTLE_ENDIAN__ cc.code = __builtin_bswap32(cc.code); #endif return cc.code; } void AudioComponentManager::analyzeComponent(CFBundleRef bundle) { CFDictionaryRef dict = CFBundleGetInfoDictionary(bundle); CFArrayRef components = (CFArrayRef) CFDictionaryGetValue(dict, CFSTR("AudioComponents")); if (!components || CFGetTypeID(components) != CFArrayGetTypeID()) return; for (CFIndex i = 0; i < CFArrayGetCount(components); i++) { CFDictionaryRef props = (CFDictionaryRef) CFArrayGetValueAtIndex(components, i); if (!props || CFGetTypeID(props) != CFDictionaryGetTypeID()) continue; CFStringRef name, manufacturer, type, subtype, factoryFunction; CFNumberRef version; CFBooleanRef sandboxSafe; name = (CFStringRef) CFDictionaryGetValue(props, CFSTR("name")); manufacturer = (CFStringRef) CFDictionaryGetValue(props, CFSTR("manufacturer")); type = (CFStringRef) CFDictionaryGetValue(props, CFSTR("type")); subtype = (CFStringRef) CFDictionaryGetValue(props, CFSTR("subtype")); factoryFunction = (CFStringRef) CFDictionaryGetValue(props, CFSTR("factoryFunction")); if (!isString(name) || !isString(manufacturer) || !isString(type) || !isString(subtype) || !isString(factoryFunction)) continue; version = (CFNumberRef) CFDictionaryGetValue(props, CFSTR("version")); sandboxSafe = (CFBooleanRef) CFDictionaryGetValue(props, CFSTR("sandboxSafe")); if (version && CFGetTypeID(version) != CFNumberGetTypeID()) version = nullptr; if (!sandboxSafe) sandboxSafe = kCFBooleanFalse; CFURLRef bundleUrl = CFBundleCopyBundleURL(bundle); CFStringRef bundlePath = CFURLCopyFileSystemPath(bundleUrl, kCFURLPOSIXPathStyle); CFRelease(bundleUrl); AudioComponentDescription desc; desc.componentType = stringToFourCC(type); desc.componentSubType = stringToFourCC(subtype); desc.componentManufacturer = stringToFourCC(manufacturer); desc.componentFlags = 0; desc.componentFlagsMask = 0; AudioComponentFlags flags = 0; if (sandboxSafe == kCFBooleanTrue) flags |= kAudioComponentFlag_SandboxSafe; UInt32 versionValue = 0; if (version) CFNumberGetValue(version, kCFNumberSInt32Type, &versionValue); registerComponent(&desc, CFStringGetCStringPtr(name, kCFStringEncodingUTF8), versionValue, CFStringGetCStringPtr(bundlePath, kCFStringEncodingUTF8), CFStringGetCStringPtr(factoryFunction, kCFStringEncodingUTF8), flags); CFRelease(bundlePath); } } AudioComponentManager* AudioComponentManager::instance() { static AudioComponentManager inst; return &inst; } bool AudioComponentManager::isOurInstance(AudioComponentInstance instance) { uint32_t v = uint32_t(uintptr_t(instance)); return v & 0x80000000; } bool AudioComponentManager::isOurInstance(AudioComponent component) { return isOurInstance(AudioComponentInstance(component)); } /* AudioComponentInstance AudioComponentManager::audioUnitToInstance(AudioUnit unit) { uint32_t v = uint32_t(uintptr_t(unit)); if (v & 0x80000000) { v &= 0x7fffffff; return AudioComponentInstance(uintptr_t(v)); } return nullptr; } */ std::vector AudioComponentManager::findMatching(const AudioComponentDescription* cd) { std::vector rv; std::unique_lock l(m_componentsMutex); for (auto const& [componentId, d] : m_components) { if (!cd->componentManufacturer || cd->componentManufacturer == d.desc.componentManufacturer) { if (!cd->componentSubType || cd->componentSubType == d.desc.componentSubType) { if (!cd->componentType || cd->componentType == d.desc.componentType) { if (d.flags & kAudioComponentFlag_Unsearchable) { if (!cd->componentManufacturer || !cd->componentSubType || !cd->componentType) continue; } rv.push_back(d.id); } } } } return rv; } AudioComponent AudioComponentManager::registerComponent(const AudioComponentDescription* desc, const char* name, uint32_t version, AudioComponentFactoryFunction factory) { std::unique_lock l(m_componentsMutex); RegisteredComponent rc; rc.desc = *desc; rc.name = name; rc.version = version; rc.factory = factory; rc.id = (AudioComponent) uintptr_t(m_nextComponentId++); m_components.insert({ rc.id, rc }); return rc.id; } AudioComponent AudioComponentManager::registerComponent(const AudioComponentDescription* desc, const char* name, uint32_t version, const char* bundlePath, const char* entryPointName, AudioComponentFlags flags) { std::unique_lock l(m_componentsMutex); RegisteredComponent rc; rc.desc = *desc; rc.name = name; rc.version = version; rc.factory = nullptr; rc.bundlePath = bundlePath; rc.entryPointName = entryPointName; rc.id = (AudioComponent) uintptr_t(m_nextComponentId++); rc.flags = flags; m_components.insert({ rc.id, rc }); return rc.id; } CFBundleRef AudioComponentManager::bundleFromPath(const char* path) { CFStringRef cfpath = CFStringCreateWithCString(nullptr, path, kCFStringEncodingUTF8); CFURLRef url = CFURLCreateWithFileSystemPath(nullptr, cfpath, kCFURLPOSIXPathStyle, true); CFRelease(cfpath); CFBundleRef bundle = CFBundleCreate(nullptr, url); CFRelease(url); return bundle; } OSStatus AudioComponentManager::instantiate(AudioComponent c, AudioComponentInstance* out) { std::unique_lock l(m_componentsMutex); *out = nullptr; auto it = m_components.find(c); if (it == m_components.end()) return invalidComponentID; RegisteredComponent& regd = it->second; if (!regd.factory) { // Load the bundle CFBundleRef bundle = bundleFromPath(regd.bundlePath.c_str()); if (!bundle) return invalidComponentID; CFStringRef factoryName = CFStringCreateWithCString(nullptr, regd.entryPointName.c_str(), kCFStringEncodingUTF8); regd.factory = (AudioComponentFactoryFunction) CFBundleGetFunctionPointerForName(bundle, factoryName); CFRelease(factoryName); if (!regd.factory) { CFRelease(bundle); return invalidComponentID; } regd.loadedBundle = bundle; } std::unique_lock l2(m_componentInstancesMutex); ComponentInstanceData cid; *out = (AudioComponentInstance) uintptr_t(m_nextComponentInstanceId++); cid.component = c; cid.object = regd.factory(®d.desc); if (!cid.object) return invalidComponentID; m_componentInstances.insert({ *out, cid }); OSStatus status = cid.object->Open(cid.object, *out); if (status != noErr) { dispose(*out); *out = nullptr; return status; } regd.instances++; return status; } OSStatus AudioComponentManager::dispose(AudioComponentInstance instance) { std::unique_lock l2(m_componentInstancesMutex); auto it = m_componentInstances.find(instance); if (it == m_componentInstances.end()) return badComponentInstance; ComponentInstanceData& cid = it->second; OSStatus status = cid.object->Close(cid.object); std::unique_lock l(m_componentsMutex); auto itComp = m_components.find(cid.component); if (itComp != m_components.end()) { RegisteredComponent& regd = itComp->second; regd.instances--; if (regd.instances == 0 && regd.loadedBundle != nullptr) { CFRelease(regd.loadedBundle); regd.loadedBundle = nullptr; regd.factory = nullptr; } } m_componentInstances.erase(it); return status; } AudioComponentPlugInInterface* AudioComponentManager::instanceInterface(AudioComponentInstance instance) { std::unique_lock l2(m_componentInstancesMutex); auto it = m_componentInstances.find(instance); if (it == m_componentInstances.end()) return nullptr; return it->second.object; } AudioComponentManager::RegisteredComponent* AudioComponentManager::getById(AudioComponent component) { auto itComp = m_components.find(component); if (itComp != m_components.end()) return &itComp->second; return nullptr; } OSStatus AudioComponentManager::getDescription(AudioComponent c, AudioComponentDescription* out) { std::unique_lock l(m_componentsMutex); RegisteredComponent* regd = getById(c); if (!regd) return invalidComponentID; *out = regd->desc; return noErr; } OSStatus AudioComponentManager::getName(AudioComponent c, const char** name) { std::unique_lock l(m_componentsMutex); RegisteredComponent* regd = getById(c); if (!regd) return invalidComponentID; *name = regd->name.c_str(); return noErr; } OSStatus AudioComponentManager::getVersion(AudioComponent c, uint32_t* version) { std::unique_lock l(m_componentsMutex); RegisteredComponent* regd = getById(c); if (!regd) return invalidComponentID; *version = regd->version; return noErr; } AudioComponent AudioComponentManager::getComponent(AudioComponentInstance instance) { std::unique_lock l2(m_componentInstancesMutex); auto it = m_componentInstances.find(instance); if (it == m_componentInstances.end()) return nullptr; return it->second.component; }