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.

555 lines
19KB

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