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.

459 lines
15KB

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