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.

502 lines
17KB

  1. /*
  2. * DISTRHO Ildaeil Plugin
  3. * Copyright (C) 2021 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 "CarlaNativePlugin.h"
  18. #include "CarlaEngine.hpp"
  19. #include "water/streams/MemoryOutputStream.h"
  20. #include "water/xml/XmlDocument.h"
  21. #include "DistrhoPlugin.hpp"
  22. START_NAMESPACE_DISTRHO
  23. // --------------------------------------------------------------------------------------------------------------------
  24. using namespace CarlaBackend;
  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 const char* host_ui_open_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter);
  32. static const char* host_ui_save_file(NativeHostHandle handle, bool isDir, const char* title, const char* filter);
  33. static intptr_t host_dispatcher(NativeHostHandle handle, NativeHostDispatcherOpcode opcode, int32_t index, intptr_t value, void* ptr, float opt);
  34. // --------------------------------------------------------------------------------------------------------------------
  35. void ildaeilParameterChangeForUI(void* ui, uint32_t index, float value);
  36. // --------------------------------------------------------------------------------------------------------------------
  37. class IldaeilPlugin : public Plugin
  38. {
  39. public:
  40. const NativePluginDescriptor* fCarlaPluginDescriptor;
  41. NativePluginHandle fCarlaPluginHandle;
  42. NativeHostDescriptor fCarlaHostDescriptor;
  43. CarlaHostHandle fCarlaHostHandle;
  44. void* fUI;
  45. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  46. static constexpr const uint kMaxMidiEventCount = 512;
  47. NativeMidiEvent* fMidiEvents;
  48. uint32_t fMidiEventCount;
  49. float* fDummyBuffer;
  50. float* fDummyBuffers[2];
  51. #endif
  52. mutable NativeTimeInfo fCarlaTimeInfo;
  53. mutable water::MemoryOutputStream fLastProjectState;
  54. uint32_t fLastLatencyValue;
  55. IldaeilPlugin()
  56. : Plugin(0, 0, 1),
  57. fCarlaPluginDescriptor(nullptr),
  58. fCarlaPluginHandle(nullptr),
  59. fCarlaHostHandle(nullptr),
  60. fUI(nullptr),
  61. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  62. fMidiEvents(nullptr),
  63. fMidiEventCount(0),
  64. fDummyBuffer(nullptr),
  65. #endif
  66. fLastLatencyValue(0)
  67. {
  68. fCarlaPluginDescriptor = carla_get_native_rack_plugin();
  69. DISTRHO_SAFE_ASSERT_RETURN(fCarlaPluginDescriptor != nullptr,);
  70. memset(&fCarlaHostDescriptor, 0, sizeof(fCarlaHostDescriptor));
  71. memset(&fCarlaTimeInfo, 0, sizeof(fCarlaTimeInfo));
  72. fCarlaHostDescriptor.handle = this;
  73. fCarlaHostDescriptor.resourceDir = carla_get_library_folder();
  74. fCarlaHostDescriptor.uiName = "Ildaeil";
  75. fCarlaHostDescriptor.uiParentId = 0;
  76. fCarlaHostDescriptor.get_buffer_size = host_get_buffer_size;
  77. fCarlaHostDescriptor.get_sample_rate = host_get_sample_rate;
  78. fCarlaHostDescriptor.is_offline = host_is_offline;
  79. fCarlaHostDescriptor.get_time_info = host_get_time_info;
  80. fCarlaHostDescriptor.write_midi_event = host_write_midi_event;
  81. fCarlaHostDescriptor.ui_parameter_changed = host_ui_parameter_changed;
  82. fCarlaHostDescriptor.ui_midi_program_changed = nullptr;
  83. fCarlaHostDescriptor.ui_custom_data_changed = nullptr;
  84. fCarlaHostDescriptor.ui_closed = nullptr;
  85. fCarlaHostDescriptor.ui_open_file = host_ui_open_file;
  86. fCarlaHostDescriptor.ui_save_file = host_ui_save_file;
  87. fCarlaHostDescriptor.dispatcher = host_dispatcher;
  88. fCarlaPluginHandle = fCarlaPluginDescriptor->instantiate(&fCarlaHostDescriptor);
  89. DISTRHO_SAFE_ASSERT_RETURN(fCarlaPluginHandle != nullptr,);
  90. fCarlaHostHandle = carla_create_native_plugin_host_handle(fCarlaPluginDescriptor, fCarlaPluginHandle);
  91. DISTRHO_SAFE_ASSERT_RETURN(fCarlaHostHandle != nullptr,);
  92. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_BINARIES, 0, "/usr/lib/carla");
  93. carla_set_engine_option(fCarlaHostHandle, ENGINE_OPTION_PATH_RESOURCES, 0, "/usr/share/carla/resources");
  94. fCarlaPluginDescriptor->dispatcher(fCarlaPluginHandle, NATIVE_PLUGIN_OPCODE_HOST_USES_EMBED,
  95. 0, 0, nullptr, 0.0f);
  96. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  97. fMidiEvents = new NativeMidiEvent[kMaxMidiEventCount];
  98. // create dummy buffers
  99. bufferSizeChanged(getBufferSize());
  100. #endif
  101. }
  102. ~IldaeilPlugin() override
  103. {
  104. if (fCarlaHostHandle != nullptr)
  105. {
  106. carla_host_handle_free(fCarlaHostHandle);
  107. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  108. delete[] fMidiEvents;
  109. delete[] fDummyBuffer;
  110. #endif
  111. }
  112. if (fCarlaPluginHandle != nullptr)
  113. fCarlaPluginDescriptor->cleanup(fCarlaPluginHandle);
  114. }
  115. const NativeTimeInfo* hostGetTimeInfo() const noexcept
  116. {
  117. const TimePosition& timePos(getTimePosition());
  118. fCarlaTimeInfo.playing = timePos.playing;
  119. fCarlaTimeInfo.frame = timePos.frame;
  120. fCarlaTimeInfo.bbt.valid = timePos.bbt.valid;
  121. fCarlaTimeInfo.bbt.bar = timePos.bbt.bar;
  122. fCarlaTimeInfo.bbt.beat = timePos.bbt.beat;
  123. fCarlaTimeInfo.bbt.tick = timePos.bbt.tick;
  124. fCarlaTimeInfo.bbt.barStartTick = timePos.bbt.barStartTick;
  125. fCarlaTimeInfo.bbt.beatsPerBar = timePos.bbt.beatsPerBar;
  126. fCarlaTimeInfo.bbt.beatType = timePos.bbt.beatType;
  127. fCarlaTimeInfo.bbt.ticksPerBeat = timePos.bbt.ticksPerBeat;
  128. fCarlaTimeInfo.bbt.beatsPerMinute = timePos.bbt.beatsPerMinute;
  129. return &fCarlaTimeInfo;
  130. }
  131. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  132. bool hostWriteMidiEvent(const NativeMidiEvent* const event)
  133. {
  134. MidiEvent midiEvent;
  135. midiEvent.frame = event->time;
  136. midiEvent.size = event->size;
  137. midiEvent.dataExt = nullptr;
  138. uint32_t i = 0;
  139. for (; i < event->size; ++i)
  140. midiEvent.data[i] = event->data[i];
  141. for (; i < MidiEvent::kDataSize; ++i)
  142. midiEvent.data[i] = 0;
  143. return writeMidiEvent(midiEvent);
  144. }
  145. #endif
  146. intptr_t hostDispatcher(const NativeHostDispatcherOpcode opcode,
  147. const int32_t index, const intptr_t value, void* const ptr, const float opt)
  148. {
  149. switch (opcode)
  150. {
  151. case NATIVE_HOST_OPCODE_NULL:
  152. case NATIVE_HOST_OPCODE_UPDATE_PARAMETER:
  153. case NATIVE_HOST_OPCODE_UPDATE_MIDI_PROGRAM:
  154. case NATIVE_HOST_OPCODE_RELOAD_PARAMETERS:
  155. case NATIVE_HOST_OPCODE_RELOAD_MIDI_PROGRAMS:
  156. case NATIVE_HOST_OPCODE_RELOAD_ALL:
  157. case NATIVE_HOST_OPCODE_UI_UNAVAILABLE:
  158. case NATIVE_HOST_OPCODE_HOST_IDLE:
  159. case NATIVE_HOST_OPCODE_INTERNAL_PLUGIN:
  160. case NATIVE_HOST_OPCODE_QUEUE_INLINE_DISPLAY:
  161. case NATIVE_HOST_OPCODE_UI_TOUCH_PARAMETER:
  162. case NATIVE_HOST_OPCODE_REQUEST_IDLE:
  163. case NATIVE_HOST_OPCODE_GET_FILE_PATH:
  164. case NATIVE_HOST_OPCODE_UI_RESIZE:
  165. case NATIVE_HOST_OPCODE_PREVIEW_BUFFER_DATA:
  166. // TESTING
  167. d_stdout("dispatcher %i, %i, %li, %p, %f", opcode, index, value, ptr, opt);
  168. break;
  169. }
  170. return 0;
  171. }
  172. protected:
  173. /* --------------------------------------------------------------------------------------------------------
  174. * Information */
  175. /**
  176. Get the plugin label.
  177. A plugin label follows the same rules as Parameter::symbol, with the exception that it can start with numbers.
  178. */
  179. const char* getLabel() const override
  180. {
  181. #if DISTRHO_PLUGIN_IS_SYNTH
  182. return "IldaeilSynth";
  183. #elif DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  184. return "IldaeilMIDI";
  185. #else
  186. return "IldaeilFX";
  187. #endif
  188. }
  189. /**
  190. Get an extensive comment/description about the plugin.
  191. */
  192. const char* getDescription() const override
  193. {
  194. return "Ildaeil is a mini-plugin host working as a plugin, allowing one-to-one plugin format reusage.";
  195. }
  196. /**
  197. Get the plugin author/maker.
  198. */
  199. const char* getMaker() const override
  200. {
  201. return "DISTRHO";
  202. }
  203. /**
  204. Get the plugin homepage.
  205. */
  206. const char* getHomePage() const override
  207. {
  208. return "https://github.com/DISTRHO/Ildaeil";
  209. }
  210. /**
  211. Get the plugin license name (a single line of text).
  212. For commercial plugins this should return some short copyright information.
  213. */
  214. const char* getLicense() const override
  215. {
  216. return "GPLv2+";
  217. }
  218. /**
  219. Get the plugin version, in hexadecimal.
  220. */
  221. uint32_t getVersion() const override
  222. {
  223. return d_version(1, 0, 0);
  224. }
  225. /**
  226. Get the plugin unique Id.
  227. This value is used by LADSPA, DSSI and VST plugin formats.
  228. */
  229. int64_t getUniqueId() const override
  230. {
  231. #if DISTRHO_PLUGIN_IS_SYNTH
  232. return d_cconst('d', 'I', 'l', 'S');
  233. #elif DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  234. return d_cconst('d', 'I', 'l', 'M');
  235. #else
  236. return d_cconst('d', 'I', 'l', 'F');
  237. #endif
  238. }
  239. /* --------------------------------------------------------------------------------------------------------
  240. * Init */
  241. void initState(const uint32_t index, String& stateKey, String& defaultStateValue) override
  242. {
  243. DISTRHO_SAFE_ASSERT_RETURN(index == 0,);
  244. stateKey = "project";
  245. defaultStateValue = ""
  246. "<?xml version='1.0' encoding='UTF-8'?>\n"
  247. "<!DOCTYPE CARLA-PROJECT>\n"
  248. "<CARLA-PROJECT VERSION='" CARLA_VERSION_STRMIN "'>\n"
  249. "</CARLA-PROJECT>\n";
  250. }
  251. /* --------------------------------------------------------------------------------------------------------
  252. * Internal data */
  253. String getState(const char* const key) const override
  254. {
  255. if (std::strcmp(key, "project") == 0)
  256. {
  257. CarlaEngine* const engine = carla_get_engine_from_handle(fCarlaHostHandle);
  258. fLastProjectState.reset();
  259. engine->saveProjectInternal(fLastProjectState);
  260. return String(static_cast<char*>(fLastProjectState.getDataAndRelease()), false);
  261. }
  262. return String();
  263. }
  264. void setState(const char* const key, const char* const value) override
  265. {
  266. if (std::strcmp(key, "project") == 0)
  267. {
  268. CarlaEngine* const engine = carla_get_engine_from_handle(fCarlaHostHandle);
  269. water::XmlDocument xml(value);
  270. engine->loadProjectInternal(xml, true);
  271. }
  272. }
  273. /* --------------------------------------------------------------------------------------------------------
  274. * Process */
  275. void checkLatencyChanged()
  276. {
  277. if (fCarlaHostHandle == nullptr)
  278. return;
  279. uint32_t latency = 0;
  280. for (uint32_t i=0; i < carla_get_current_plugin_count(fCarlaHostHandle); ++i)
  281. latency += carla_get_plugin_latency(fCarlaHostHandle, i);
  282. if (fLastLatencyValue != latency)
  283. {
  284. fLastLatencyValue = latency;
  285. setLatency(latency);
  286. }
  287. }
  288. void activate() override
  289. {
  290. if (fCarlaPluginHandle != nullptr)
  291. fCarlaPluginDescriptor->activate(fCarlaPluginHandle);
  292. checkLatencyChanged();
  293. }
  294. void deactivate() override
  295. {
  296. if (fCarlaPluginHandle != nullptr)
  297. fCarlaPluginDescriptor->deactivate(fCarlaPluginHandle);
  298. }
  299. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  300. void run(const float** inputs, float** outputs, uint32_t frames,
  301. const MidiEvent* dpfMidiEvents, uint32_t dpfMidiEventCount) override
  302. #else
  303. void run(const float** inputs, float** outputs, uint32_t frames) override
  304. #endif
  305. {
  306. if (fCarlaPluginHandle != nullptr)
  307. {
  308. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  309. uint32_t midiEventCount = 0;
  310. for (uint32_t i=0; i < dpfMidiEventCount; ++i)
  311. {
  312. const MidiEvent& dpfMidiEvent(dpfMidiEvents[i]);
  313. if (dpfMidiEvent.size > 4)
  314. continue;
  315. NativeMidiEvent& midiEvent(fMidiEvents[midiEventCount]);
  316. midiEvent.time = dpfMidiEvent.frame;
  317. midiEvent.port = 0;
  318. midiEvent.size = dpfMidiEvent.size;
  319. std::memcpy(midiEvent.data, dpfMidiEvent.data, midiEvent.size);
  320. if (++midiEventCount == kMaxMidiEventCount)
  321. break;
  322. }
  323. # if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  324. fCarlaPluginDescriptor->process(fCarlaPluginHandle, fDummyBuffers, fDummyBuffers, frames,
  325. fMidiEvents, midiEventCount);
  326. // unused
  327. (void)outputs;
  328. # else
  329. fCarlaPluginDescriptor->process(fCarlaPluginHandle, fDummyBuffers, outputs, frames,
  330. fMidiEvents, midiEventCount);
  331. # endif
  332. // unused
  333. (void)inputs;
  334. #else
  335. fCarlaPluginDescriptor->process(fCarlaPluginHandle, (float**)inputs, outputs, frames, nullptr, 0);
  336. #endif
  337. checkLatencyChanged();
  338. }
  339. else
  340. {
  341. std::memset(outputs[0], 0, sizeof(float)*frames);
  342. std::memset(outputs[1], 0, sizeof(float)*frames);
  343. }
  344. }
  345. void bufferSizeChanged(const uint32_t newBufferSize) override
  346. {
  347. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  348. delete[] fDummyBuffer;
  349. fDummyBuffer = new float[newBufferSize];
  350. fDummyBuffers[0] = fDummyBuffer;
  351. fDummyBuffers[1] = fDummyBuffer;
  352. std::memset(fDummyBuffer, 0, sizeof(float)*newBufferSize);
  353. #endif
  354. if (fCarlaPluginHandle != nullptr)
  355. fCarlaPluginDescriptor->dispatcher(fCarlaPluginHandle, NATIVE_PLUGIN_OPCODE_BUFFER_SIZE_CHANGED,
  356. 0, newBufferSize, nullptr, 0.0f);
  357. }
  358. void sampleRateChanged(const double newSampleRate) override
  359. {
  360. if (fCarlaPluginHandle != nullptr)
  361. fCarlaPluginDescriptor->dispatcher(fCarlaPluginHandle, NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED,
  362. 0, 0, nullptr, newSampleRate);
  363. }
  364. // -------------------------------------------------------------------------------------------------------
  365. /**
  366. Set our plugin class as non-copyable and add a leak detector just in case.
  367. */
  368. DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(IldaeilPlugin)
  369. };
  370. // -----------------------------------------------------------------------------------------------------------
  371. static uint32_t host_get_buffer_size(const NativeHostHandle handle)
  372. {
  373. return static_cast<IldaeilPlugin*>(handle)->getBufferSize();
  374. }
  375. static double host_get_sample_rate(const NativeHostHandle handle)
  376. {
  377. return static_cast<IldaeilPlugin*>(handle)->getSampleRate();
  378. }
  379. static bool host_is_offline(NativeHostHandle)
  380. {
  381. return false;
  382. }
  383. static const NativeTimeInfo* host_get_time_info(const NativeHostHandle handle)
  384. {
  385. return static_cast<IldaeilPlugin*>(handle)->hostGetTimeInfo();
  386. }
  387. static bool host_write_midi_event(const NativeHostHandle handle, const NativeMidiEvent* const event)
  388. {
  389. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  390. return static_cast<IldaeilPlugin*>(handle)->hostWriteMidiEvent(event);
  391. #else
  392. return handle != nullptr && event != nullptr && false;
  393. #endif
  394. }
  395. static void host_ui_parameter_changed(const NativeHostHandle handle, const uint32_t index, const float value)
  396. {
  397. ildaeilParameterChangeForUI(static_cast<IldaeilPlugin*>(handle)->fUI, index, value);
  398. }
  399. static const char* host_ui_open_file(NativeHostHandle, bool, const char*, const char*)
  400. {
  401. return nullptr;
  402. }
  403. static const char* host_ui_save_file(NativeHostHandle, bool, const char*, const char*)
  404. {
  405. return nullptr;
  406. }
  407. static intptr_t host_dispatcher(const NativeHostHandle handle, const NativeHostDispatcherOpcode opcode,
  408. const int32_t index, const intptr_t value, void* const ptr, const float opt)
  409. {
  410. return static_cast<IldaeilPlugin*>(handle)->hostDispatcher(opcode, index, value, ptr, opt);
  411. }
  412. /* --------------------------------------------------------------------------------------------------------------------
  413. * Plugin entry point, called by DPF to create a new plugin instance. */
  414. Plugin* createPlugin()
  415. {
  416. return new IldaeilPlugin();
  417. }
  418. // --------------------------------------------------------------------------------------------------------------------
  419. END_NAMESPACE_DISTRHO