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.

CarlaPluginCLAP.cpp 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  1. /*
  2. * Carla CLAP Plugin
  3. * Copyright (C) 2022 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of
  8. * the License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * For a full copy of the GNU General Public License see the doc/GPL.txt file.
  16. */
  17. #include "CarlaPluginInternal.hpp"
  18. #include "CarlaEngine.hpp"
  19. #include "CarlaBackendUtils.hpp"
  20. #include "CarlaClapUtils.hpp"
  21. #include "CarlaMathUtils.hpp"
  22. #include "CarlaPluginUI.hpp"
  23. #ifdef CARLA_OS_MAC
  24. # include "CarlaMacUtils.hpp"
  25. # import <Foundation/Foundation.h>
  26. #endif
  27. #include "water/files/File.h"
  28. CARLA_BACKEND_START_NAMESPACE
  29. // --------------------------------------------------------------------------------------------------------------------
  30. struct carla_clap_host : clap_host_t {
  31. carla_clap_host()
  32. {
  33. clap_version = CLAP_VERSION;
  34. host_data = this;
  35. name = "Carla";
  36. vendor = "falkTX";
  37. url = "https://kx.studio/carla";
  38. version = CARLA_VERSION_STRING;
  39. get_extension = carla_get_extension;
  40. request_restart = carla_request_restart;
  41. request_process = carla_request_process;
  42. request_callback = carla_request_callback;
  43. }
  44. static const void* carla_get_extension(const clap_host_t*, const char*) { return nullptr; }
  45. static void carla_request_restart(const clap_host_t*) {}
  46. static void carla_request_process(const clap_host_t*) {}
  47. static void carla_request_callback(const clap_host_t*) {}
  48. };
  49. // --------------------------------------------------------------------------------------------------------------------
  50. class CarlaPluginCLAP : public CarlaPlugin,
  51. private CarlaPluginUI::Callback
  52. {
  53. public:
  54. CarlaPluginCLAP(CarlaEngine* const engine, const uint id)
  55. : CarlaPlugin(engine, id),
  56. fPlugin(nullptr),
  57. fPluginDescriptor(nullptr),
  58. fPluginEntry(nullptr),
  59. fHost()
  60. {
  61. carla_debug("CarlaPluginCLAP::CarlaPluginCLAP(%p, %i)", engine, id);
  62. }
  63. ~CarlaPluginCLAP() override
  64. {
  65. carla_debug("CarlaPluginCLAP::~CarlaPluginCLAP()");
  66. // close UI
  67. // if (pData->hints & PLUGIN_HAS_CUSTOM_UI)
  68. // {
  69. // if (! fUI.isEmbed)
  70. // showCustomUI(false);
  71. //
  72. // if (fUI.isOpen)
  73. // {
  74. // fUI.isOpen = false;
  75. // dispatcher(effEditClose);
  76. // }
  77. // }
  78. pData->singleMutex.lock();
  79. pData->masterMutex.lock();
  80. if (pData->client != nullptr && pData->client->isActive())
  81. pData->client->deactivate(true);
  82. // CARLA_ASSERT(! fIsProcessing);
  83. if (pData->active)
  84. {
  85. deactivate();
  86. pData->active = false;
  87. }
  88. if (fPlugin != nullptr)
  89. {
  90. fPlugin->destroy(fPlugin);
  91. fPlugin = nullptr;
  92. }
  93. clearBuffers();
  94. if (fPluginEntry != nullptr)
  95. {
  96. fPluginEntry->deinit();
  97. fPluginEntry = nullptr;
  98. }
  99. }
  100. // -------------------------------------------------------------------
  101. // Information (base)
  102. PluginType getType() const noexcept override
  103. {
  104. return PLUGIN_CLAP;
  105. }
  106. /*
  107. PluginCategory getCategory() const noexcept override
  108. {
  109. }
  110. uint32_t getLatencyInFrames() const noexcept override
  111. {
  112. }
  113. */
  114. // -------------------------------------------------------------------
  115. // Information (count)
  116. // nothing
  117. // -------------------------------------------------------------------
  118. // Information (current data)
  119. /*
  120. std::size_t getChunkData(void** const dataPtr) noexcept override
  121. {
  122. }
  123. */
  124. // -------------------------------------------------------------------
  125. // Information (per-plugin data)
  126. /*
  127. uint getOptionsAvailable() const noexcept override
  128. {
  129. }
  130. float getParameterValue(const uint32_t parameterId) const noexcept override
  131. {
  132. }
  133. */
  134. bool getLabel(char* const strBuf) const noexcept override
  135. {
  136. CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, false);
  137. std::strncpy(strBuf, fPluginDescriptor->id, STR_MAX);
  138. return true;
  139. }
  140. bool getMaker(char* const strBuf) const noexcept override
  141. {
  142. CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, false);
  143. std::strncpy(strBuf, fPluginDescriptor->vendor, STR_MAX);
  144. return true;
  145. }
  146. bool getCopyright(char* const strBuf) const noexcept override
  147. {
  148. return getMaker(strBuf);
  149. }
  150. bool getRealName(char* const strBuf) const noexcept override
  151. {
  152. CARLA_SAFE_ASSERT_RETURN(fPluginDescriptor != nullptr, false);
  153. std::strncpy(strBuf, fPluginDescriptor->name, STR_MAX);
  154. return true;
  155. }
  156. /*
  157. bool getParameterName(const uint32_t parameterId, char* const strBuf) const noexcept override
  158. {
  159. }
  160. bool getParameterText(const uint32_t parameterId, char* const strBuf) noexcept override
  161. {
  162. }
  163. bool getParameterUnit(const uint32_t parameterId, char* const strBuf) const noexcept override
  164. {
  165. }
  166. bool getParameterGroupName(const uint32_t parameterId, char* const strBuf) const noexcept override
  167. {
  168. }
  169. */
  170. // -------------------------------------------------------------------
  171. // Set data (state)
  172. // nothing
  173. // -------------------------------------------------------------------
  174. // Set data (internal stuff)
  175. /*
  176. void setName(const char* const newName) override
  177. {
  178. }
  179. */
  180. // -------------------------------------------------------------------
  181. // Set data (plugin-specific stuff)
  182. /*
  183. void setParameterValue(const uint32_t parameterId, const float value, const bool sendGui, const bool sendOsc, const bool sendCallback) noexcept override
  184. {
  185. }
  186. void setParameterValueRT(const uint32_t parameterId, const float value, const uint32_t frameOffset, const bool sendCallbackLater) noexcept override
  187. {
  188. }
  189. void setChunkData(const void* const data, const std::size_t dataSize) override
  190. {
  191. }
  192. void setProgram(const int32_t index, const bool sendGui, const bool sendOsc, const bool sendCallback, const bool doingInit) noexcept override
  193. {
  194. }
  195. void setProgramRT(const uint32_t uindex, const bool sendCallbackLater) noexcept override
  196. {
  197. }
  198. */
  199. // -------------------------------------------------------------------
  200. // Set ui stuff
  201. /*
  202. void setCustomUITitle(const char* const title) noexcept override
  203. {
  204. }
  205. void showCustomUI(const bool yesNo) override
  206. {
  207. }
  208. void* embedCustomUI(void* const ptr) override
  209. {
  210. }
  211. */
  212. void idle() override
  213. {
  214. CarlaPlugin::idle();
  215. }
  216. void uiIdle() override
  217. {
  218. CarlaPlugin::uiIdle();
  219. }
  220. // -------------------------------------------------------------------
  221. // Plugin state
  222. void reload() override
  223. {
  224. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr,);
  225. // CARLA_SAFE_ASSERT_RETURN(fEffect != nullptr,);
  226. carla_debug("CarlaPluginCLAP::reload() - start");
  227. const EngineProcessMode processMode = pData->engine->getProccessMode();
  228. // Safely disable plugin for reload
  229. const ScopedDisabler sd(this);
  230. const clap_plugin_audio_ports_t* const audioPorts = static_cast<const clap_plugin_audio_ports_t*>(
  231. fPlugin->get_extension(fPlugin, CLAP_EXT_AUDIO_PORTS));
  232. const clap_plugin_note_ports_t* const notePorts = static_cast<const clap_plugin_note_ports_t*>(
  233. fPlugin->get_extension(fPlugin, CLAP_EXT_NOTE_PORTS));
  234. const clap_plugin_params_t* const params = static_cast<const clap_plugin_params_t*>(
  235. fPlugin->get_extension(fPlugin, CLAP_EXT_PARAMS));
  236. carla_debug("CarlaPluginCLAP::reload() - end");
  237. }
  238. void reloadPrograms(const bool doInit) override
  239. {
  240. carla_debug("CarlaPluginCLAP::reloadPrograms(%s)", bool2str(doInit));
  241. }
  242. // -------------------------------------------------------------------
  243. // Plugin processing
  244. void activate() noexcept override
  245. {
  246. }
  247. void deactivate() noexcept override
  248. {
  249. }
  250. void process(const float* const*,
  251. float** const audioOut,
  252. const float* const*,
  253. float** const,
  254. const uint32_t frames) override
  255. {
  256. // --------------------------------------------------------------------------------------------------------
  257. // Check if active
  258. if (! pData->active)
  259. {
  260. // disable any output sound
  261. for (uint32_t i=0; i < pData->audioOut.count; ++i)
  262. carla_zeroFloats(audioOut[i], frames);
  263. return;
  264. }
  265. }
  266. void bufferSizeChanged(const uint32_t newBufferSize) override
  267. {
  268. CARLA_ASSERT_INT(newBufferSize > 0, newBufferSize);
  269. carla_debug("CarlaPluginCLAP::bufferSizeChanged(%i)", newBufferSize);
  270. }
  271. void sampleRateChanged(const double newSampleRate) override
  272. {
  273. CARLA_ASSERT_INT(newSampleRate > 0.0, newSampleRate);
  274. carla_debug("CarlaPluginCLAP::sampleRateChanged(%g)", newSampleRate);
  275. }
  276. // -------------------------------------------------------------------
  277. // Plugin buffers
  278. void clearBuffers() noexcept override
  279. {
  280. carla_debug("CarlaPluginCLAP::clearBuffers() - start");
  281. CarlaPlugin::clearBuffers();
  282. carla_debug("CarlaPluginCLAP::clearBuffers() - end");
  283. }
  284. // -------------------------------------------------------------------
  285. // Post-poned UI Stuff
  286. // nothing
  287. // -------------------------------------------------------------------
  288. protected:
  289. void handlePluginUIClosed() override
  290. {
  291. // CARLA_SAFE_ASSERT_RETURN(fUI.window != nullptr,);
  292. carla_debug("CarlaPluginCLAP::handlePluginUIClosed()");
  293. showCustomUI(false);
  294. pData->engine->callback(true, true,
  295. ENGINE_CALLBACK_UI_STATE_CHANGED,
  296. pData->id,
  297. 0,
  298. 0, 0, 0.0f, nullptr);
  299. }
  300. void handlePluginUIResized(const uint width, const uint height) override
  301. {
  302. // CARLA_SAFE_ASSERT_RETURN(fUI.window != nullptr,);
  303. carla_debug("CarlaPluginCLAP::handlePluginUIResized(%u, %u)", width, height);
  304. }
  305. // -------------------------------------------------------------------
  306. public:
  307. bool init(const CarlaPluginPtr plugin,
  308. const char* const filename, const char* const name, const char* const id, const uint options)
  309. {
  310. CARLA_SAFE_ASSERT_RETURN(pData->engine != nullptr, false);
  311. // ---------------------------------------------------------------
  312. // first checks
  313. if (pData->client != nullptr)
  314. {
  315. pData->engine->setLastError("Plugin client is already registered");
  316. return false;
  317. }
  318. if (filename == nullptr || filename[0] == '\0')
  319. {
  320. pData->engine->setLastError("null filename");
  321. return false;
  322. }
  323. if (id == nullptr || id[0] == '\0')
  324. {
  325. pData->engine->setLastError("null label/id");
  326. return false;
  327. }
  328. // ---------------------------------------------------------------
  329. const clap_plugin_entry_t* entry;
  330. #ifdef CARLA_OS_MAC
  331. if (!File(filename).existsAsFile())
  332. {
  333. if (! fBundleLoader.load(filename))
  334. {
  335. pData->engine->setLastError("Failed to load CLAP bundle executable");
  336. return false;
  337. }
  338. entry = fBundleLoader.getSymbol<const clap_plugin_entry_t*>(CFSTR("clap_entry"));
  339. }
  340. else
  341. #endif
  342. {
  343. if (! pData->libOpen(filename))
  344. {
  345. pData->engine->setLastError(pData->libError(filename));
  346. return false;
  347. }
  348. entry = pData->libSymbol<const clap_plugin_entry_t*>("clap_entry");
  349. }
  350. if (entry == nullptr)
  351. {
  352. pData->engine->setLastError("Could not find the CLAP entry in the plugin library");
  353. return false;
  354. }
  355. if (entry->init == nullptr || entry->deinit == nullptr || entry->get_factory == nullptr)
  356. {
  357. pData->engine->setLastError("CLAP factory entries are null");
  358. return false;
  359. }
  360. if (!clap_version_is_compatible(entry->clap_version))
  361. {
  362. pData->engine->setLastError("Incompatible CLAP plugin");
  363. return false;
  364. }
  365. // ---------------------------------------------------------------
  366. const water::String pluginPath(water::File(filename).getParentDirectory().getFullPathName());
  367. if (!entry->init(pluginPath.toRawUTF8()))
  368. {
  369. pData->engine->setLastError("Plugin entry failed to initialize");
  370. return false;
  371. }
  372. fPluginEntry = entry;
  373. // ---------------------------------------------------------------
  374. const clap_plugin_factory_t* const factory = static_cast<const clap_plugin_factory_t*>(
  375. entry->get_factory(CLAP_PLUGIN_FACTORY_ID));
  376. if (factory == nullptr
  377. || factory->get_plugin_count == nullptr
  378. || factory->get_plugin_descriptor == nullptr
  379. || factory->create_plugin == nullptr)
  380. {
  381. pData->engine->setLastError("Plugin is missing factory methods");
  382. return false;
  383. }
  384. // ---------------------------------------------------------------
  385. if (const uint32_t count = factory->get_plugin_count(factory))
  386. {
  387. for (uint32_t i=0; i<count; ++i)
  388. {
  389. const clap_plugin_descriptor_t* const desc = factory->get_plugin_descriptor(factory, i);
  390. CARLA_SAFE_ASSERT_CONTINUE(desc != nullptr);
  391. CARLA_SAFE_ASSERT_CONTINUE(desc->id != nullptr);
  392. if (std::strcmp(desc->id, id) == 0)
  393. {
  394. fPluginDescriptor = desc;
  395. break;
  396. }
  397. }
  398. }
  399. else
  400. {
  401. pData->engine->setLastError("Plugin library contains no plugins");
  402. return false;
  403. }
  404. if (fPluginDescriptor == nullptr)
  405. {
  406. pData->engine->setLastError("Plugin library does not contain the requested plugin");
  407. return false;
  408. }
  409. // ---------------------------------------------------------------
  410. fPlugin = factory->create_plugin(factory, &fHost, fPluginDescriptor->id);
  411. if (fPlugin == nullptr)
  412. {
  413. pData->engine->setLastError("Failed to create CLAP plugin instance");
  414. return false;
  415. }
  416. if (!fPlugin->init(fPlugin))
  417. {
  418. pData->engine->setLastError("Failed to initialize CLAP plugin instance");
  419. return false;
  420. }
  421. // ---------------------------------------------------------------
  422. // get info
  423. pData->name = pData->engine->getUniquePluginName(name != nullptr && name[0] != '\0'
  424. ? name
  425. : fPluginDescriptor->name);
  426. pData->filename = carla_strdup(filename);
  427. // ---------------------------------------------------------------
  428. // register client
  429. pData->client = pData->engine->addClient(plugin);
  430. if (pData->client == nullptr || ! pData->client->isOk())
  431. {
  432. pData->engine->setLastError("Failed to register plugin client");
  433. return false;
  434. }
  435. // ---------------------------------------------------------------
  436. // set default options
  437. pData->options = 0x0;
  438. // if (fEffect->initialDelay > 0 || hasMidiOutput() || isPluginOptionEnabled(options, PLUGIN_OPTION_FIXED_BUFFERS))
  439. // pData->options |= PLUGIN_OPTION_FIXED_BUFFERS;
  440. //
  441. // if (fEffect->flags & effFlagsProgramChunks)
  442. // if (isPluginOptionEnabled(options, PLUGIN_OPTION_USE_CHUNKS))
  443. // pData->options |= PLUGIN_OPTION_USE_CHUNKS;
  444. //
  445. // if (hasMidiInput())
  446. // {
  447. // if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CONTROL_CHANGES))
  448. // pData->options |= PLUGIN_OPTION_SEND_CONTROL_CHANGES;
  449. // if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_CHANNEL_PRESSURE))
  450. // pData->options |= PLUGIN_OPTION_SEND_CHANNEL_PRESSURE;
  451. // if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH))
  452. // pData->options |= PLUGIN_OPTION_SEND_NOTE_AFTERTOUCH;
  453. // if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PITCHBEND))
  454. // pData->options |= PLUGIN_OPTION_SEND_PITCHBEND;
  455. // if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_ALL_SOUND_OFF))
  456. // pData->options |= PLUGIN_OPTION_SEND_ALL_SOUND_OFF;
  457. // if (isPluginOptionEnabled(options, PLUGIN_OPTION_SEND_PROGRAM_CHANGES))
  458. // pData->options |= PLUGIN_OPTION_SEND_PROGRAM_CHANGES;
  459. // if (isPluginOptionInverseEnabled(options, PLUGIN_OPTION_SKIP_SENDING_NOTES))
  460. // pData->options |= PLUGIN_OPTION_SKIP_SENDING_NOTES;
  461. // }
  462. //
  463. // if (fEffect->numPrograms > 1 && (pData->options & PLUGIN_OPTION_SEND_PROGRAM_CHANGES) == 0)
  464. // if (isPluginOptionEnabled(options, PLUGIN_OPTION_MAP_PROGRAM_CHANGES))
  465. // pData->options |= PLUGIN_OPTION_MAP_PROGRAM_CHANGES;
  466. return true;
  467. }
  468. private:
  469. const clap_plugin_t* fPlugin;
  470. const clap_plugin_descriptor_t* fPluginDescriptor;
  471. const clap_plugin_entry_t* fPluginEntry;
  472. const carla_clap_host fHost;
  473. #ifdef CARLA_OS_MAC
  474. BundleLoader fBundleLoader;
  475. #endif
  476. };
  477. // --------------------------------------------------------------------------------------------------------------------
  478. CarlaPluginPtr CarlaPlugin::newCLAP(const Initializer& init)
  479. {
  480. carla_debug("CarlaPlugin::newCLAP({%p, \"%s\", \"%s\", \"%s\"})",
  481. init.engine, init.filename, init.name, init.label);
  482. std::shared_ptr<CarlaPluginCLAP> plugin(new CarlaPluginCLAP(init.engine, init.id));
  483. if (! plugin->init(plugin, init.filename, init.name, init.label, init.options))
  484. return nullptr;
  485. return plugin;
  486. }
  487. // -------------------------------------------------------------------------------------------------------------------
  488. CARLA_BACKEND_END_NAMESPACE