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.

765 lines
28KB

  1. /*
  2. * DISTRHO Ildaeil Plugin
  3. * Copyright (C) 2021-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 LICENSE file.
  16. */
  17. #include "IldaeilBasePlugin.hpp"
  18. #include "CarlaEngine.hpp"
  19. #include "water/files/File.h"
  20. #include "water/streams/MemoryOutputStream.h"
  21. #include "water/xml/XmlDocument.h"
  22. START_NAMESPACE_DISTRHO
  23. using namespace CARLA_BACKEND_NAMESPACE;
  24. // --------------------------------------------------------------------------------------------------------------------
  25. static uint32_t host_get_buffer_size(NativeHostHandle);
  26. static double host_get_sample_rate(NativeHostHandle);
  27. static bool host_is_offline(NativeHostHandle);
  28. static const NativeTimeInfo* host_get_time_info(NativeHostHandle handle);
  29. static bool host_write_midi_event(NativeHostHandle handle, const NativeMidiEvent* event);
  30. static void host_ui_parameter_changed(NativeHostHandle handle, uint32_t index, float value);
  31. static void host_ui_midi_program_changed(NativeHostHandle handle, uint8_t channel, uint32_t bank, uint32_t program);
  32. static void host_ui_custom_data_changed(NativeHostHandle handle, const char* key, const char* value);
  33. static void host_ui_closed(NativeHostHandle handle);
  34. static const char* host_ui_open_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter);
  35. static const char* host_ui_save_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter);
  36. static intptr_t host_dispatcher(NativeHostHandle handle, NativeHostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt);
  37. // --------------------------------------------------------------------------------------------------------------------
  38. Mutex IldaeilBasePlugin::sPluginInfoLoadMutex;
  39. // --------------------------------------------------------------------------------------------------------------------
  40. #ifndef CARLA_OS_WIN
  41. static water::String getHomePath()
  42. {
  43. static water::String path(water::File::getSpecialLocation(water::File::userHomeDirectory).getFullPathName());
  44. return path;
  45. }
  46. #endif
  47. static const char* getPathForLADSPA()
  48. {
  49. static water::String path;
  50. if (path.isEmpty())
  51. {
  52. #if defined(CARLA_OS_HAIKU)
  53. path = getHomePath() + "/.ladspa:/system/add-ons/media/ladspaplugins:/system/lib/ladspa";
  54. #elif defined(CARLA_OS_MAC)
  55. path = getHomePath() + "/Library/Audio/Plug-Ins/LADSPA:/Library/Audio/Plug-Ins/LADSPA";
  56. #elif defined(CARLA_OS_WASM)
  57. path = "/ladspa";
  58. #elif defined(CARLA_OS_WIN)
  59. path = water::File::getSpecialLocation(water::File::winAppData).getFullPathName() + "\\LADSPA;";
  60. path += water::File::getSpecialLocation(water::File::winProgramFiles).getFullPathName() + "\\LADSPA";
  61. #else
  62. path = getHomePath() + "/.ladspa:/usr/lib/ladspa:/usr/local/lib/ladspa";
  63. #endif
  64. }
  65. return path.toRawUTF8();
  66. }
  67. static const char* getPathForDSSI()
  68. {
  69. static water::String path;
  70. if (path.isEmpty())
  71. {
  72. #if defined(CARLA_OS_HAIKU)
  73. path = getHomePath() + "/.dssi:/system/add-ons/media/dssiplugins:/system/lib/dssi";
  74. #elif defined(CARLA_OS_MAC)
  75. path = getHomePath() + "/Library/Audio/Plug-Ins/DSSI:/Library/Audio/Plug-Ins/DSSI";
  76. #elif defined(CARLA_OS_WASM)
  77. path = "/dssi";
  78. #elif defined(CARLA_OS_WIN)
  79. path = water::File::getSpecialLocation(water::File::winAppData).getFullPathName() + "\\DSSI;";
  80. path += water::File::getSpecialLocation(water::File::winProgramFiles).getFullPathName() + "\\DSSI";
  81. #else
  82. path = getHomePath() + "/.dssi:/usr/lib/dssi:/usr/local/lib/dssi";
  83. #endif
  84. }
  85. return path.toRawUTF8();
  86. }
  87. static const char* getPathForLV2()
  88. {
  89. static water::String path;
  90. if (path.isEmpty())
  91. {
  92. #if defined(CARLA_OS_HAIKU)
  93. path = getHomePath() + "/.lv2:/system/add-ons/media/lv2plugins";
  94. #elif defined(CARLA_OS_MAC)
  95. path = getHomePath() + "/Library/Audio/Plug-Ins/LV2:/Library/Audio/Plug-Ins/LV2";
  96. #elif defined(CARLA_OS_WASM)
  97. path = "/lv2";
  98. #elif defined(CARLA_OS_WIN)
  99. path = water::File::getSpecialLocation(water::File::winAppData).getFullPathName() + "\\LV2;";
  100. path += water::File::getSpecialLocation(water::File::winCommonProgramFiles).getFullPathName() + "\\LV2";
  101. #else
  102. path = getHomePath() + "/.lv2:/usr/lib/lv2:/usr/local/lib/lv2";
  103. #endif
  104. }
  105. return path.toRawUTF8();
  106. }
  107. static const char* getPathForVST2()
  108. {
  109. static water::String path;
  110. if (path.isEmpty())
  111. {
  112. #if defined(CARLA_OS_HAIKU)
  113. path = getHomePath() + "/.vst:/system/add-ons/media/vstplugins";
  114. #elif defined(CARLA_OS_MAC)
  115. path = getHomePath() + "/Library/Audio/Plug-Ins/VST:/Library/Audio/Plug-Ins/VST";
  116. #elif defined(CARLA_OS_WASM)
  117. path = "/vst";
  118. #elif defined(CARLA_OS_WIN)
  119. path = water::File::getSpecialLocation(water::File::winProgramFiles).getFullPathName() + "\\VstPlugins;";
  120. path += water::File::getSpecialLocation(water::File::winProgramFiles).getFullPathName() + "\\Steinberg\\VstPlugins;";
  121. path += water::File::getSpecialLocation(water::File::winCommonProgramFiles).getFullPathName() + "\\VST2";
  122. #else
  123. path = getHomePath() + "/.vst:/usr/lib/vst:/usr/local/lib/vst";
  124. #endif
  125. }
  126. return path.toRawUTF8();
  127. }
  128. static const char* getPathForVST3()
  129. {
  130. static water::String path;
  131. if (path.isEmpty())
  132. {
  133. #if defined(CARLA_OS_HAIKU)
  134. path = getHomePath() + "/.vst3:/system/add-ons/media/dssiplugins";
  135. #elif defined(CARLA_OS_MAC)
  136. path = getHomePath() + "/Library/Audio/Plug-Ins/VST3:/Library/Audio/Plug-Ins/VST3";
  137. #elif defined(CARLA_OS_WASM)
  138. path = "/vst3";
  139. #elif defined(CARLA_OS_WIN)
  140. path = water::File::getSpecialLocation(water::File::winAppData).getFullPathName() + "\\VST3;";
  141. path += water::File::getSpecialLocation(water::File::winCommonProgramFiles).getFullPathName() + "\\VST3";
  142. #else
  143. path = getHomePath() + "/.vst3:/usr/lib/vst3:/usr/local/lib/vst3";
  144. #endif
  145. }
  146. return path.toRawUTF8();
  147. }
  148. static const char* getPathForCLAP()
  149. {
  150. static water::String path;
  151. if (path.isEmpty())
  152. {
  153. #if defined(CARLA_OS_HAIKU)
  154. path = getHomePath() + "/.clap:/system/add-ons/media/clapplugins";
  155. #elif defined(CARLA_OS_MAC)
  156. path = getHomePath() + "/Library/Audio/Plug-Ins/CLAP:/Library/Audio/Plug-Ins/CLAP";
  157. #elif defined(CARLA_OS_WASM)
  158. path = "/clap";
  159. #elif defined(CARLA_OS_WIN)
  160. path = water::File::getSpecialLocation(water::File::winAppData).getFullPathName() + "\\CLAP;";
  161. path += water::File::getSpecialLocation(water::File::winCommonProgramFiles).getFullPathName() + "\\CLAP";
  162. #else
  163. path = getHomePath() + "/.clap:/usr/lib/clap:/usr/local/lib/clap";
  164. #endif
  165. }
  166. return path.toRawUTF8();
  167. }
  168. static const char* getPathForJSFX()
  169. {
  170. static water::String path;
  171. if (path.isEmpty())
  172. {
  173. #if defined(CARLA_OS_MAC)
  174. path = getHomePath()
  175. + "/Library/Application Support/REAPER/Effects";
  176. if (! water::File(path).isDirectory())
  177. path = "/Applications/REAPER.app/Contents/InstallFiles/Effects";
  178. #elif defined(CARLA_OS_WASM)
  179. path = "/jsfx";
  180. #elif defined(CARLA_OS_WIN)
  181. path = water::File::getSpecialLocation(water::File::winAppData).getFullPathName() + "\\REAPER\\Effects";
  182. if (! water::File(path).isDirectory())
  183. path = water::File::getSpecialLocation(water::File::winProgramFiles).getFullPathName()
  184. + "\\REAPER\\InstallData\\Effects";
  185. #else
  186. if (const char* const configHome = std::getenv("XDG_CONFIG_HOME"))
  187. path = configHome;
  188. else
  189. path = getHomePath() + "/.config";
  190. path += "/REAPER/Effects";
  191. #endif
  192. }
  193. return path.toRawUTF8();
  194. }
  195. const char* IldaeilBasePlugin::getPluginPath(const PluginType ptype)
  196. {
  197. switch (ptype)
  198. {
  199. case PLUGIN_LADSPA:
  200. if (const char* const path = std::getenv("LADSPA_PATH"))
  201. return path;
  202. return getPathForLADSPA();
  203. case PLUGIN_DSSI:
  204. if (const char* const path = std::getenv("DSSI_PATH"))
  205. return path;
  206. return getPathForDSSI();
  207. case PLUGIN_LV2:
  208. if (const char* const path = std::getenv("LV2_PATH"))
  209. return path;
  210. return getPathForLV2();
  211. case PLUGIN_VST2:
  212. if (const char* const path = std::getenv("VST_PATH"))
  213. return path;
  214. return getPathForVST2();
  215. case PLUGIN_VST3:
  216. if (const char* const path = std::getenv("VST3_PATH"))
  217. return path;
  218. return getPathForVST3();
  219. case PLUGIN_CLAP:
  220. if (const char* const path = std::getenv("CLAP_PATH"))
  221. return path;
  222. return getPathForCLAP();
  223. case PLUGIN_JSFX:
  224. return getPathForJSFX();
  225. default:
  226. return nullptr;
  227. }
  228. }
  229. // --------------------------------------------------------------------------------------------------------------------
  230. class IldaeilPlugin : public IldaeilBasePlugin
  231. {
  232. #if DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  233. float* fDummyBuffer;
  234. float* fDummyBuffers[2];
  235. #endif
  236. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  237. static constexpr const uint kMaxMidiEventCount = 512;
  238. NativeMidiEvent* fMidiEvents;
  239. #endif
  240. mutable NativeTimeInfo fCarlaTimeInfo;
  241. mutable water::MemoryOutputStream fLastProjectState;
  242. uint32_t fLastLatencyValue;
  243. public:
  244. IldaeilPlugin()
  245. : IldaeilBasePlugin(),
  246. #if DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  247. fDummyBuffer(nullptr),
  248. #endif
  249. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  250. fMidiEvents(nullptr),
  251. #endif
  252. fLastLatencyValue(0)
  253. {
  254. fCarlaPluginDescriptor = carla_get_native_rack_plugin();
  255. DISTRHO_SAFE_ASSERT_RETURN(fCarlaPluginDescriptor != nullptr,);
  256. memset(&fCarlaTimeInfo, 0, sizeof(fCarlaTimeInfo));
  257. fCarlaHostDescriptor.handle = this;
  258. fCarlaHostDescriptor.resourceDir = carla_get_library_folder();
  259. fCarlaHostDescriptor.uiName = "Ildaeil";
  260. fCarlaHostDescriptor.uiParentId = 0;
  261. fCarlaHostDescriptor.get_buffer_size = host_get_buffer_size;
  262. fCarlaHostDescriptor.get_sample_rate = host_get_sample_rate;
  263. fCarlaHostDescriptor.is_offline = host_is_offline;
  264. fCarlaHostDescriptor.get_time_info = host_get_time_info;
  265. fCarlaHostDescriptor.write_midi_event = host_write_midi_event;
  266. fCarlaHostDescriptor.ui_parameter_changed = host_ui_parameter_changed;
  267. fCarlaHostDescriptor.ui_midi_program_changed = host_ui_midi_program_changed;
  268. fCarlaHostDescriptor.ui_custom_data_changed = host_ui_custom_data_changed;
  269. fCarlaHostDescriptor.ui_closed = host_ui_closed;
  270. fCarlaHostDescriptor.ui_open_file = host_ui_open_file;
  271. fCarlaHostDescriptor.ui_save_file = host_ui_save_file;
  272. fCarlaHostDescriptor.dispatcher = host_dispatcher;
  273. fCarlaPluginHandle = fCarlaPluginDescriptor->instantiate(&fCarlaHostDescriptor);
  274. DISTRHO_SAFE_ASSERT_RETURN(fCarlaPluginHandle != nullptr,);
  275. fCarlaHostHandle = carla_create_native_plugin_host_handle(fCarlaPluginDescriptor, fCarlaPluginHandle);
  276. DISTRHO_SAFE_ASSERT_RETURN(fCarlaHostHandle != nullptr,);
  277. const char* const bundlePath = getBundlePath();
  278. #ifdef CARLA_OS_WIN
  279. #define EXT ".exe"
  280. #else
  281. #define EXT ""
  282. #endif
  283. if (bundlePath != nullptr
  284. && water::File(bundlePath + water::String(DISTRHO_OS_SEP_STR "carla-bridge-native" EXT)).existsAsFile())
  285. {
  286. fDiscoveryTool = bundlePath;
  287. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_BINARIES, 0, bundlePath);
  288. // carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_RESOURCES, 0, "");
  289. }
  290. else
  291. {
  292. #ifdef CARLA_OS_MAC
  293. fDiscoveryTool = "/Applications/Carla.app/Contents/MacOS";
  294. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_BINARIES, 0, "/Applications/Carla.app/Contents/MacOS");
  295. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_RESOURCES, 0, "/Applications/Carla.app/Contents/MacOS/resources");
  296. #else
  297. fDiscoveryTool = "/usr/lib/carla";
  298. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_BINARIES, 0, "/usr/lib/carla");
  299. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_RESOURCES, 0, "/usr/share/carla/resources");
  300. #endif
  301. }
  302. carla_stdout("Using binary path: %s", fDiscoveryTool.buffer());
  303. fDiscoveryTool += DISTRHO_OS_SEP_STR "carla-discovery-native" EXT;
  304. #undef EXT
  305. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LADSPA, getPluginPath(PLUGIN_LADSPA));
  306. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PLUGIN_PATH, PLUGIN_DSSI, getPluginPath(PLUGIN_DSSI));
  307. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PLUGIN_PATH, PLUGIN_LV2, getPluginPath(PLUGIN_LV2));
  308. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST2, getPluginPath(PLUGIN_VST2));
  309. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PLUGIN_PATH, PLUGIN_VST3, getPluginPath(PLUGIN_VST3));
  310. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PLUGIN_PATH, PLUGIN_CLAP, getPluginPath(PLUGIN_CLAP));
  311. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PLUGIN_PATH, PLUGIN_JSFX, getPluginPath(PLUGIN_JSFX));
  312. fCarlaPluginDescriptor->dispatcher(fCarlaPluginHandle, NATIVE_PLUGIN_OPCODE_HOST_USES_EMBED,
  313. 0, 0, nullptr, 0.0f);
  314. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  315. fMidiEvents = new NativeMidiEvent[kMaxMidiEventCount];
  316. #endif
  317. #if DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  318. // create dummy buffers
  319. bufferSizeChanged(getBufferSize());
  320. #endif
  321. }
  322. ~IldaeilPlugin() override
  323. {
  324. if (fCarlaHostHandle != nullptr)
  325. {
  326. carla_host_handle_free(fCarlaHostHandle);
  327. #if DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  328. delete[] fDummyBuffer;
  329. #endif
  330. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  331. delete[] fMidiEvents;
  332. #endif
  333. }
  334. if (fCarlaPluginHandle != nullptr)
  335. fCarlaPluginDescriptor->cleanup(fCarlaPluginHandle);
  336. }
  337. const NativeTimeInfo* hostGetTimeInfo() const noexcept
  338. {
  339. const TimePosition& timePos(getTimePosition());
  340. fCarlaTimeInfo.playing = timePos.playing;
  341. fCarlaTimeInfo.frame = timePos.frame;
  342. fCarlaTimeInfo.bbt.valid = timePos.bbt.valid;
  343. fCarlaTimeInfo.bbt.bar = timePos.bbt.bar;
  344. fCarlaTimeInfo.bbt.beat = timePos.bbt.beat;
  345. fCarlaTimeInfo.bbt.tick = timePos.bbt.tick;
  346. fCarlaTimeInfo.bbt.barStartTick = timePos.bbt.barStartTick;
  347. fCarlaTimeInfo.bbt.beatsPerBar = timePos.bbt.beatsPerBar;
  348. fCarlaTimeInfo.bbt.beatType = timePos.bbt.beatType;
  349. fCarlaTimeInfo.bbt.ticksPerBeat = timePos.bbt.ticksPerBeat;
  350. fCarlaTimeInfo.bbt.beatsPerMinute = timePos.bbt.beatsPerMinute;
  351. return &fCarlaTimeInfo;
  352. }
  353. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  354. bool hostWriteMidiEvent(const NativeMidiEvent* const event)
  355. {
  356. MidiEvent midiEvent;
  357. midiEvent.frame = event->time;
  358. midiEvent.size = event->size;
  359. midiEvent.dataExt = nullptr;
  360. uint32_t i = 0;
  361. for (; i < event->size; ++i)
  362. midiEvent.data[i] = event->data[i];
  363. for (; i < MidiEvent::kDataSize; ++i)
  364. midiEvent.data[i] = 0;
  365. return writeMidiEvent(midiEvent);
  366. }
  367. #endif
  368. intptr_t hostDispatcher(const NativeHostDispatcherOpcode opcode,
  369. const int32_t index, const intptr_t value, void* const ptr, const float opt)
  370. {
  371. switch (opcode)
  372. {
  373. // cannnot be supported
  374. case NATIVE_HOST_OPCODE_HOST_IDLE:
  375. break;
  376. // other stuff
  377. case NATIVE_HOST_OPCODE_NULL:
  378. case NATIVE_HOST_OPCODE_UPDATE_PARAMETER:
  379. case NATIVE_HOST_OPCODE_UPDATE_MIDI_PROGRAM:
  380. case NATIVE_HOST_OPCODE_RELOAD_PARAMETERS:
  381. case NATIVE_HOST_OPCODE_RELOAD_MIDI_PROGRAMS:
  382. case NATIVE_HOST_OPCODE_RELOAD_ALL:
  383. case NATIVE_HOST_OPCODE_UI_UNAVAILABLE:
  384. case NATIVE_HOST_OPCODE_INTERNAL_PLUGIN:
  385. case NATIVE_HOST_OPCODE_QUEUE_INLINE_DISPLAY:
  386. case NATIVE_HOST_OPCODE_UI_TOUCH_PARAMETER:
  387. case NATIVE_HOST_OPCODE_REQUEST_IDLE:
  388. case NATIVE_HOST_OPCODE_GET_FILE_PATH:
  389. case NATIVE_HOST_OPCODE_UI_RESIZE:
  390. case NATIVE_HOST_OPCODE_PREVIEW_BUFFER_DATA:
  391. // TESTING
  392. d_stdout("dispatcher %i, %i, %li, %p, %f", opcode, index, value, ptr, opt);
  393. break;
  394. }
  395. return 0;
  396. }
  397. protected:
  398. /* --------------------------------------------------------------------------------------------------------
  399. * Information */
  400. const char* getLabel() const override
  401. {
  402. #if ILDAEIL_STANDALONE
  403. return "Ildaeil";
  404. #elif DISTRHO_PLUGIN_IS_SYNTH
  405. return "IldaeilSynth";
  406. #elif DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  407. return "IldaeilMIDI";
  408. #else
  409. return "IldaeilFX";
  410. #endif
  411. }
  412. const char* getDescription() const override
  413. {
  414. return "Ildaeil is a mini-plugin host working as a plugin, allowing one-to-one plugin format reusage.";
  415. }
  416. const char* getMaker() const override
  417. {
  418. return "DISTRHO";
  419. }
  420. const char* getHomePage() const override
  421. {
  422. return "https://github.com/DISTRHO/Ildaeil";
  423. }
  424. const char* getLicense() const override
  425. {
  426. return "GPLv2+";
  427. }
  428. uint32_t getVersion() const override
  429. {
  430. return d_version(1, 2, 0);
  431. }
  432. int64_t getUniqueId() const override
  433. {
  434. #if ILDAEIL_STANDALONE
  435. return d_cconst('d', 'I', 'l', 'd');
  436. #elif DISTRHO_PLUGIN_IS_SYNTH
  437. return d_cconst('d', 'I', 'l', 'S');
  438. #elif DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  439. return d_cconst('d', 'I', 'l', 'M');
  440. #else
  441. return d_cconst('d', 'I', 'l', 'F');
  442. #endif
  443. }
  444. /* --------------------------------------------------------------------------------------------------------
  445. * Init */
  446. void initAudioPort(bool input, uint32_t index, AudioPort& port) override
  447. {
  448. port.groupId = kPortGroupStereo;
  449. Plugin::initAudioPort(input, index, port);
  450. }
  451. void initState(const uint32_t index, State& state) override
  452. {
  453. DISTRHO_SAFE_ASSERT_RETURN(index == 0,);
  454. state.hints = kStateIsOnlyForDSP;
  455. state.key = "project";
  456. state.defaultValue = ""
  457. "<?xml version='1.0' encoding='UTF-8'?>\n"
  458. "<!DOCTYPE CARLA-PROJECT>\n"
  459. "<CARLA-PROJECT VERSION='" CARLA_VERSION_STRMIN "'>\n"
  460. "</CARLA-PROJECT>\n";
  461. }
  462. /* --------------------------------------------------------------------------------------------------------
  463. * Internal data */
  464. String getState(const char* const key) const override
  465. {
  466. if (std::strcmp(key, "project") == 0)
  467. {
  468. CarlaEngine* const engine = carla_get_engine_from_handle(fCarlaHostHandle);
  469. fLastProjectState.reset();
  470. engine->saveProjectInternal(fLastProjectState);
  471. return String(static_cast<char*>(fLastProjectState.getDataAndRelease()), false);
  472. }
  473. return String();
  474. }
  475. void setState(const char* const key, const char* const value) override
  476. {
  477. if (std::strcmp(key, "project") == 0)
  478. {
  479. CarlaEngine* const engine = carla_get_engine_from_handle(fCarlaHostHandle);
  480. water::XmlDocument xml(value);
  481. {
  482. const MutexLocker cml(sPluginInfoLoadMutex);
  483. engine->loadProjectInternal(xml, true);
  484. }
  485. if (fUI != nullptr)
  486. ildaeilProjectLoadedFromDSP(fUI);
  487. }
  488. }
  489. /* --------------------------------------------------------------------------------------------------------
  490. * Process */
  491. void checkLatencyChanged()
  492. {
  493. if (fCarlaHostHandle == nullptr)
  494. return;
  495. uint32_t latency = 0;
  496. for (uint32_t i=0; i < carla_get_current_plugin_count(fCarlaHostHandle); ++i)
  497. latency += carla_get_plugin_latency(fCarlaHostHandle, i);
  498. if (fLastLatencyValue != latency)
  499. {
  500. fLastLatencyValue = latency;
  501. setLatency(latency);
  502. }
  503. }
  504. void activate() override
  505. {
  506. if (fCarlaPluginHandle != nullptr)
  507. fCarlaPluginDescriptor->activate(fCarlaPluginHandle);
  508. checkLatencyChanged();
  509. }
  510. void deactivate() override
  511. {
  512. checkLatencyChanged();
  513. if (fCarlaPluginHandle != nullptr)
  514. fCarlaPluginDescriptor->deactivate(fCarlaPluginHandle);
  515. }
  516. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  517. void run(const float** inputs, float** outputs, uint32_t frames,
  518. const MidiEvent* dpfMidiEvents, uint32_t dpfMidiEventCount) override
  519. #else
  520. void run(const float** inputs, float** outputs, uint32_t frames) override
  521. #endif
  522. {
  523. if (fCarlaPluginHandle != nullptr)
  524. {
  525. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  526. uint32_t midiEventCount = 0;
  527. for (uint32_t i=0; i < dpfMidiEventCount; ++i)
  528. {
  529. const MidiEvent& dpfMidiEvent(dpfMidiEvents[i]);
  530. if (dpfMidiEvent.size > 4)
  531. continue;
  532. NativeMidiEvent& midiEvent(fMidiEvents[midiEventCount]);
  533. midiEvent.time = dpfMidiEvent.frame;
  534. midiEvent.port = 0;
  535. midiEvent.size = dpfMidiEvent.size;
  536. std::memcpy(midiEvent.data, dpfMidiEvent.data, midiEvent.size);
  537. if (++midiEventCount == kMaxMidiEventCount)
  538. break;
  539. }
  540. #else
  541. static constexpr const NativeMidiEvent* fMidiEvents = nullptr;
  542. static constexpr const uint32_t midiEventCount = 0;
  543. #endif
  544. #if DISTRHO_PLUGIN_NUM_INPUTS == 0
  545. inputs = (const float**)fDummyBuffers;
  546. #endif
  547. #if DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  548. outputs = fDummyBuffers;
  549. #endif
  550. fCarlaPluginDescriptor->process(fCarlaPluginHandle, (float**)inputs, outputs, frames,
  551. fMidiEvents, midiEventCount);
  552. checkLatencyChanged();
  553. }
  554. else
  555. {
  556. std::memset(outputs[0], 0, sizeof(float)*frames);
  557. std::memset(outputs[1], 0, sizeof(float)*frames);
  558. }
  559. }
  560. void bufferSizeChanged(const uint32_t newBufferSize) override
  561. {
  562. #if DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_OUTPUTS == 0
  563. delete[] fDummyBuffer;
  564. fDummyBuffer = new float[newBufferSize];
  565. fDummyBuffers[0] = fDummyBuffer;
  566. fDummyBuffers[1] = fDummyBuffer;
  567. std::memset(fDummyBuffer, 0, sizeof(float)*newBufferSize);
  568. #endif
  569. if (fCarlaPluginHandle != nullptr)
  570. fCarlaPluginDescriptor->dispatcher(fCarlaPluginHandle, NATIVE_PLUGIN_OPCODE_BUFFER_SIZE_CHANGED,
  571. 0, newBufferSize, nullptr, 0.0f);
  572. }
  573. void sampleRateChanged(const double newSampleRate) override
  574. {
  575. if (fCarlaPluginHandle != nullptr)
  576. fCarlaPluginDescriptor->dispatcher(fCarlaPluginHandle, NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED,
  577. 0, 0, nullptr, newSampleRate);
  578. }
  579. // -------------------------------------------------------------------------------------------------------
  580. /**
  581. Set our plugin class as non-copyable and add a leak detector just in case.
  582. */
  583. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(IldaeilPlugin)
  584. };
  585. // -----------------------------------------------------------------------------------------------------------
  586. static uint32_t host_get_buffer_size(const NativeHostHandle handle)
  587. {
  588. return static_cast<IldaeilPlugin*>(handle)->getBufferSize();
  589. }
  590. static double host_get_sample_rate(const NativeHostHandle handle)
  591. {
  592. return static_cast<IldaeilPlugin*>(handle)->getSampleRate();
  593. }
  594. static bool host_is_offline(NativeHostHandle)
  595. {
  596. return false;
  597. }
  598. static const NativeTimeInfo* host_get_time_info(const NativeHostHandle handle)
  599. {
  600. return static_cast<IldaeilPlugin*>(handle)->hostGetTimeInfo();
  601. }
  602. static bool host_write_midi_event(const NativeHostHandle handle, const NativeMidiEvent* const event)
  603. {
  604. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  605. return static_cast<IldaeilPlugin*>(handle)->hostWriteMidiEvent(event);
  606. #else
  607. return handle != nullptr && event != nullptr && false;
  608. #endif
  609. }
  610. static void host_ui_parameter_changed(const NativeHostHandle handle, const uint32_t index, const float value)
  611. {
  612. ildaeilParameterChangeForUI(static_cast<IldaeilPlugin*>(handle)->fUI, index, value);
  613. }
  614. static void host_ui_midi_program_changed(NativeHostHandle handle, uint8_t channel, uint32_t bank, uint32_t program)
  615. {
  616. d_stdout("%s %p %u %u %u", __FUNCTION__, handle, channel, bank, program);
  617. }
  618. static void host_ui_custom_data_changed(NativeHostHandle handle, const char* key, const char* value)
  619. {
  620. d_stdout("%s %p %s %s", __FUNCTION__, handle, key, value);
  621. }
  622. static void host_ui_closed(NativeHostHandle handle)
  623. {
  624. ildaeilCloseUI(static_cast<IldaeilPlugin*>(handle));
  625. }
  626. static const char* host_ui_open_file(const NativeHostHandle handle, const bool isDir, const char* const title, const char* const filter)
  627. {
  628. return ildaeilOpenFileForUI(static_cast<IldaeilPlugin*>(handle)->fUI, isDir, title, filter);
  629. }
  630. static const char* host_ui_save_file(NativeHostHandle, bool, const char*, const char*)
  631. {
  632. return nullptr;
  633. }
  634. static intptr_t host_dispatcher(const NativeHostHandle handle, const NativeHostDispatcherOpcode opcode,
  635. const int32_t index, const intptr_t value, void* const ptr, const float opt)
  636. {
  637. return static_cast<IldaeilPlugin*>(handle)->hostDispatcher(opcode, index, value, ptr, opt);
  638. }
  639. /* --------------------------------------------------------------------------------------------------------------------
  640. * Plugin entry point, called by DPF to create a new plugin instance. */
  641. Plugin* createPlugin()
  642. {
  643. return new IldaeilPlugin();
  644. }
  645. // --------------------------------------------------------------------------------------------------------------------
  646. END_NAMESPACE_DISTRHO