Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

419 lines
13KB

  1. // SPDX-FileCopyrightText: 2011-2024 Filipe Coelho <falktx@falktx.com>
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include "CarlaPluginInternal.hpp"
  4. #include "CarlaEngine.hpp"
  5. #ifdef CARLA_OS_MAC
  6. # include "CarlaBackendUtils.hpp"
  7. # include "CarlaPluginUI.hpp"
  8. # include "CarlaMacUtils.hpp"
  9. # include <AudioToolbox/AudioUnit.h>
  10. # include <Foundation/Foundation.h>
  11. #endif
  12. CARLA_BACKEND_START_NAMESPACE
  13. // --------------------------------------------------------------------------------------------------------------------
  14. #ifdef CARLA_OS_MAC
  15. typedef AudioComponentPlugInInterface* (*FactoryFn)(const AudioComponentDescription*);
  16. typedef OSStatus (*InitializeFn)(void*);
  17. typedef OSStatus (*UninitializeFn)(void*);
  18. typedef OSStatus (*GetPropertyInfoFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, UInt32*, Boolean*);
  19. typedef OSStatus (*GetPropertyFn)(void*, AudioUnitPropertyID, AudioUnitScope, AudioUnitElement, void*, UInt32*);
  20. typedef OSStatus (*MIDIEventFn)(void*, UInt32, UInt32, UInt32, UInt32);
  21. static constexpr FourCharCode getFourCharCodeFromString(const char str[4])
  22. {
  23. return (str[0] << 24) + (str[1] << 16) + (str[2] << 8) + str[3];
  24. }
  25. class CarlaPluginAU : public CarlaPlugin,
  26. private CarlaPluginUI::Callback
  27. {
  28. public:
  29. CarlaPluginAU(CarlaEngine* const engine, const uint id)
  30. : CarlaPlugin(engine, id),
  31. fInterface(nullptr)
  32. {
  33. carla_stdout("CarlaPluginAU::CarlaPluginAU(%p, %i)", engine, id);
  34. }
  35. ~CarlaPluginAU() override
  36. {
  37. carla_stdout("CarlaPluginAU::~CarlaPluginAU()");
  38. // close UI
  39. showCustomUI(false);
  40. pData->singleMutex.lock();
  41. pData->masterMutex.lock();
  42. if (pData->client != nullptr && pData->client->isActive())
  43. pData->client->deactivate(true);
  44. if (pData->active)
  45. {
  46. deactivate();
  47. pData->active = false;
  48. }
  49. if (fInterface != nullptr)
  50. {
  51. fInterface->Close(fInterface);
  52. fInterface = nullptr;
  53. }
  54. // if (fLastChunk != nullptr)
  55. // {
  56. // std::free(fLastChunk);
  57. // fLastChunk = nullptr;
  58. // }
  59. clearBuffers();
  60. }
  61. // -------------------------------------------------------------------
  62. // Information (base)
  63. PluginType getType() const noexcept override
  64. {
  65. return PLUGIN_AU;
  66. }
  67. PluginCategory getCategory() const noexcept override
  68. {
  69. // TODO
  70. return PLUGIN_CATEGORY_NONE;
  71. }
  72. uint32_t getLatencyInFrames() const noexcept override
  73. {
  74. // TODO
  75. return 0;
  76. }
  77. // -------------------------------------------------------------------
  78. // Information (count)
  79. // -------------------------------------------------------------------
  80. // Information (current data)
  81. // -------------------------------------------------------------------
  82. // Information (per-plugin data)
  83. bool getLabel(char* const strBuf) const noexcept override
  84. {
  85. std::strncpy(strBuf, fLabel.buffer(), STR_MAX);
  86. return true;
  87. }
  88. bool getMaker(char* const strBuf) const noexcept override
  89. {
  90. std::strncpy(strBuf, fMaker.buffer(), STR_MAX);
  91. return true;
  92. }
  93. bool getRealName(char* const strBuf) const noexcept override
  94. {
  95. std::strncpy(strBuf, fName.buffer(), STR_MAX);
  96. return true;
  97. }
  98. // -------------------------------------------------------------------
  99. // Plugin state
  100. void reload() override
  101. {
  102. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
  103. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  104. carla_debug("CarlaPluginAU::reload() - start");
  105. // Safely disable plugin for reload
  106. const ScopedDisabler sd(this);
  107. if (pData->active)
  108. deactivate();
  109. clearBuffers();
  110. bufferSizeChanged(pData->engine->getBufferSize());
  111. reloadPrograms(true);
  112. if (pData->active)
  113. activate();
  114. carla_debug("CarlaPluginAU::reload() - end");
  115. }
  116. // -------------------------------------------------------------------
  117. // Plugin processing
  118. void activate() noexcept override
  119. {
  120. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  121. // TODO
  122. }
  123. void deactivate() noexcept override
  124. {
  125. CARLA_SAFE_ASSERT_RETURN(fInterface != nullptr,);
  126. // TODO
  127. }
  128. void process(const float* const* const audioIn,
  129. float** const audioOut,
  130. const float* const* const cvIn,
  131. float** const,
  132. const uint32_t frames) override
  133. {
  134. // TODO
  135. }
  136. // -------------------------------------------------------------------
  137. protected:
  138. void handlePluginUIClosed() override
  139. {
  140. carla_stdout("CarlaPluginCLAP::handlePluginUIClosed()");
  141. // TODO
  142. }
  143. void handlePluginUIResized(const uint width, const uint height) override
  144. {
  145. // TODO
  146. }
  147. // -------------------------------------------------------------------
  148. public:
  149. bool init(const CarlaPluginPtr plugin,
  150. const char* const filename,
  151. const char* const label,
  152. const char* const name,
  153. const uint options)
  154. {
  155. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
  156. // ---------------------------------------------------------------
  157. // first checks
  158. if (pData->client != nullptr)
  159. {
  160. pData->engine->setLastError("Plugin client is already registered");
  161. return false;
  162. }
  163. if (filename == nullptr || filename[0] == '\0')
  164. {
  165. pData->engine->setLastError("null filename");
  166. return false;
  167. }
  168. if (label == nullptr)
  169. {
  170. pData->engine->setLastError("null label");
  171. return false;
  172. }
  173. // ---------------------------------------------------------------
  174. // load bundle information
  175. if (! fBundleLoader.load(filename))
  176. {
  177. pData->engine->setLastError("Failed to load AU bundle executable");
  178. return false;
  179. }
  180. const CFTypeRef componentsRef = CFBundleGetValueForInfoDictionaryKey(fBundleLoader.getRef(), CFSTR("AudioComponents"));
  181. if (componentsRef == nullptr || CFGetTypeID(componentsRef) != CFArrayGetTypeID())
  182. {
  183. pData->engine->setLastError("Not an AU component");
  184. return false;
  185. }
  186. // ---------------------------------------------------------------
  187. // find binary matching requested label
  188. CFStringRef componentName;
  189. AudioComponentDescription desc = {};
  190. FactoryFn factoryFn;
  191. const CFArrayRef components = static_cast<CFArrayRef>(componentsRef);
  192. for (uint32_t c = 0, count = CFArrayGetCount(components); c < count; ++c)
  193. {
  194. // reset
  195. desc.componentType = 0;
  196. const CFTypeRef componentRef = CFArrayGetValueAtIndex(components, c);
  197. CARLA_SAFE_ASSERT_CONTINUE(componentRef != nullptr);
  198. CARLA_SAFE_ASSERT_CONTINUE(CFGetTypeID(componentRef) == CFDictionaryGetTypeID());
  199. const CFDictionaryRef component = static_cast<CFDictionaryRef>(componentRef);
  200. componentName = nullptr;
  201. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("name"), (const void **)&componentName));
  202. CFStringRef componentFactoryFunction = nullptr;
  203. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("factoryFunction"), (const void **)&componentFactoryFunction));
  204. CFStringRef componentType = nullptr;
  205. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("type"), (const void **)&componentType));
  206. CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentType) == 4);
  207. CFStringRef componentSubType = nullptr;
  208. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("subtype"), (const void **)&componentSubType));
  209. CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentSubType) == 4);
  210. CFStringRef componentManufacturer = nullptr;
  211. CARLA_SAFE_ASSERT_CONTINUE(CFDictionaryGetValueIfPresent(component, CFSTR("manufacturer"), (const void **)&componentManufacturer));
  212. CARLA_SAFE_ASSERT_CONTINUE(CFStringGetLength(componentManufacturer) == 4);
  213. factoryFn = fBundleLoader.getSymbol<FactoryFn>(componentFactoryFunction);
  214. CARLA_SAFE_ASSERT_CONTINUE(factoryFn != nullptr);
  215. char clabel[15] = {};
  216. CFStringGetCString(componentType, clabel, 5, kCFStringEncodingASCII);
  217. CFStringGetCString(componentSubType, clabel + 5, 5, kCFStringEncodingASCII);
  218. CFStringGetCString(componentManufacturer, clabel + 10, 5, kCFStringEncodingASCII);
  219. desc.componentType = getFourCharCodeFromString(clabel);
  220. desc.componentSubType = getFourCharCodeFromString(clabel + 5);
  221. desc.componentManufacturer = getFourCharCodeFromString(clabel + 10);
  222. CARLA_SAFE_ASSERT_CONTINUE(desc.componentType != 0);
  223. CARLA_SAFE_ASSERT_CONTINUE(desc.componentSubType != 0);
  224. CARLA_SAFE_ASSERT_CONTINUE(desc.componentManufacturer != 0);
  225. clabel[4] = clabel[9] = ',';
  226. if (label[0] == '\0' || std::strcmp(label, clabel) == 0)
  227. break;
  228. }
  229. if (desc.componentType == 0)
  230. {
  231. pData->engine->setLastError("Failed to find request plugin in Component bundle");
  232. return false;
  233. }
  234. // ---------------------------------------------------------------
  235. // load binary
  236. fInterface = factoryFn(&desc);
  237. if (fInterface == nullptr)
  238. {
  239. pData->engine->setLastError("Component failed to create new interface");
  240. return false;
  241. }
  242. const InitializeFn auInitialize = (InitializeFn)fInterface->Lookup(kAudioUnitInitializeSelect);
  243. const UninitializeFn auUninitialize = (UninitializeFn)fInterface->Lookup(kAudioUnitUninitializeSelect);
  244. const GetPropertyInfoFn auGetPropertyInfo = (GetPropertyInfoFn)fInterface->Lookup(kAudioUnitGetPropertyInfoSelect);
  245. const GetPropertyFn auGetProperty = (GetPropertyFn)fInterface->Lookup(kAudioUnitGetPropertySelect);
  246. const MIDIEventFn auMIDIEvent = (MIDIEventFn)fInterface->Lookup(kMusicDeviceMIDIEventSelect);
  247. if (auInitialize == nullptr ||
  248. auUninitialize == nullptr ||
  249. auGetPropertyInfo == nullptr ||
  250. auGetProperty == nullptr)
  251. {
  252. pData->engine->setLastError("Component does not provide all necessary functions");
  253. fInterface = nullptr;
  254. return false;
  255. }
  256. if (fInterface->Open(fInterface, (AudioUnit)(void*)0x1) != noErr)
  257. {
  258. pData->engine->setLastError("Component failed to open");
  259. fInterface->Close(fInterface);
  260. fInterface = nullptr;
  261. return false;
  262. }
  263. // ---------------------------------------------------------------
  264. // get info
  265. const CFIndex componentNameLen = CFStringGetLength(componentName);
  266. char* const nameBuffer = new char[componentNameLen + 1];
  267. if (CFStringGetCString(componentName, nameBuffer, componentNameLen + 1, kCFStringEncodingUTF8))
  268. {
  269. if (char* const sep = std::strstr(nameBuffer, ": "))
  270. {
  271. sep[0] = sep[1] = '\0';
  272. fName = sep + 2;
  273. fMaker = nameBuffer;
  274. }
  275. else
  276. {
  277. fName = nameBuffer;
  278. fMaker = nameBuffer + componentNameLen;
  279. }
  280. }
  281. fLabel = label;
  282. pData->name = pData->engine->getUniquePluginName(name != nullptr && name[0] != '\0' ? name : fName.buffer());
  283. pData->filename = carla_strdup(filename);
  284. delete[] nameBuffer;
  285. // ---------------------------------------------------------------
  286. // register client
  287. pData->client = pData->engine->addClient(plugin);
  288. if (pData->client == nullptr || ! pData->client->isOk())
  289. {
  290. pData->engine->setLastError("Failed to register plugin client");
  291. return false;
  292. }
  293. // ---------------------------------------------------------------
  294. // set default options
  295. pData->options = PLUGIN_OPTION_FIXED_BUFFERS;
  296. return true;
  297. }
  298. private:
  299. BundleLoader fBundleLoader;
  300. AudioComponentPlugInInterface* fInterface;
  301. CarlaString fName;
  302. CarlaString fLabel;
  303. CarlaString fMaker;
  304. };
  305. #endif
  306. // -------------------------------------------------------------------------------------------------------------------
  307. CarlaPluginPtr CarlaPlugin::newAU(const Initializer& init)
  308. {
  309. carla_stdout("CarlaPlugin::newAU({%p, \"%s\", \"%s\", \"%s\", " P_INT64 "})",
  310. init.engine, init.filename, init.label, init.name, init.uniqueId);
  311. #ifdef CARLA_OS_MAC
  312. std::shared_ptr<CarlaPluginAU> plugin(new CarlaPluginAU(init.engine, init.id));
  313. if (! plugin->init(plugin, init.filename, init.label, init.name, init.options))
  314. return nullptr;
  315. return plugin;
  316. #else
  317. init.engine->setLastError("AU support not available");
  318. return nullptr;
  319. #endif
  320. }
  321. // -------------------------------------------------------------------------------------------------------------------
  322. CARLA_BACKEND_END_NAMESPACE