this repo has no description
1/*
2This file is part of Darling.
3
4Copyright (C) 2020 Lubos Dolezel
5
6Darling is free software: you can redistribute it and/or modify
7it under the terms of the GNU General Public License as published by
8the Free Software Foundation, either version 3 of the License, or
9(at your option) any later version.
10
11Darling is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16You should have received a copy of the GNU General Public License
17along with Darling. If not, see <http://www.gnu.org/licenses/>.
18*/
19#include "AudioComponentManager.h"
20#include <cstdlib>
21#include <CoreFoundation/CoreFoundation.h>
22#include <CarbonCore/MacErrors.h>
23
24AudioComponentManager::AudioComponentManager()
25{
26 discoverComponents();
27}
28
29void AudioComponentManager::discoverComponents()
30{
31 discoverComponents("/System/Library/Components");
32 discoverComponents("/Library/Components");
33
34 const char* home = ::getenv("HOME");
35 if (home != NULL)
36 {
37 std::string userPath = home;
38 userPath += "/Library/Audio/Plug-Ins/Components";
39
40 discoverComponents(userPath.c_str());
41 }
42}
43
44void AudioComponentManager::discoverComponents(const char* dir)
45{
46 CFArrayRef componentBundles;
47 CFURLRef urlDir;
48 CFStringRef strDir;
49
50 strDir = CFStringCreateWithCStringNoCopy(nullptr, dir, kCFStringEncodingUTF8, kCFAllocatorNull);
51 urlDir = CFURLCreateWithFileSystemPath(nullptr, strDir, kCFURLPOSIXPathStyle, true);
52 CFRelease(strDir);
53
54 componentBundles = CFBundleCreateBundlesFromDirectory(nullptr, urlDir, CFSTR("component"));
55
56 CFRelease(urlDir);
57
58 for (CFIndex i = 0; i < CFArrayGetCount(componentBundles); i++)
59 {
60 CFBundleRef bundle = (CFBundleRef) CFArrayGetValueAtIndex(componentBundles, i);
61 analyzeComponent(bundle);
62 CFRelease(bundle);
63 }
64
65 CFRelease(componentBundles);
66}
67
68static bool isString(CFStringRef str)
69{
70 if (!str)
71 return false;
72 if (CFGetTypeID(str) != CFStringGetTypeID())
73 return false;
74 return true;
75}
76
77static UInt32 stringToFourCC(CFStringRef str)
78{
79 union {
80 char chars[5]; // CFStringGetCString requires space for the NUL character
81 uint32_t code;
82 } cc;
83
84 if (CFStringGetLength(str) != 4)
85 return 0;
86 if (!CFStringGetCString(str, cc.chars, 5, kCFStringEncodingUTF8))
87 return 0;
88
89#if __LITTLE_ENDIAN__
90 cc.code = __builtin_bswap32(cc.code);
91#endif
92
93 return cc.code;
94}
95
96void AudioComponentManager::analyzeComponent(CFBundleRef bundle)
97{
98 CFDictionaryRef dict = CFBundleGetInfoDictionary(bundle);
99 CFArrayRef components = (CFArrayRef) CFDictionaryGetValue(dict, CFSTR("AudioComponents"));
100
101 if (!components || CFGetTypeID(components) != CFArrayGetTypeID())
102 return;
103
104 for (CFIndex i = 0; i < CFArrayGetCount(components); i++)
105 {
106 CFDictionaryRef props = (CFDictionaryRef) CFArrayGetValueAtIndex(components, i);
107 if (!props || CFGetTypeID(props) != CFDictionaryGetTypeID())
108 continue;
109
110 CFStringRef name, manufacturer, type, subtype, factoryFunction;
111 CFNumberRef version;
112 CFBooleanRef sandboxSafe;
113
114 name = (CFStringRef) CFDictionaryGetValue(props, CFSTR("name"));
115 manufacturer = (CFStringRef) CFDictionaryGetValue(props, CFSTR("manufacturer"));
116 type = (CFStringRef) CFDictionaryGetValue(props, CFSTR("type"));
117 subtype = (CFStringRef) CFDictionaryGetValue(props, CFSTR("subtype"));
118 factoryFunction = (CFStringRef) CFDictionaryGetValue(props, CFSTR("factoryFunction"));
119
120 if (!isString(name) || !isString(manufacturer) || !isString(type) || !isString(subtype) || !isString(factoryFunction))
121 continue;
122
123 version = (CFNumberRef) CFDictionaryGetValue(props, CFSTR("version"));
124 sandboxSafe = (CFBooleanRef) CFDictionaryGetValue(props, CFSTR("sandboxSafe"));
125
126 if (version && CFGetTypeID(version) != CFNumberGetTypeID())
127 version = nullptr;
128
129 if (!sandboxSafe)
130 sandboxSafe = kCFBooleanFalse;
131
132 CFURLRef bundleUrl = CFBundleCopyBundleURL(bundle);
133 CFStringRef bundlePath = CFURLCopyFileSystemPath(bundleUrl, kCFURLPOSIXPathStyle);
134 CFRelease(bundleUrl);
135
136 AudioComponentDescription desc;
137 desc.componentType = stringToFourCC(type);
138 desc.componentSubType = stringToFourCC(subtype);
139 desc.componentManufacturer = stringToFourCC(manufacturer);
140 desc.componentFlags = 0;
141 desc.componentFlagsMask = 0;
142
143 AudioComponentFlags flags = 0;
144 if (sandboxSafe == kCFBooleanTrue)
145 flags |= kAudioComponentFlag_SandboxSafe;
146
147 UInt32 versionValue = 0;
148 if (version)
149 CFNumberGetValue(version, kCFNumberSInt32Type, &versionValue);
150
151 registerComponent(&desc, CFStringGetCStringPtr(name, kCFStringEncodingUTF8), versionValue,
152 CFStringGetCStringPtr(bundlePath, kCFStringEncodingUTF8), CFStringGetCStringPtr(factoryFunction, kCFStringEncodingUTF8),
153 flags);
154
155 CFRelease(bundlePath);
156 }
157}
158
159AudioComponentManager* AudioComponentManager::instance()
160{
161 static AudioComponentManager inst;
162 return &inst;
163}
164
165bool AudioComponentManager::isOurInstance(AudioComponentInstance instance)
166{
167 uint32_t v = uint32_t(uintptr_t(instance));
168 return v & 0x80000000;
169}
170
171bool AudioComponentManager::isOurInstance(AudioComponent component)
172{
173 return isOurInstance(AudioComponentInstance(component));
174}
175
176/*
177AudioComponentInstance AudioComponentManager::audioUnitToInstance(AudioUnit unit)
178{
179 uint32_t v = uint32_t(uintptr_t(unit));
180 if (v & 0x80000000)
181 {
182 v &= 0x7fffffff;
183 return AudioComponentInstance(uintptr_t(v));
184 }
185 return nullptr;
186}
187*/
188
189std::vector<AudioComponent> AudioComponentManager::findMatching(const AudioComponentDescription* cd)
190{
191 std::vector<AudioComponent> rv;
192 std::unique_lock<std::recursive_mutex> l(m_componentsMutex);
193
194 for (auto const& [componentId, d] : m_components)
195 {
196 if (!cd->componentManufacturer || cd->componentManufacturer == d.desc.componentManufacturer)
197 {
198 if (!cd->componentSubType || cd->componentSubType == d.desc.componentSubType)
199 {
200 if (!cd->componentType || cd->componentType == d.desc.componentType)
201 {
202 if (d.flags & kAudioComponentFlag_Unsearchable)
203 {
204 if (!cd->componentManufacturer || !cd->componentSubType || !cd->componentType)
205 continue;
206 }
207 rv.push_back(d.id);
208 }
209 }
210 }
211 }
212
213 return rv;
214}
215
216AudioComponent AudioComponentManager::registerComponent(const AudioComponentDescription* desc, const char* name,
217 uint32_t version, AudioComponentFactoryFunction factory)
218{
219 std::unique_lock<std::recursive_mutex> l(m_componentsMutex);
220
221 RegisteredComponent rc;
222 rc.desc = *desc;
223 rc.name = name;
224 rc.version = version;
225 rc.factory = factory;
226 rc.id = (AudioComponent) uintptr_t(m_nextComponentId++);
227
228 m_components.insert({ rc.id, rc });
229
230 return rc.id;
231}
232
233AudioComponent AudioComponentManager::registerComponent(const AudioComponentDescription* desc, const char* name,
234 uint32_t version, const char* bundlePath, const char* entryPointName, AudioComponentFlags flags)
235{
236 std::unique_lock<std::recursive_mutex> l(m_componentsMutex);
237
238 RegisteredComponent rc;
239 rc.desc = *desc;
240 rc.name = name;
241 rc.version = version;
242 rc.factory = nullptr;
243 rc.bundlePath = bundlePath;
244 rc.entryPointName = entryPointName;
245 rc.id = (AudioComponent) uintptr_t(m_nextComponentId++);
246 rc.flags = flags;
247
248 m_components.insert({ rc.id, rc });
249
250 return rc.id;
251}
252
253CFBundleRef AudioComponentManager::bundleFromPath(const char* path)
254{
255 CFStringRef cfpath = CFStringCreateWithCString(nullptr, path, kCFStringEncodingUTF8);
256
257 CFURLRef url = CFURLCreateWithFileSystemPath(nullptr, cfpath, kCFURLPOSIXPathStyle, true);
258 CFRelease(cfpath);
259
260 CFBundleRef bundle = CFBundleCreate(nullptr, url);
261 CFRelease(url);
262
263 return bundle;
264}
265
266OSStatus AudioComponentManager::instantiate(AudioComponent c, AudioComponentInstance* out)
267{
268 std::unique_lock<std::recursive_mutex> l(m_componentsMutex);
269
270 *out = nullptr;
271
272 auto it = m_components.find(c);
273 if (it == m_components.end())
274 return invalidComponentID;
275
276 RegisteredComponent& regd = it->second;
277 if (!regd.factory)
278 {
279 // Load the bundle
280 CFBundleRef bundle = bundleFromPath(regd.bundlePath.c_str());
281
282 if (!bundle)
283 return invalidComponentID;
284
285 CFStringRef factoryName = CFStringCreateWithCString(nullptr, regd.entryPointName.c_str(), kCFStringEncodingUTF8);
286 regd.factory = (AudioComponentFactoryFunction) CFBundleGetFunctionPointerForName(bundle, factoryName);
287 CFRelease(factoryName);
288
289 if (!regd.factory)
290 {
291 CFRelease(bundle);
292 return invalidComponentID;
293 }
294
295 regd.loadedBundle = bundle;
296 }
297
298 std::unique_lock<std::recursive_mutex> l2(m_componentInstancesMutex);
299
300 ComponentInstanceData cid;
301 *out = (AudioComponentInstance) uintptr_t(m_nextComponentInstanceId++);
302
303 cid.component = c;
304 cid.object = regd.factory(®d.desc);
305
306 if (!cid.object)
307 return invalidComponentID;
308
309 m_componentInstances.insert({ *out, cid });
310
311 OSStatus status = cid.object->Open(cid.object, *out);
312 if (status != noErr)
313 {
314 dispose(*out);
315 *out = nullptr;
316 return status;
317 }
318 regd.instances++;
319
320 return status;
321}
322
323OSStatus AudioComponentManager::dispose(AudioComponentInstance instance)
324{
325 std::unique_lock<std::recursive_mutex> l2(m_componentInstancesMutex);
326 auto it = m_componentInstances.find(instance);
327 if (it == m_componentInstances.end())
328 return badComponentInstance;
329
330 ComponentInstanceData& cid = it->second;
331 OSStatus status = cid.object->Close(cid.object);
332
333 std::unique_lock<std::recursive_mutex> l(m_componentsMutex);
334 auto itComp = m_components.find(cid.component);
335 if (itComp != m_components.end())
336 {
337 RegisteredComponent& regd = itComp->second;
338 regd.instances--;
339
340 if (regd.instances == 0 && regd.loadedBundle != nullptr)
341 {
342 CFRelease(regd.loadedBundle);
343 regd.loadedBundle = nullptr;
344 regd.factory = nullptr;
345 }
346 }
347
348 m_componentInstances.erase(it);
349 return status;
350}
351
352AudioComponentPlugInInterface* AudioComponentManager::instanceInterface(AudioComponentInstance instance)
353{
354 std::unique_lock<std::recursive_mutex> l2(m_componentInstancesMutex);
355 auto it = m_componentInstances.find(instance);
356 if (it == m_componentInstances.end())
357 return nullptr;
358
359 return it->second.object;
360}
361
362AudioComponentManager::RegisteredComponent* AudioComponentManager::getById(AudioComponent component)
363{
364 auto itComp = m_components.find(component);
365 if (itComp != m_components.end())
366 return &itComp->second;
367 return nullptr;
368}
369
370OSStatus AudioComponentManager::getDescription(AudioComponent c, AudioComponentDescription* out)
371{
372 std::unique_lock<std::recursive_mutex> l(m_componentsMutex);
373 RegisteredComponent* regd = getById(c);
374
375 if (!regd)
376 return invalidComponentID;
377
378 *out = regd->desc;
379 return noErr;
380}
381
382OSStatus AudioComponentManager::getName(AudioComponent c, const char** name)
383{
384 std::unique_lock<std::recursive_mutex> l(m_componentsMutex);
385 RegisteredComponent* regd = getById(c);
386
387 if (!regd)
388 return invalidComponentID;
389
390 *name = regd->name.c_str();
391 return noErr;
392}
393
394OSStatus AudioComponentManager::getVersion(AudioComponent c, uint32_t* version)
395{
396 std::unique_lock<std::recursive_mutex> l(m_componentsMutex);
397 RegisteredComponent* regd = getById(c);
398
399 if (!regd)
400 return invalidComponentID;
401
402 *version = regd->version;
403 return noErr;
404}
405
406AudioComponent AudioComponentManager::getComponent(AudioComponentInstance instance)
407{
408 std::unique_lock<std::recursive_mutex> l2(m_componentInstancesMutex);
409 auto it = m_componentInstances.find(instance);
410 if (it == m_componentInstances.end())
411 return nullptr;
412
413 return it->second.component;
414}