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.

776 lines
28KB

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