DISTRHO Plugin Framework
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.

4406 lines
160KB

  1. /*
  2. * DISTRHO Plugin Framework (DPF)
  3. * Copyright (C) 2012-2021 Filipe Coelho <falktx@falktx.com>
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any purpose with
  6. * or without fee is hereby granted, provided that the above copyright notice and this
  7. * permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
  10. * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
  11. * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
  12. * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
  13. * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  14. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. */
  16. #include "DistrhoPluginInternal.hpp"
  17. #include "../DistrhoPluginUtils.hpp"
  18. #include "../extra/ScopedPointer.hpp"
  19. #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI
  20. # undef DISTRHO_PLUGIN_HAS_UI
  21. # define DISTRHO_PLUGIN_HAS_UI 0
  22. #endif
  23. #if DISTRHO_PLUGIN_HAS_UI && ! defined(HAVE_DGL) && ! DISTRHO_PLUGIN_HAS_EXTERNAL_UI
  24. # undef DISTRHO_PLUGIN_HAS_UI
  25. # define DISTRHO_PLUGIN_HAS_UI 0
  26. #endif
  27. #if DISTRHO_PLUGIN_HAS_UI == 1 && DISTRHO_PLUGIN_WANT_DIRECT_ACCESS == 0
  28. # define DPF_VST3_USES_SEPARATE_CONTROLLER
  29. #endif
  30. #define DPF_VST3_MAX_BUFFER_SIZE 32768
  31. #define DPF_VST3_MAX_SAMPLE_RATE 384000
  32. #define DPF_VST3_MAX_LATENCY DPF_VST3_MAX_SAMPLE_RATE * 10
  33. #if DISTRHO_PLUGIN_HAS_UI
  34. # include "../extra/RingBuffer.hpp"
  35. #endif
  36. #include "travesty/audio_processor.h"
  37. #include "travesty/component.h"
  38. #include "travesty/edit_controller.h"
  39. #include "travesty/factory.h"
  40. #include "travesty/host.h"
  41. #ifdef DISTRHO_PROPER_CPP11_SUPPORT
  42. # include <atomic>
  43. #else
  44. // quick and dirty std::atomic replacement for the things we need
  45. namespace std {
  46. struct atomic_int {
  47. volatile int value;
  48. explicit atomic_int(volatile int v) noexcept : value(v) {}
  49. int operator++() volatile noexcept { return __atomic_add_fetch(&value, 1, __ATOMIC_RELAXED); }
  50. int operator--() volatile noexcept { return __atomic_sub_fetch(&value, 1, __ATOMIC_RELAXED); }
  51. operator int() volatile noexcept { return __atomic_load_n(&value, __ATOMIC_RELAXED); }
  52. };
  53. }
  54. #endif
  55. #include <map>
  56. #include <string>
  57. #include <vector>
  58. /* TODO items:
  59. * == parameters
  60. * - test parameter triggers
  61. * - have parameter outputs host-provided UI working in at least 1 host
  62. * - parameter groups via unit ids
  63. * - test parameter changes from DSP (aka requestParameterValueChange)
  64. * - how to hide parameter outputs?
  65. * - how to hide program parameter?
  66. * - test receiving midi CC
  67. * - implement getParameterNormalized/setParameterNormalized for MIDI CC params ?
  68. * - fully implemented parameter stuff and verify
  69. * - float to int safe casting
  70. * - verify that latency changes works (with and without DPF_VST3_USES_SEPARATE_CONTROLLER)
  71. * == MIDI
  72. * - MIDI CC changes (need to store value to give to the host?)
  73. * - MIDI program changes
  74. * - MIDI sysex
  75. * - append MIDI input events in a sorted way
  76. * == BUSES
  77. * - bus arrangements
  78. * - optional audio buses, create dummy buffer of max_block_size length for them
  79. * - routing info, do we care?
  80. * - set sidechain bus name from port group
  81. * == CV
  82. * - cv scaling to -1/+1
  83. * - test in at least 1 host
  84. * == INFO
  85. * - set factory email (needs new DPF API, useful for LV2 as well)
  86. * - do something with set_io_mode?
  87. */
  88. START_NAMESPACE_DISTRHO
  89. // --------------------------------------------------------------------------------------------------------------------
  90. #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  91. static constexpr const writeMidiFunc writeMidiCallback = nullptr;
  92. #endif
  93. #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  94. static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr;
  95. #endif
  96. typedef std::map<const String, String> StringMap;
  97. // --------------------------------------------------------------------------------------------------------------------
  98. // custom v3_tuid compatible type
  99. typedef uint32_t dpf_tuid[4];
  100. #ifdef DISTRHO_PROPER_CPP11_SUPPORT
  101. static_assert(sizeof(v3_tuid) == sizeof(dpf_tuid), "uid size mismatch");
  102. #endif
  103. // --------------------------------------------------------------------------------------------------------------------
  104. // custom, constant uids related to DPF
  105. static constexpr const uint32_t dpf_id_entry = d_cconst('D', 'P', 'F', ' ');
  106. static constexpr const uint32_t dpf_id_clas = d_cconst('c', 'l', 'a', 's');
  107. static constexpr const uint32_t dpf_id_comp = d_cconst('c', 'o', 'm', 'p');
  108. static constexpr const uint32_t dpf_id_ctrl = d_cconst('c', 't', 'r', 'l');
  109. static constexpr const uint32_t dpf_id_proc = d_cconst('p', 'r', 'o', 'c');
  110. static constexpr const uint32_t dpf_id_view = d_cconst('v', 'i', 'e', 'w');
  111. // --------------------------------------------------------------------------------------------------------------------
  112. // plugin specific uids (values are filled in during plugin init)
  113. static dpf_tuid dpf_tuid_class = { dpf_id_entry, dpf_id_clas, 0, 0 };
  114. static dpf_tuid dpf_tuid_component = { dpf_id_entry, dpf_id_comp, 0, 0 };
  115. static dpf_tuid dpf_tuid_controller = { dpf_id_entry, dpf_id_ctrl, 0, 0 };
  116. static dpf_tuid dpf_tuid_processor = { dpf_id_entry, dpf_id_proc, 0, 0 };
  117. static dpf_tuid dpf_tuid_view = { dpf_id_entry, dpf_id_view, 0, 0 };
  118. // --------------------------------------------------------------------------------------------------------------------
  119. // Utility functions
  120. const char* tuid2str(const v3_tuid iid)
  121. {
  122. static constexpr const struct {
  123. v3_tuid iid;
  124. const char* name;
  125. } extra_known_iids[] = {
  126. { V3_ID(0x00000000,0x00000000,0x00000000,0x00000000), "(nil)" },
  127. // edit-controller
  128. { V3_ID(0xF040B4B3,0xA36045EC,0xABCDC045,0xB4D5A2CC), "{v3_component_handler2|NOT}" },
  129. { V3_ID(0x7F4EFE59,0xF3204967,0xAC27A3AE,0xAFB63038), "{v3_edit_controller2|NOT}" },
  130. { V3_ID(0x067D02C1,0x5B4E274D,0xA92D90FD,0x6EAF7240), "{v3_component_handler_bus_activation|NOT}" },
  131. { V3_ID(0xC1271208,0x70594098,0xB9DD34B3,0x6BB0195E), "{v3_edit_controller_host_editing|NOT}" },
  132. { V3_ID(0xB7F8F859,0x41234872,0x91169581,0x4F3721A3), "{v3_edit_controller_note_expression_controller|NOT}" },
  133. // units
  134. { V3_ID(0x8683B01F,0x7B354F70,0xA2651DEC,0x353AF4FF), "{v3_program_list_data|NOT}" },
  135. { V3_ID(0x6C389611,0xD391455D,0xB870B833,0x94A0EFDD), "{v3_unit_data|NOT}" },
  136. { V3_ID(0x4B5147F8,0x4654486B,0x8DAB30BA,0x163A3C56), "{v3_unit_handler|NOT}" },
  137. { V3_ID(0xF89F8CDF,0x699E4BA5,0x96AAC9A4,0x81452B01), "{v3_unit_handler2|NOT}" },
  138. { V3_ID(0x3D4BD6B5,0x913A4FD2,0xA886E768,0xA5EB92C1), "{v3_unit_info|NOT}" },
  139. // misc
  140. { V3_ID(0x0F194781,0x8D984ADA,0xBBA0C1EF,0xC011D8D0), "{v3_info_listener|NOT}" },
  141. };
  142. if (v3_tuid_match(iid, v3_audio_processor_iid))
  143. return "{v3_audio_processor}";
  144. if (v3_tuid_match(iid, v3_attribute_list_iid))
  145. return "{v3_attribute_list_iid}";
  146. if (v3_tuid_match(iid, v3_bstream_iid))
  147. return "{v3_bstream}";
  148. if (v3_tuid_match(iid, v3_component_iid))
  149. return "{v3_component}";
  150. if (v3_tuid_match(iid, v3_component_handler_iid))
  151. return "{v3_component_handler}";
  152. if (v3_tuid_match(iid, v3_connection_point_iid))
  153. return "{v3_connection_point_iid}";
  154. if (v3_tuid_match(iid, v3_edit_controller_iid))
  155. return "{v3_edit_controller}";
  156. if (v3_tuid_match(iid, v3_event_handler_iid))
  157. return "{v3_event_handler_iid}";
  158. if (v3_tuid_match(iid, v3_event_list_iid))
  159. return "{v3_event_list}";
  160. if (v3_tuid_match(iid, v3_funknown_iid))
  161. return "{v3_funknown}";
  162. if (v3_tuid_match(iid, v3_host_application_iid))
  163. return "{v3_host_application_iid}";
  164. if (v3_tuid_match(iid, v3_message_iid))
  165. return "{v3_message_iid}";
  166. if (v3_tuid_match(iid, v3_midi_mapping_iid))
  167. return "{v3_midi_mapping_iid}";
  168. if (v3_tuid_match(iid, v3_param_value_queue_iid))
  169. return "{v3_param_value_queue}";
  170. if (v3_tuid_match(iid, v3_param_changes_iid))
  171. return "{v3_param_changes}";
  172. if (v3_tuid_match(iid, v3_plugin_base_iid))
  173. return "{v3_plugin_base}";
  174. if (v3_tuid_match(iid, v3_plugin_factory_iid))
  175. return "{v3_plugin_factory}";
  176. if (v3_tuid_match(iid, v3_plugin_factory_2_iid))
  177. return "{v3_plugin_factory_2}";
  178. if (v3_tuid_match(iid, v3_plugin_factory_3_iid))
  179. return "{v3_plugin_factory_3}";
  180. if (v3_tuid_match(iid, v3_plugin_frame_iid))
  181. return "{v3_plugin_frame}";
  182. if (v3_tuid_match(iid, v3_plugin_view_iid))
  183. return "{v3_plugin_view}";
  184. if (v3_tuid_match(iid, v3_plugin_view_content_scale_iid))
  185. return "{v3_plugin_view_content_scale_iid}";
  186. if (v3_tuid_match(iid, v3_plugin_view_parameter_finder_iid))
  187. return "{v3_plugin_view_parameter_finder}";
  188. if (v3_tuid_match(iid, v3_process_context_requirements_iid))
  189. return "{v3_process_context_requirements}";
  190. if (v3_tuid_match(iid, v3_run_loop_iid))
  191. return "{v3_run_loop_iid}";
  192. if (v3_tuid_match(iid, v3_timer_handler_iid))
  193. return "{v3_timer_handler_iid}";
  194. if (std::memcmp(iid, dpf_tuid_class, sizeof(dpf_tuid)) == 0)
  195. return "{dpf_tuid_class}";
  196. if (std::memcmp(iid, dpf_tuid_component, sizeof(dpf_tuid)) == 0)
  197. return "{dpf_tuid_component}";
  198. if (std::memcmp(iid, dpf_tuid_controller, sizeof(dpf_tuid)) == 0)
  199. return "{dpf_tuid_controller}";
  200. if (std::memcmp(iid, dpf_tuid_processor, sizeof(dpf_tuid)) == 0)
  201. return "{dpf_tuid_processor}";
  202. if (std::memcmp(iid, dpf_tuid_view, sizeof(dpf_tuid)) == 0)
  203. return "{dpf_tuid_view}";
  204. for (size_t i=0; i<ARRAY_SIZE(extra_known_iids); ++i)
  205. {
  206. if (v3_tuid_match(iid, extra_known_iids[i].iid))
  207. return extra_known_iids[i].name;
  208. }
  209. static char buf[46];
  210. std::snprintf(buf, sizeof(buf), "{0x%08X,0x%08X,0x%08X,0x%08X}",
  211. (uint32_t)d_cconst(iid[ 0], iid[ 1], iid[ 2], iid[ 3]),
  212. (uint32_t)d_cconst(iid[ 4], iid[ 5], iid[ 6], iid[ 7]),
  213. (uint32_t)d_cconst(iid[ 8], iid[ 9], iid[10], iid[11]),
  214. (uint32_t)d_cconst(iid[12], iid[13], iid[14], iid[15]));
  215. return buf;
  216. }
  217. // --------------------------------------------------------------------------------------------------------------------
  218. static bool strcmp_utf16(const int16_t* const str16, const char* const str8)
  219. {
  220. size_t i = 0;
  221. for (; str8[i] != '\0'; ++i)
  222. {
  223. const uint8_t char8 = static_cast<uint8_t>(str8[i]);
  224. // skip non-ascii chars, unsupported
  225. if (char8 >= 0x80)
  226. return false;
  227. if (str16[i] != char8)
  228. return false;
  229. }
  230. return str16[i] == str8[i];
  231. }
  232. // --------------------------------------------------------------------------------------------------------------------
  233. static size_t strlen_utf16(const int16_t* const str)
  234. {
  235. size_t i = 0;
  236. while (str[i] != 0)
  237. ++i;
  238. return i;
  239. }
  240. // --------------------------------------------------------------------------------------------------------------------
  241. static void strncpy(char* const dst, const char* const src, const size_t length)
  242. {
  243. DISTRHO_SAFE_ASSERT_RETURN(length > 0,);
  244. if (const size_t len = std::min(std::strlen(src), length-1U))
  245. {
  246. std::memcpy(dst, src, len);
  247. dst[len] = '\0';
  248. }
  249. else
  250. {
  251. dst[0] = '\0';
  252. }
  253. }
  254. void strncpy_utf8(char* const dst, const int16_t* const src, const size_t length)
  255. {
  256. DISTRHO_SAFE_ASSERT_RETURN(length > 0,);
  257. if (const size_t len = std::min(strlen_utf16(src), length-1U))
  258. {
  259. for (size_t i=0; i<len; ++i)
  260. {
  261. // skip non-ascii chars, unsupported
  262. if (src[i] >= 0x80)
  263. continue;
  264. dst[i] = src[i];
  265. }
  266. dst[len] = 0;
  267. }
  268. else
  269. {
  270. dst[0] = 0;
  271. }
  272. }
  273. void strncpy_utf16(int16_t* const dst, const char* const src, const size_t length)
  274. {
  275. DISTRHO_SAFE_ASSERT_RETURN(length > 0,);
  276. if (const size_t len = std::min(std::strlen(src), length-1U))
  277. {
  278. for (size_t i=0; i<len; ++i)
  279. {
  280. // skip non-ascii chars, unsupported
  281. if ((uint8_t)src[i] >= 0x80)
  282. continue;
  283. dst[i] = src[i];
  284. }
  285. dst[len] = 0;
  286. }
  287. else
  288. {
  289. dst[0] = 0;
  290. }
  291. }
  292. // --------------------------------------------------------------------------------------------------------------------
  293. template<typename T>
  294. static void snprintf_t(char* const dst, const T value, const char* const format, const size_t size)
  295. {
  296. DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
  297. std::snprintf(dst, size-1, format, value);
  298. dst[size-1] = '\0';
  299. }
  300. template<typename T>
  301. static void snprintf_utf16_t(int16_t* const dst, const T value, const char* const format, const size_t size)
  302. {
  303. DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
  304. char* const tmpbuf = (char*)std::malloc(size);
  305. DISTRHO_SAFE_ASSERT_RETURN(tmpbuf != nullptr,);
  306. std::snprintf(tmpbuf, size-1, format, value);
  307. tmpbuf[size-1] = '\0';
  308. strncpy_utf16(dst, tmpbuf, size);
  309. std::free(tmpbuf);
  310. }
  311. static inline
  312. void snprintf_u32(char* const dst, const uint32_t value, const size_t size)
  313. {
  314. return snprintf_t<uint32_t>(dst, value, "%u", size);
  315. }
  316. static inline
  317. void snprintf_f32_utf16(int16_t* const dst, const float value, const size_t size)
  318. {
  319. return snprintf_utf16_t<float>(dst, value, "%f", size);
  320. }
  321. static inline
  322. void snprintf_i32_utf16(int16_t* const dst, const int value, const size_t size)
  323. {
  324. return snprintf_utf16_t<int>(dst, value, "%d", size);
  325. }
  326. // --------------------------------------------------------------------------------------------------------------------
  327. // handy way to create a utf16 string from a utf8 one on the current function scope, used for message strings
  328. struct ScopedUTF16String {
  329. int16_t* str;
  330. ScopedUTF16String(const char* const s) noexcept;
  331. ~ScopedUTF16String() noexcept;
  332. operator const int16_t*() const noexcept;
  333. };
  334. // --------------------------------------------------------------------------------------------------------------------
  335. ScopedUTF16String::ScopedUTF16String(const char* const s) noexcept
  336. : str(nullptr)
  337. {
  338. const size_t len = std::strlen(s);
  339. str = static_cast<int16_t*>(std::malloc(sizeof(int16_t) * (len + 1)));
  340. DISTRHO_SAFE_ASSERT_RETURN(str != nullptr,);
  341. strncpy_utf16(str, s, len + 1);
  342. }
  343. ScopedUTF16String::~ScopedUTF16String() noexcept
  344. {
  345. std::free(str);
  346. }
  347. ScopedUTF16String::operator const int16_t*() const noexcept
  348. {
  349. return str;
  350. }
  351. // --------------------------------------------------------------------------------------------------------------------
  352. // handy way to create a utf8 string from a utf16 one on the current function scope
  353. struct ScopedUTF8String {
  354. char str[128];
  355. ScopedUTF8String(const int16_t* const s) noexcept
  356. {
  357. strncpy_utf8(str, s, 128);
  358. }
  359. operator const char*() const noexcept
  360. {
  361. return str;
  362. }
  363. };
  364. // --------------------------------------------------------------------------------------------------------------------
  365. // dpf_plugin_view_create (implemented on UI side)
  366. v3_plugin_view** dpf_plugin_view_create(v3_host_application** host, void* instancePointer, double sampleRate);
  367. // --------------------------------------------------------------------------------------------------------------------
  368. /**
  369. * VST3 DSP class.
  370. *
  371. * All the dynamic things from VST3 get implemented here, free of complex low-level VST3 pointer things.
  372. * The DSP is created during the "initialize" component event, and destroyed during "terminate".
  373. *
  374. * The low-level VST3 stuff comes after.
  375. */
  376. class PluginVst3
  377. {
  378. /* buses: we provide 1 for the main audio (if there is any) plus 1 for each sidechain or cv port.
  379. * Main audio comes first, if available.
  380. * Then sidechain, also if available.
  381. * And finally each CV port individually.
  382. *
  383. * MIDI will have a single bus, nothing special there.
  384. */
  385. struct BusInfo {
  386. uint8_t audio; // either 0 or 1
  387. uint8_t sidechain; // either 0 or 1
  388. uint32_t numMainAudio;
  389. uint32_t numSidechain;
  390. uint32_t numCV;
  391. BusInfo()
  392. : audio(0),
  393. sidechain(0),
  394. numMainAudio(0),
  395. numSidechain(0),
  396. numCV(0) {}
  397. } inputBuses, outputBuses;
  398. public:
  399. PluginVst3(v3_host_application** const host)
  400. : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback),
  401. fComponentHandler(nullptr),
  402. #if DISTRHO_PLUGIN_HAS_UI
  403. # ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  404. fConnectionFromCompToCtrl(nullptr),
  405. # endif
  406. fConnectionFromCtrlToView(nullptr),
  407. fHostApplication(host),
  408. #endif
  409. fParameterCount(fPlugin.getParameterCount()),
  410. fVst3ParameterCount(fParameterCount + kVst3InternalParameterCount),
  411. fCachedParameterValues(nullptr),
  412. fParameterValuesChangedDuringProcessing(nullptr)
  413. #if DISTRHO_PLUGIN_HAS_UI
  414. , fParameterValueChangesForUI(nullptr)
  415. , fConnectedToUI(false)
  416. #endif
  417. #if DISTRHO_PLUGIN_WANT_LATENCY
  418. , fLastKnownLatency(fPlugin.getLatency())
  419. #endif
  420. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  421. , fHostEventOutputHandle(nullptr)
  422. #endif
  423. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  424. , fCurrentProgram(0)
  425. , fProgramCountMinusOne(fPlugin.getProgramCount()-1)
  426. #endif
  427. {
  428. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  429. for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  430. {
  431. const uint32_t hints = fPlugin.getAudioPortHints(true, i);
  432. if (hints & kAudioPortIsCV)
  433. ++inputBuses.numCV;
  434. else
  435. ++inputBuses.numMainAudio;
  436. if (hints & kAudioPortIsSidechain)
  437. ++inputBuses.numSidechain;
  438. }
  439. if (inputBuses.numMainAudio != 0)
  440. inputBuses.audio = 1;
  441. if (inputBuses.numSidechain != 0)
  442. inputBuses.sidechain = 1;
  443. uint32_t cvInputBusId = 0;
  444. for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  445. {
  446. AudioPortWithBusId& port(fPlugin.getAudioPort(true, i));
  447. if (port.hints & kAudioPortIsCV)
  448. port.busId = inputBuses.audio + inputBuses.sidechain + cvInputBusId++;
  449. else if (port.hints & kAudioPortIsSidechain)
  450. port.busId = inputBuses.audio;
  451. else
  452. port.busId = 0;
  453. }
  454. #endif
  455. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  456. for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  457. {
  458. const uint32_t hints = fPlugin.getAudioPortHints(false, i);
  459. if (hints & kAudioPortIsCV)
  460. ++outputBuses.numCV;
  461. else
  462. ++outputBuses.numMainAudio;
  463. if (hints & kAudioPortIsSidechain)
  464. ++outputBuses.numSidechain;
  465. }
  466. if (outputBuses.numMainAudio != 0)
  467. outputBuses.audio = 1;
  468. if (outputBuses.numSidechain != 0)
  469. outputBuses.sidechain = 1;
  470. uint32_t cvOutputBusId = 0;
  471. for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  472. {
  473. AudioPortWithBusId& port(fPlugin.getAudioPort(false, i));
  474. if (port.hints & kAudioPortIsCV)
  475. port.busId = outputBuses.audio + outputBuses.sidechain + cvOutputBusId++;
  476. else if (port.hints & kAudioPortIsSidechain)
  477. port.busId = outputBuses.audio;
  478. else
  479. port.busId = 0;
  480. }
  481. #endif
  482. if (const uint32_t extraParameterCount = fParameterCount + kVst3InternalParameterBaseCount)
  483. {
  484. fCachedParameterValues = new float[extraParameterCount];
  485. fCachedParameterValues[kVst3InternalParameterActive] = 0.0f;
  486. fCachedParameterValues[kVst3InternalParameterBufferSize] = fPlugin.getBufferSize();
  487. fCachedParameterValues[kVst3InternalParameterSampleRate] = fPlugin.getSampleRate();
  488. #if DISTRHO_PLUGIN_WANT_LATENCY
  489. fCachedParameterValues[kVst3InternalParameterLatency] = fLastKnownLatency;
  490. #endif
  491. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  492. fCachedParameterValues[kVst3InternalParameterProgram] = 0.0f;
  493. #endif
  494. for (uint32_t i=0; i < fParameterCount; ++i)
  495. fCachedParameterValues[kVst3InternalParameterBaseCount + i] = fPlugin.getParameterDefault(i);
  496. fParameterValuesChangedDuringProcessing = new bool[extraParameterCount];
  497. std::memset(fParameterValuesChangedDuringProcessing, 0, sizeof(bool)*extraParameterCount);
  498. #if DISTRHO_PLUGIN_HAS_UI
  499. fParameterValueChangesForUI = new bool[extraParameterCount];
  500. std::memset(fParameterValueChangesForUI, 0, sizeof(bool)*extraParameterCount);
  501. #endif
  502. }
  503. #if DISTRHO_PLUGIN_WANT_STATE
  504. for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i)
  505. {
  506. const String& dkey(fPlugin.getStateKey(i));
  507. fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
  508. }
  509. #endif
  510. #if !DISTRHO_PLUGIN_HAS_UI
  511. // unused
  512. return; (void)host;
  513. #endif
  514. }
  515. ~PluginVst3()
  516. {
  517. if (fCachedParameterValues != nullptr)
  518. {
  519. delete[] fCachedParameterValues;
  520. fCachedParameterValues = nullptr;
  521. }
  522. if (fParameterValuesChangedDuringProcessing != nullptr)
  523. {
  524. delete[] fParameterValuesChangedDuringProcessing;
  525. fParameterValuesChangedDuringProcessing = nullptr;
  526. }
  527. #if DISTRHO_PLUGIN_HAS_UI
  528. if (fParameterValueChangesForUI != nullptr)
  529. {
  530. delete[] fParameterValueChangesForUI;
  531. fParameterValueChangesForUI = nullptr;
  532. }
  533. #endif
  534. }
  535. // ----------------------------------------------------------------------------------------------------------------
  536. // utilities and common code
  537. void setNormalizedPluginParameterValue(const uint32_t index, const float normalized)
  538. {
  539. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  540. const uint32_t hints = fPlugin.getParameterHints(index);
  541. float value = ranges.getUnnormalizedValue(normalized);
  542. if (hints & kParameterIsBoolean)
  543. {
  544. const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
  545. value = value > midRange ? ranges.max : ranges.min;
  546. }
  547. else if (hints & kParameterIsInteger)
  548. {
  549. value = std::round(value);
  550. }
  551. fCachedParameterValues[kVst3InternalParameterBaseCount + index] = value;
  552. #if DISTRHO_PLUGIN_HAS_UI
  553. fParameterValueChangesForUI[kVst3InternalParameterBaseCount + index] = true;
  554. #endif
  555. fPlugin.setParameterValue(index, value);
  556. }
  557. // ----------------------------------------------------------------------------------------------------------------
  558. // stuff called for UI creation
  559. void* getInstancePointer() const noexcept
  560. {
  561. return fPlugin.getInstancePointer();
  562. }
  563. double getSampleRate() const noexcept
  564. {
  565. return fPlugin.getSampleRate();
  566. }
  567. // ----------------------------------------------------------------------------------------------------------------
  568. // v3_component interface calls
  569. int32_t getBusCount(const int32_t mediaType, const int32_t busDirection) const noexcept
  570. {
  571. switch (mediaType)
  572. {
  573. case V3_AUDIO:
  574. if (busDirection == V3_INPUT)
  575. return inputBuses.audio + inputBuses.sidechain + inputBuses.numCV;
  576. if (busDirection == V3_OUTPUT)
  577. return outputBuses.audio + outputBuses.sidechain + outputBuses.numCV;
  578. break;
  579. case V3_EVENT:
  580. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  581. if (busDirection == V3_INPUT)
  582. return 1;
  583. #endif
  584. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  585. if (busDirection == V3_OUTPUT)
  586. return 1;
  587. #endif
  588. break;
  589. }
  590. return 0;
  591. }
  592. v3_result getBusInfo(const int32_t mediaType,
  593. const int32_t busDirection,
  594. const int32_t busIndex,
  595. v3_bus_info* const info) const
  596. {
  597. DISTRHO_SAFE_ASSERT_INT_RETURN(mediaType == V3_AUDIO || mediaType == V3_EVENT, mediaType, V3_INVALID_ARG);
  598. DISTRHO_SAFE_ASSERT_INT_RETURN(busDirection == V3_INPUT || busDirection == V3_OUTPUT, busDirection, V3_INVALID_ARG);
  599. DISTRHO_SAFE_ASSERT_INT_RETURN(busIndex >= 0, busIndex, V3_INVALID_ARG);
  600. #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0 || DISTRHO_PLUGIN_WANT_MIDI_INPUT || DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  601. const uint32_t busId = static_cast<uint32_t>(busIndex);
  602. #endif
  603. if (mediaType == V3_AUDIO)
  604. {
  605. #if DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  606. int32_t numChannels;
  607. v3_bus_flags flags;
  608. v3_bus_types busType;
  609. v3_str_128 busName = {};
  610. if (busDirection == V3_INPUT)
  611. {
  612. #if DISTRHO_PLUGIN_NUM_INPUTS > 0
  613. switch (busId)
  614. {
  615. case 0:
  616. if (inputBuses.audio)
  617. {
  618. numChannels = inputBuses.numMainAudio;
  619. busType = V3_MAIN;
  620. flags = V3_DEFAULT_ACTIVE;
  621. break;
  622. }
  623. // fall-through
  624. case 1:
  625. if (inputBuses.sidechain)
  626. {
  627. numChannels = inputBuses.numSidechain;
  628. busType = V3_AUX;
  629. flags = static_cast<v3_bus_flags>(0);
  630. break;
  631. }
  632. // fall-through
  633. default:
  634. numChannels = 1;
  635. busType = V3_AUX;
  636. flags = V3_IS_CONTROL_VOLTAGE;
  637. break;
  638. }
  639. if (busType == V3_MAIN)
  640. {
  641. strncpy_utf16(busName, "Audio Input", 128);
  642. }
  643. else
  644. {
  645. for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_INPUTS; ++i)
  646. {
  647. const AudioPortWithBusId& port(fPlugin.getAudioPort(true, i));
  648. // TODO find port group name for sidechain buses
  649. if (port.busId == busId)
  650. {
  651. strncpy_utf16(busName, port.name, 128);
  652. break;
  653. }
  654. }
  655. }
  656. #else
  657. d_stdout("invalid bus %d", busId);
  658. return V3_INVALID_ARG;
  659. #endif // DISTRHO_PLUGIN_NUM_INPUTS
  660. }
  661. else
  662. {
  663. #if DISTRHO_PLUGIN_NUM_OUTPUTS > 0
  664. switch (busId)
  665. {
  666. case 0:
  667. if (outputBuses.audio)
  668. {
  669. numChannels = outputBuses.numMainAudio;
  670. busType = V3_MAIN;
  671. flags = V3_DEFAULT_ACTIVE;
  672. break;
  673. }
  674. // fall-through
  675. case 1:
  676. if (outputBuses.sidechain)
  677. {
  678. numChannels = outputBuses.numSidechain;
  679. busType = V3_AUX;
  680. flags = static_cast<v3_bus_flags>(0);
  681. break;
  682. }
  683. // fall-through
  684. default:
  685. numChannels = 1;
  686. busType = V3_AUX;
  687. flags = V3_IS_CONTROL_VOLTAGE;
  688. break;
  689. }
  690. if (busType == V3_MAIN)
  691. {
  692. strncpy_utf16(busName, "Audio Output", 128);
  693. }
  694. else
  695. {
  696. for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i)
  697. {
  698. const AudioPortWithBusId& port(fPlugin.getAudioPort(false, i));
  699. // TODO find port group name for sidechain buses
  700. if (port.busId == busId)
  701. {
  702. strncpy_utf16(busName, port.name, 128);
  703. break;
  704. }
  705. }
  706. }
  707. #else
  708. d_stdout("invalid bus %d", busId);
  709. return V3_INVALID_ARG;
  710. #endif // DISTRHO_PLUGIN_NUM_OUTPUTS
  711. }
  712. std::memset(info, 0, sizeof(v3_bus_info));
  713. info->media_type = V3_AUDIO;
  714. info->direction = busDirection;
  715. info->channel_count = numChannels;
  716. std::memcpy(info->bus_name, busName, sizeof(busName));
  717. info->bus_type = busType;
  718. info->flags = flags;
  719. return V3_OK;
  720. #else
  721. d_stdout("invalid bus, line %d", __LINE__);
  722. return V3_INVALID_ARG;
  723. #endif // DISTRHO_PLUGIN_NUM_INPUTS+DISTRHO_PLUGIN_NUM_OUTPUTS
  724. }
  725. else
  726. {
  727. if (busDirection == V3_INPUT)
  728. {
  729. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  730. DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG);
  731. #else
  732. d_stdout("invalid bus, line %d", __LINE__);
  733. return V3_INVALID_ARG;
  734. #endif
  735. }
  736. else
  737. {
  738. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  739. DISTRHO_SAFE_ASSERT_RETURN(busId == 0, V3_INVALID_ARG);
  740. #else
  741. d_stdout("invalid bus, line %d", __LINE__);
  742. return V3_INVALID_ARG;
  743. #endif
  744. }
  745. info->media_type = V3_EVENT;
  746. info->direction = busDirection;
  747. info->channel_count = 1;
  748. strncpy_utf16(info->bus_name, busDirection == V3_INPUT ? "Event/MIDI Input"
  749. : "Event/MIDI Output", 128);
  750. info->bus_type = V3_MAIN;
  751. info->flags = V3_DEFAULT_ACTIVE;
  752. return V3_OK;
  753. }
  754. }
  755. v3_result getRoutingInfo(v3_routing_info*, v3_routing_info*)
  756. {
  757. // TODO
  758. return V3_NOT_IMPLEMENTED;
  759. }
  760. v3_result activateBus(const int32_t /* mediaType */,
  761. const int32_t /* busDirection */,
  762. const int32_t /* busIndex */,
  763. const bool /* state */)
  764. {
  765. // TODO, returning ok to make bitwig happy
  766. return V3_OK;
  767. }
  768. v3_result setActive(const bool active)
  769. {
  770. if (active)
  771. fPlugin.activate();
  772. else
  773. fPlugin.deactivateIfNeeded();
  774. return V3_OK;
  775. }
  776. /* state: we pack pairs of key-value strings each separated by a null/zero byte.
  777. * current-program comes first, then dpf key/value states and then parameters.
  778. * parameters are simply converted to/from strings and floats.
  779. * the parameter symbol is used as the "key", so it is possible to reorder them or even remove and add safely.
  780. * there are markers for begin and end of state and parameters, so they never conflict.
  781. */
  782. v3_result setState(v3_bstream** const stream)
  783. {
  784. #if DISTRHO_PLUGIN_HAS_UI
  785. const bool connectedToUI = fConnectionFromCtrlToView != nullptr && fConnectedToUI;
  786. #endif
  787. String key, value;
  788. bool fillingKey = true; // if filling key or value
  789. char queryingType = 'i'; // can be 'n', 's' or 'p' (none, states, parameters)
  790. char buffer[512], orig;
  791. buffer[sizeof(buffer)-1] = '\xff';
  792. v3_result res;
  793. for (int32_t pos = 0, term = 0, read; term == 0; pos += read)
  794. {
  795. res = v3_cpp_obj(stream)->read(stream, buffer, sizeof(buffer)-1, &read);
  796. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  797. if (read == 0)
  798. return V3_OK;
  799. DISTRHO_SAFE_ASSERT_INT_RETURN(read > 0, read, V3_INTERNAL_ERR);
  800. for (int32_t i = 0; i < read; ++i)
  801. {
  802. // found terminator, stop here
  803. if (buffer[i] == '\xfe')
  804. {
  805. term = 1;
  806. break;
  807. }
  808. // store character at read position
  809. orig = buffer[read];
  810. // place null character to create valid string
  811. buffer[read] = '\0';
  812. // append to temporary vars
  813. if (fillingKey)
  814. key += buffer + i;
  815. else
  816. value += buffer + i;
  817. // increase buffer offset by length of string
  818. i += std::strlen(buffer + i);
  819. // restore read character
  820. buffer[read] = orig;
  821. // if buffer offset points to null, we found the end of a string, lets check
  822. if (buffer[i] == '\0')
  823. {
  824. // special keys
  825. if (key == "__dpf_state_begin__")
  826. {
  827. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n',
  828. queryingType, V3_INTERNAL_ERR);
  829. queryingType = 's';
  830. key.clear();
  831. value.clear();
  832. continue;
  833. }
  834. if (key == "__dpf_state_end__")
  835. {
  836. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 's', queryingType, V3_INTERNAL_ERR);
  837. queryingType = 'n';
  838. key.clear();
  839. value.clear();
  840. continue;
  841. }
  842. if (key == "__dpf_parameters_begin__")
  843. {
  844. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i' || queryingType == 'n',
  845. queryingType, V3_INTERNAL_ERR);
  846. queryingType = 'p';
  847. key.clear();
  848. value.clear();
  849. continue;
  850. }
  851. if (key == "__dpf_parameters_end__")
  852. {
  853. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'p', queryingType, V3_INTERNAL_ERR);
  854. queryingType = 'x';
  855. key.clear();
  856. value.clear();
  857. continue;
  858. }
  859. // no special key, swap between reading real key and value
  860. fillingKey = !fillingKey;
  861. // if there is no value yet keep reading until we have one (TODO check empty values on purpose)
  862. if (value.isEmpty())
  863. continue;
  864. if (key == "__dpf_program__")
  865. {
  866. DISTRHO_SAFE_ASSERT_INT_RETURN(queryingType == 'i', queryingType, V3_INTERNAL_ERR);
  867. queryingType = 'n';
  868. d_stdout("found program '%s'", value.buffer());
  869. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  870. const int program = std::atoi(value.buffer());
  871. DISTRHO_SAFE_ASSERT_CONTINUE(program >= 0);
  872. fCurrentProgram = static_cast<uint32_t>(program);
  873. fPlugin.loadProgram(fCurrentProgram);
  874. # if DISTRHO_PLUGIN_HAS_UI
  875. if (connectedToUI)
  876. {
  877. fParameterValueChangesForUI[kVst3InternalParameterProgram] = false;
  878. sendParameterSetToUI(kVst3InternalParameterProgram, program);
  879. }
  880. # endif
  881. #endif
  882. }
  883. else if (queryingType == 's')
  884. {
  885. d_stdout("found state '%s' '%s'", key.buffer(), value.buffer());
  886. #if DISTRHO_PLUGIN_WANT_STATE
  887. if (fPlugin.wantStateKey(key))
  888. {
  889. fStateMap[key] = value;
  890. fPlugin.setState(key, value);
  891. # if DISTRHO_PLUGIN_HAS_UI
  892. if (connectedToUI)
  893. sendStateSetToUI(key, value);
  894. # endif
  895. }
  896. #endif
  897. }
  898. else if (queryingType == 'p')
  899. {
  900. d_stdout("found parameter '%s' '%s'", key.buffer(), value.buffer());
  901. float fvalue;
  902. // find parameter with this symbol, and set its value
  903. for (uint32_t j=0; j < fParameterCount; ++j)
  904. {
  905. if (fPlugin.isParameterOutputOrTrigger(j))
  906. continue;
  907. if (fPlugin.getParameterSymbol(j) != key)
  908. continue;
  909. if (fPlugin.getParameterHints(j) & kParameterIsInteger)
  910. fvalue = std::atoi(value.buffer());
  911. else
  912. fvalue = std::atof(value.buffer());
  913. // TODO atoi if integer
  914. fCachedParameterValues[kVst3InternalParameterBaseCount + j] = fvalue;
  915. #if DISTRHO_PLUGIN_HAS_UI
  916. if (connectedToUI)
  917. {
  918. // UI parameter updates are handled outside the read loop (after host param restart)
  919. fParameterValueChangesForUI[kVst3InternalParameterBaseCount + j] = true;
  920. }
  921. #endif
  922. fPlugin.setParameterValue(j, fvalue);
  923. break;
  924. }
  925. }
  926. key.clear();
  927. value.clear();
  928. }
  929. }
  930. }
  931. if (fComponentHandler != nullptr)
  932. v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, V3_RESTART_PARAM_VALUES_CHANGED);
  933. #if DISTRHO_PLUGIN_HAS_UI
  934. if (connectedToUI)
  935. {
  936. for (uint32_t i=0; i<fParameterCount; ++i)
  937. {
  938. if (fPlugin.isParameterOutputOrTrigger(i))
  939. continue;
  940. fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = false;
  941. sendParameterSetToUI(kVst3InternalParameterBaseCount + i,
  942. fCachedParameterValues[kVst3InternalParameterBaseCount + i]);
  943. }
  944. }
  945. #endif
  946. return V3_OK;
  947. }
  948. v3_result getState(v3_bstream** const stream)
  949. {
  950. const uint32_t paramCount = fPlugin.getParameterCount();
  951. #if DISTRHO_PLUGIN_WANT_STATE
  952. const uint32_t stateCount = fPlugin.getStateCount();
  953. #else
  954. const uint32_t stateCount = 0;
  955. #endif
  956. if (stateCount == 0 && paramCount == 0)
  957. {
  958. char buffer = '\0';
  959. int32_t ignored;
  960. return v3_cpp_obj(stream)->write(stream, &buffer, 1, &ignored);
  961. }
  962. #if DISTRHO_PLUGIN_WANT_FULL_STATE
  963. // Update current state
  964. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  965. {
  966. const String& key = cit->first;
  967. fStateMap[key] = fPlugin.getState(key);
  968. }
  969. #endif
  970. String state;
  971. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  972. {
  973. String tmpStr("__dpf_program__\xff");
  974. tmpStr += String(fCurrentProgram);
  975. tmpStr += "\xff";
  976. state += tmpStr;
  977. }
  978. #endif
  979. #if DISTRHO_PLUGIN_WANT_STATE
  980. if (stateCount != 0)
  981. {
  982. state += "__dpf_state_begin__\xff";
  983. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  984. {
  985. const String& key = cit->first;
  986. const String& value = cit->second;
  987. // join key and value
  988. String tmpStr;
  989. tmpStr = key;
  990. tmpStr += "\xff";
  991. tmpStr += value;
  992. tmpStr += "\xff";
  993. state += tmpStr;
  994. }
  995. state += "__dpf_state_end__\xff";
  996. }
  997. #endif
  998. if (paramCount != 0)
  999. {
  1000. state += "__dpf_parameters_begin__\xff";
  1001. for (uint32_t i=0; i<paramCount; ++i)
  1002. {
  1003. if (fPlugin.isParameterOutputOrTrigger(i))
  1004. continue;
  1005. // join key and value
  1006. String tmpStr;
  1007. tmpStr = fPlugin.getParameterSymbol(i);
  1008. tmpStr += "\xff";
  1009. // TODO cast to integer if needed
  1010. tmpStr += String(fPlugin.getParameterValue(i));
  1011. tmpStr += "\xff";
  1012. state += tmpStr;
  1013. }
  1014. state += "__dpf_parameters_end__\xff";
  1015. }
  1016. // terminator
  1017. state += "\xfe";
  1018. state.replace('\xff', '\0');
  1019. // now saving state, carefully until host written bytes matches full state size
  1020. const char* buffer = state.buffer();
  1021. const int32_t size = static_cast<int32_t>(state.length())+1;
  1022. v3_result res;
  1023. for (int32_t wrtntotal = 0, wrtn; wrtntotal < size; wrtntotal += wrtn)
  1024. {
  1025. wrtn = 0;
  1026. res = v3_cpp_obj(stream)->write(stream, const_cast<char*>(buffer), size - wrtntotal, &wrtn);
  1027. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1028. DISTRHO_SAFE_ASSERT_INT_RETURN(wrtn > 0, wrtn, V3_INTERNAL_ERR);
  1029. }
  1030. return V3_OK;
  1031. }
  1032. // ----------------------------------------------------------------------------------------------------------------
  1033. // v3_audio_processor interface calls
  1034. v3_result setBusArrangements(v3_speaker_arrangement* /*inputs*/, const int32_t /*numInputs*/,
  1035. v3_speaker_arrangement* /*outputs*/, const int32_t /*numOutputs*/)
  1036. {
  1037. // TODO
  1038. return V3_NOT_IMPLEMENTED;
  1039. }
  1040. v3_result getBusArrangement(const int32_t direction, const int32_t /*idx*/, v3_speaker_arrangement*)
  1041. {
  1042. switch (direction)
  1043. {
  1044. case V3_INPUT:
  1045. case V3_OUTPUT:
  1046. // TODO
  1047. return V3_NOT_IMPLEMENTED;
  1048. }
  1049. return V3_INVALID_ARG;
  1050. }
  1051. uint32_t getLatencySamples() const noexcept
  1052. {
  1053. #if DISTRHO_PLUGIN_WANT_LATENCY
  1054. return fPlugin.getLatency();
  1055. #else
  1056. return 0;
  1057. #endif
  1058. }
  1059. v3_result setupProcessing(v3_process_setup* const setup)
  1060. {
  1061. DISTRHO_SAFE_ASSERT_RETURN(setup->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG);
  1062. const bool active = fPlugin.isActive();
  1063. fPlugin.deactivateIfNeeded();
  1064. // TODO process_mode can be V3_REALTIME, V3_PREFETCH, V3_OFFLINE
  1065. fPlugin.setSampleRate(setup->sample_rate, true);
  1066. fPlugin.setBufferSize(setup->max_block_size, true);
  1067. fCachedParameterValues[kVst3InternalParameterBufferSize] = setup->max_block_size;
  1068. fParameterValuesChangedDuringProcessing[kVst3InternalParameterBufferSize] = true;
  1069. fCachedParameterValues[kVst3InternalParameterSampleRate] = setup->sample_rate;
  1070. fParameterValuesChangedDuringProcessing[kVst3InternalParameterSampleRate] = true;
  1071. #if DISTRHO_PLUGIN_HAS_UI
  1072. fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = true;
  1073. #endif
  1074. if (active)
  1075. fPlugin.activate();
  1076. // TODO create dummy buffer of max_block_size length, to use for disabled buses
  1077. return V3_OK;
  1078. }
  1079. v3_result setProcessing(const bool processing)
  1080. {
  1081. if (processing)
  1082. {
  1083. if (! fPlugin.isActive())
  1084. fPlugin.activate();
  1085. }
  1086. else
  1087. {
  1088. fPlugin.deactivateIfNeeded();
  1089. }
  1090. fCachedParameterValues[kVst3InternalParameterActive] = processing ? 1.0f : 0.0f;
  1091. fParameterValuesChangedDuringProcessing[kVst3InternalParameterActive] = true;
  1092. return V3_OK;
  1093. }
  1094. v3_result process(v3_process_data* const data)
  1095. {
  1096. DISTRHO_SAFE_ASSERT_RETURN(data->symbolic_sample_size == V3_SAMPLE_32, V3_INVALID_ARG);
  1097. // d_stdout("process %i", data->symbolic_sample_size);
  1098. // activate plugin if not done yet
  1099. if (! fPlugin.isActive())
  1100. fPlugin.activate();
  1101. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  1102. if (v3_process_context* const ctx = data->ctx)
  1103. {
  1104. fTimePosition.playing = ctx->state & V3_PROCESS_CTX_PLAYING;
  1105. fTimePosition.bbt.valid = ctx->state & (V3_PROCESS_CTX_TEMPO_VALID|V3_PROCESS_CTX_TIME_SIG_VALID);
  1106. // ticksPerBeat is not possible with VST3
  1107. fTimePosition.bbt.ticksPerBeat = 1920.0;
  1108. if (ctx->state & V3_PROCESS_CTX_CONT_TIME_VALID)
  1109. fTimePosition.frame = ctx->continuous_time_in_samples;
  1110. else
  1111. fTimePosition.frame = ctx->project_time_in_samples;
  1112. if (ctx->state & V3_PROCESS_CTX_TEMPO_VALID)
  1113. fTimePosition.bbt.beatsPerMinute = ctx->bpm;
  1114. else
  1115. fTimePosition.bbt.beatsPerMinute = 120.0;
  1116. if (ctx->state & (V3_PROCESS_CTX_PROJECT_TIME_VALID|V3_PROCESS_CTX_TIME_SIG_VALID))
  1117. {
  1118. const double ppqPos = std::abs(ctx->project_time_quarters);
  1119. const int ppqPerBar = ctx->time_sig_numerator * 4 / ctx->time_sig_denom;
  1120. const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * ctx->time_sig_numerator;
  1121. const double rest = std::fmod(barBeats, 1.0);
  1122. fTimePosition.bbt.bar = static_cast<int32_t>(ppqPos) / ppqPerBar + 1;
  1123. fTimePosition.bbt.beat = static_cast<int32_t>(barBeats - rest + 0.5) + 1;
  1124. fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat;
  1125. fTimePosition.bbt.beatsPerBar = ctx->time_sig_numerator;
  1126. fTimePosition.bbt.beatType = ctx->time_sig_denom;
  1127. if (ctx->project_time_quarters < 0.0)
  1128. {
  1129. --fTimePosition.bbt.bar;
  1130. fTimePosition.bbt.beat = ctx->time_sig_numerator - fTimePosition.bbt.beat + 1;
  1131. fTimePosition.bbt.tick = fTimePosition.bbt.ticksPerBeat - fTimePosition.bbt.tick - 1;
  1132. }
  1133. }
  1134. else
  1135. {
  1136. fTimePosition.bbt.bar = 1;
  1137. fTimePosition.bbt.beat = 1;
  1138. fTimePosition.bbt.tick = 0.0;
  1139. fTimePosition.bbt.beatsPerBar = 4.0f;
  1140. fTimePosition.bbt.beatType = 4.0f;
  1141. }
  1142. fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*
  1143. fTimePosition.bbt.beatsPerBar*
  1144. (fTimePosition.bbt.bar-1);
  1145. fPlugin.setTimePosition(fTimePosition);
  1146. }
  1147. #endif
  1148. if (data->nframes <= 0)
  1149. {
  1150. updateParametersFromProcessing(data->output_params, 0);
  1151. return V3_OK;
  1152. }
  1153. const float* inputs[DISTRHO_PLUGIN_NUM_INPUTS != 0 ? DISTRHO_PLUGIN_NUM_INPUTS : 1];
  1154. /* */ float* outputs[DISTRHO_PLUGIN_NUM_OUTPUTS != 0 ? DISTRHO_PLUGIN_NUM_OUTPUTS : 1];
  1155. {
  1156. int32_t i = 0;
  1157. if (data->inputs != nullptr)
  1158. {
  1159. for (; i < data->inputs->num_channels; ++i)
  1160. {
  1161. DISTRHO_SAFE_ASSERT_INT_BREAK(i < DISTRHO_PLUGIN_NUM_INPUTS, i);
  1162. inputs[i] = data->inputs->channel_buffers_32[i];
  1163. }
  1164. }
  1165. for (; i < std::max(1, DISTRHO_PLUGIN_NUM_INPUTS); ++i)
  1166. inputs[i] = nullptr; // TODO use dummy buffer
  1167. }
  1168. {
  1169. int32_t i = 0;
  1170. if (data->outputs != nullptr)
  1171. {
  1172. for (; i < data->outputs->num_channels; ++i)
  1173. {
  1174. DISTRHO_SAFE_ASSERT_INT_BREAK(i < DISTRHO_PLUGIN_NUM_OUTPUTS, i);
  1175. outputs[i] = data->outputs->channel_buffers_32[i];
  1176. }
  1177. }
  1178. for (; i < std::max(1, DISTRHO_PLUGIN_NUM_OUTPUTS); ++i)
  1179. outputs[i] = nullptr; // TODO use dummy buffer
  1180. }
  1181. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  1182. fHostEventOutputHandle = data->output_events;
  1183. #endif
  1184. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1185. uint32_t midiEventCount = 0;
  1186. # if DISTRHO_PLUGIN_HAS_UI
  1187. while (fNotesRingBuffer.isDataAvailableForReading())
  1188. {
  1189. uint8_t midiData[3];
  1190. if (! fNotesRingBuffer.readCustomData(midiData, 3))
  1191. break;
  1192. MidiEvent& midiEvent(fMidiEvents[midiEventCount++]);
  1193. midiEvent.frame = 0;
  1194. midiEvent.size = 3;
  1195. std::memcpy(midiEvent.data, midiData, 3);
  1196. if (midiEventCount == kMaxMidiEvents)
  1197. break;
  1198. }
  1199. # endif
  1200. if (v3_event_list** const eventptr = data->input_events)
  1201. {
  1202. v3_event event;
  1203. for (uint32_t i = 0, count = v3_cpp_obj(eventptr)->get_event_count(eventptr); i < count; ++i)
  1204. {
  1205. if (v3_cpp_obj(eventptr)->get_event(eventptr, i, &event) != V3_OK)
  1206. break;
  1207. // check if event can be encoded as MIDI
  1208. switch (event.type)
  1209. {
  1210. case V3_EVENT_NOTE_ON:
  1211. case V3_EVENT_NOTE_OFF:
  1212. // case V3_EVENT_DATA:
  1213. case V3_EVENT_POLY_PRESSURE:
  1214. break;
  1215. // case V3_EVENT_NOTE_EXP_VALUE:
  1216. // case V3_EVENT_NOTE_EXP_TEXT:
  1217. // case V3_EVENT_CHORD:
  1218. // case V3_EVENT_SCALE:
  1219. // case V3_EVENT_LEGACY_MIDI_CC_OUT:
  1220. default:
  1221. continue;
  1222. }
  1223. MidiEvent& midiEvent(fMidiEvents[midiEventCount++]);
  1224. midiEvent.frame = event.sample_offset;
  1225. // encode event as MIDI
  1226. switch (event.type)
  1227. {
  1228. case V3_EVENT_NOTE_ON:
  1229. midiEvent.size = 3;
  1230. midiEvent.data[0] = 0x90 | (event.note_on.channel & 0xf);
  1231. midiEvent.data[1] = event.note_on.pitch;
  1232. midiEvent.data[2] = std::max(0, std::min(127, (int)(event.note_on.velocity * 127)));
  1233. midiEvent.data[3] = 0;
  1234. break;
  1235. case V3_EVENT_NOTE_OFF:
  1236. midiEvent.size = 3;
  1237. midiEvent.data[0] = 0x80 | (event.note_off.channel & 0xf);
  1238. midiEvent.data[1] = event.note_off.pitch;
  1239. midiEvent.data[2] = std::max(0, std::min(127, (int)(event.note_off.velocity * 127)));
  1240. midiEvent.data[3] = 0;
  1241. break;
  1242. case V3_EVENT_POLY_PRESSURE:
  1243. midiEvent.size = 3;
  1244. midiEvent.data[0] = 0xA0 | (event.poly_pressure.channel & 0xf);
  1245. midiEvent.data[1] = event.poly_pressure.pitch;
  1246. midiEvent.data[2] = std::max(0, std::min(127, (int)(event.poly_pressure.pressure * 127)));
  1247. midiEvent.data[3] = 0;
  1248. break;
  1249. default:
  1250. midiEvent.size = 0;
  1251. break;
  1252. }
  1253. if (midiEventCount == kMaxMidiEvents)
  1254. break;
  1255. }
  1256. }
  1257. // TODO append parameter MIDI events in a sorted way
  1258. /*
  1259. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1260. if (rindex == 0)
  1261. continue;
  1262. --rindex;
  1263. #endif
  1264. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1265. MidiEvent& midiEvent(fMidiEvents[midiEventCount++]);
  1266. midiEvent.frame = offset;
  1267. midiEvent.size = 3;
  1268. midiEvent.data[0] = (rindex / 130) & 0xf;
  1269. switch (rindex)
  1270. {
  1271. case 128: // channel pressure
  1272. midiEvent.data[0] |= 0xD0;
  1273. midiEvent.data[1] = std::max(0, std::min(127, (int)(value * 127)));
  1274. midiEvent.data[2] = 0;
  1275. midiEvent.data[3] = 0;
  1276. break;
  1277. case 129: // pitchbend
  1278. midiEvent.data[0] |= 0xE0;
  1279. midiEvent.data[1] = std::max(0, std::min(16384, (int)(value * 16384))) & 0x7f;
  1280. midiEvent.data[2] = std::max(0, std::min(16384, (int)(value * 16384))) >> 7;
  1281. midiEvent.data[3] = 0;
  1282. break;
  1283. default:
  1284. midiEvent.data[0] |= 0xB0;
  1285. midiEvent.data[1] = rindex % 130;
  1286. midiEvent.data[2] = std::max(0, std::min(127, (int)(value * 127)));
  1287. midiEvent.data[3] = 0;
  1288. break;
  1289. }
  1290. if (midiEventCount == kMaxMidiEvents)
  1291. break;
  1292. }
  1293. #endif
  1294. */
  1295. #endif
  1296. // if there are any parameter changes at frame 0, set them here
  1297. if (v3_param_changes** const inparamsptr = data->input_params)
  1298. {
  1299. int32_t offset;
  1300. double value;
  1301. for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i)
  1302. {
  1303. v3_param_value_queue** const queue = v3_cpp_obj(inparamsptr)->get_param_data(inparamsptr, i);
  1304. DISTRHO_SAFE_ASSERT_BREAK(queue != nullptr);
  1305. const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue);
  1306. DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex);
  1307. if (rindex < kVst3InternalParameterCount)
  1308. continue;
  1309. if (v3_cpp_obj(queue)->get_point_count(queue) <= 0)
  1310. continue;
  1311. if (v3_cpp_obj(queue)->get_point(queue, 0, &offset, &value) != V3_OK)
  1312. break;
  1313. if (offset != 0)
  1314. continue;
  1315. const uint32_t index = rindex - kVst3InternalParameterCount;
  1316. setNormalizedPluginParameterValue(index, value);
  1317. }
  1318. }
  1319. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1320. fPlugin.run(inputs, outputs, data->nframes, fMidiEvents, midiEventCount);
  1321. #else
  1322. fPlugin.run(inputs, outputs, data->nframes);
  1323. #endif
  1324. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  1325. fHostEventOutputHandle = nullptr;
  1326. #endif
  1327. // if there are any parameter changes after frame 0, set them here
  1328. if (v3_param_changes** const inparamsptr = data->input_params)
  1329. {
  1330. int32_t offset;
  1331. double value;
  1332. for (int32_t i = 0, count = v3_cpp_obj(inparamsptr)->get_param_count(inparamsptr); i < count; ++i)
  1333. {
  1334. v3_param_value_queue** const queue = v3_cpp_obj(inparamsptr)->get_param_data(inparamsptr, i);
  1335. DISTRHO_SAFE_ASSERT_BREAK(queue != nullptr);
  1336. const v3_param_id rindex = v3_cpp_obj(queue)->get_param_id(queue);
  1337. DISTRHO_SAFE_ASSERT_UINT_BREAK(rindex < fVst3ParameterCount, rindex);
  1338. if (rindex < kVst3InternalParameterCount)
  1339. continue;
  1340. const int32_t pcount = v3_cpp_obj(queue)->get_point_count(queue);
  1341. if (pcount <= 0)
  1342. continue;
  1343. if (v3_cpp_obj(queue)->get_point(queue, pcount - 1, &offset, &value) != V3_OK)
  1344. break;
  1345. if (offset == 0)
  1346. continue;
  1347. const uint32_t index = rindex - kVst3InternalParameterCount;
  1348. setNormalizedPluginParameterValue(index, value);
  1349. }
  1350. }
  1351. updateParametersFromProcessing(data->output_params, data->nframes - 1);
  1352. return V3_OK;
  1353. }
  1354. uint32_t getTailSamples() const noexcept
  1355. {
  1356. return 0;
  1357. }
  1358. // ----------------------------------------------------------------------------------------------------------------
  1359. // v3_edit_controller interface calls
  1360. int32_t getParameterCount() const noexcept
  1361. {
  1362. return fVst3ParameterCount;
  1363. }
  1364. v3_result getParameterInfo(const int32_t rindex, v3_param_info* const info) const noexcept
  1365. {
  1366. std::memset(info, 0, sizeof(v3_param_info));
  1367. DISTRHO_SAFE_ASSERT_RETURN(rindex >= 0, V3_INVALID_ARG);
  1368. // TODO hash the parameter symbol
  1369. info->param_id = rindex;
  1370. switch (rindex)
  1371. {
  1372. case kVst3InternalParameterActive:
  1373. info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN;
  1374. info->step_count = 1;
  1375. strncpy_utf16(info->title, "Active", 128);
  1376. strncpy_utf16(info->short_title, "Active", 128);
  1377. return V3_OK;
  1378. case kVst3InternalParameterBufferSize:
  1379. info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN;
  1380. info->step_count = DPF_VST3_MAX_BUFFER_SIZE - 1;
  1381. strncpy_utf16(info->title, "Buffer Size", 128);
  1382. strncpy_utf16(info->short_title, "Buffer Size", 128);
  1383. strncpy_utf16(info->units, "frames", 128);
  1384. return V3_OK;
  1385. case kVst3InternalParameterSampleRate:
  1386. info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN;
  1387. strncpy_utf16(info->title, "Sample Rate", 128);
  1388. strncpy_utf16(info->short_title, "Sample Rate", 128);
  1389. strncpy_utf16(info->units, "frames", 128);
  1390. return V3_OK;
  1391. #if DISTRHO_PLUGIN_WANT_LATENCY
  1392. case kVst3InternalParameterLatency:
  1393. info->flags = V3_PARAM_READ_ONLY | V3_PARAM_IS_HIDDEN;
  1394. strncpy_utf16(info->title, "Latency", 128);
  1395. strncpy_utf16(info->short_title, "Latency", 128);
  1396. strncpy_utf16(info->units, "frames", 128);
  1397. return V3_OK;
  1398. #endif
  1399. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1400. case kVst3InternalParameterProgram:
  1401. info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_LIST | V3_PARAM_PROGRAM_CHANGE | V3_PARAM_IS_HIDDEN;
  1402. info->step_count = fProgramCountMinusOne;
  1403. strncpy_utf16(info->title, "Current Program", 128);
  1404. strncpy_utf16(info->short_title, "Program", 128);
  1405. return V3_OK;
  1406. #endif
  1407. }
  1408. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1409. if (rindex < kVst3InternalParameterCount)
  1410. {
  1411. const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterMidiCC_start);
  1412. info->flags = V3_PARAM_CAN_AUTOMATE | V3_PARAM_IS_HIDDEN;
  1413. info->step_count = 127;
  1414. char ccstr[24];
  1415. snprintf(ccstr, sizeof(ccstr)-1, "MIDI Ch. %d CC %d", index / 130 + 1, index % 130);
  1416. strncpy_utf16(info->title, ccstr, 128);
  1417. snprintf(ccstr, sizeof(ccstr)-1, "Ch.%d CC%d", index / 130 + 1, index % 130);
  1418. strncpy_utf16(info->short_title, ccstr+5, 128);
  1419. return V3_OK;
  1420. }
  1421. #endif
  1422. const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
  1423. DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG);
  1424. // set up flags
  1425. int32_t flags = 0;
  1426. const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
  1427. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  1428. const uint32_t hints = fPlugin.getParameterHints(index);
  1429. switch (fPlugin.getParameterDesignation(index))
  1430. {
  1431. case kParameterDesignationNull:
  1432. break;
  1433. case kParameterDesignationBypass:
  1434. flags |= V3_PARAM_IS_BYPASS;
  1435. break;
  1436. }
  1437. if (hints & kParameterIsAutomatable)
  1438. flags |= V3_PARAM_CAN_AUTOMATE;
  1439. if (hints & kParameterIsOutput)
  1440. flags |= V3_PARAM_READ_ONLY;
  1441. // set up step_count
  1442. int32_t step_count = 0;
  1443. if (hints & kParameterIsBoolean)
  1444. step_count = 1;
  1445. else if (hints & kParameterIsInteger)
  1446. step_count = ranges.max - ranges.min;
  1447. if (enumValues.count >= 2 && enumValues.restrictedMode)
  1448. {
  1449. flags |= V3_PARAM_IS_LIST;
  1450. step_count = enumValues.count - 1;
  1451. }
  1452. info->flags = flags;
  1453. info->step_count = step_count;
  1454. info->default_normalised_value = ranges.getNormalizedValue(ranges.def);
  1455. // int32_t unit_id;
  1456. strncpy_utf16(info->title, fPlugin.getParameterName(index), 128);
  1457. strncpy_utf16(info->short_title, fPlugin.getParameterShortName(index), 128);
  1458. strncpy_utf16(info->units, fPlugin.getParameterUnit(index), 128);
  1459. return V3_OK;
  1460. }
  1461. v3_result getParameterStringForValue(const v3_param_id rindex, const double normalized, v3_str_128 output)
  1462. {
  1463. DISTRHO_SAFE_ASSERT_RETURN(normalized >= 0.0 && normalized <= 1.0, V3_INVALID_ARG);
  1464. switch (rindex)
  1465. {
  1466. case kVst3InternalParameterActive:
  1467. strncpy_utf16(output, normalized > 0.5 ? "On" : "Off", 128);
  1468. return V3_OK;
  1469. case kVst3InternalParameterBufferSize:
  1470. snprintf_i32_utf16(output, static_cast<int>(normalized * DPF_VST3_MAX_BUFFER_SIZE + 0.5), 128);
  1471. return V3_OK;
  1472. case kVst3InternalParameterSampleRate:
  1473. snprintf_f32_utf16(output, std::round(normalized * DPF_VST3_MAX_SAMPLE_RATE), 128);
  1474. return V3_OK;
  1475. #if DISTRHO_PLUGIN_WANT_LATENCY
  1476. case kVst3InternalParameterLatency:
  1477. snprintf_f32_utf16(output, std::round(normalized * DPF_VST3_MAX_LATENCY), 128);
  1478. return V3_OK;
  1479. #endif
  1480. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1481. case kVst3InternalParameterProgram:
  1482. const uint32_t program = std::round(normalized * fProgramCountMinusOne);
  1483. strncpy_utf16(output, fPlugin.getProgramName(program), 128);
  1484. return V3_OK;
  1485. #endif
  1486. }
  1487. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1488. if (rindex < kVst3InternalParameterCount)
  1489. {
  1490. snprintf_f32_utf16(output, std::round(normalized * 127), 128);
  1491. return V3_OK;
  1492. }
  1493. #endif
  1494. const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
  1495. DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG);
  1496. const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
  1497. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  1498. const uint32_t hints = fPlugin.getParameterHints(index);
  1499. float value = ranges.getUnnormalizedValue(normalized);
  1500. if (hints & kParameterIsBoolean)
  1501. {
  1502. const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
  1503. value = value > midRange ? ranges.max : ranges.min;
  1504. }
  1505. else if (hints & kParameterIsInteger)
  1506. {
  1507. value = std::round(value);
  1508. }
  1509. for (uint32_t i=0; i < enumValues.count; ++i)
  1510. {
  1511. if (d_isEqual(enumValues.values[i].value, value))
  1512. {
  1513. strncpy_utf16(output, enumValues.values[i].label, 128);
  1514. return V3_OK;
  1515. }
  1516. }
  1517. if (hints & kParameterIsInteger)
  1518. snprintf_i32_utf16(output, value, 128);
  1519. else
  1520. snprintf_f32_utf16(output, value, 128);
  1521. return V3_OK;
  1522. }
  1523. v3_result getParameterValueForString(const v3_param_id rindex, int16_t* const input, double* const output)
  1524. {
  1525. switch (rindex)
  1526. {
  1527. case kVst3InternalParameterActive:
  1528. *output = strcmp_utf16(input, "On") ? 1.0 : 0.0;
  1529. return V3_OK;
  1530. case kVst3InternalParameterBufferSize:
  1531. *output = static_cast<double>(std::atoi(ScopedUTF8String(input))) / DPF_VST3_MAX_BUFFER_SIZE;
  1532. return V3_OK;
  1533. case kVst3InternalParameterSampleRate:
  1534. *output = std::atof(ScopedUTF8String(input)) / DPF_VST3_MAX_SAMPLE_RATE;
  1535. return V3_OK;
  1536. #if DISTRHO_PLUGIN_WANT_LATENCY
  1537. case kVst3InternalParameterLatency:
  1538. *output = std::atof(ScopedUTF8String(input)) / DPF_VST3_MAX_LATENCY;
  1539. return V3_OK;
  1540. #endif
  1541. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1542. case kVst3InternalParameterProgram:
  1543. for (uint32_t i=0, count=fPlugin.getProgramCount(); i < count; ++i)
  1544. {
  1545. if (strcmp_utf16(input, fPlugin.getProgramName(i)))
  1546. {
  1547. *output = static_cast<double>(i) / static_cast<double>(fProgramCountMinusOne);
  1548. return V3_OK;
  1549. }
  1550. }
  1551. return V3_INVALID_ARG;
  1552. #endif
  1553. }
  1554. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1555. if (rindex < kVst3InternalParameterCount)
  1556. {
  1557. // TODO find CC/channel based on name
  1558. return V3_NOT_IMPLEMENTED;
  1559. }
  1560. #endif
  1561. const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
  1562. DISTRHO_SAFE_ASSERT_UINT_RETURN(index < fParameterCount, index, V3_INVALID_ARG);
  1563. const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
  1564. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  1565. for (uint32_t i=0; i < enumValues.count; ++i)
  1566. {
  1567. if (strcmp_utf16(input, enumValues.values[i].label))
  1568. {
  1569. *output = ranges.getNormalizedValue(enumValues.values[i].value);
  1570. return V3_OK;
  1571. }
  1572. }
  1573. const ScopedUTF8String input8(input);
  1574. float value;
  1575. if (fPlugin.getParameterHints(index) & kParameterIsInteger)
  1576. value = std::atoi(input8);
  1577. else
  1578. value = std::atof(input8);
  1579. *output = ranges.getNormalizedValue(value);
  1580. return V3_OK;
  1581. }
  1582. double normalizedParameterToPlain(const v3_param_id rindex, const double normalized)
  1583. {
  1584. DISTRHO_SAFE_ASSERT_RETURN(normalized >= 0.0 && normalized <= 1.0, 0.0);
  1585. switch (rindex)
  1586. {
  1587. case kVst3InternalParameterActive:
  1588. return normalized > 0.5 ? 1.0 : 0.0;
  1589. case kVst3InternalParameterBufferSize:
  1590. return std::round(normalized * DPF_VST3_MAX_BUFFER_SIZE);
  1591. case kVst3InternalParameterSampleRate:
  1592. return normalized * DPF_VST3_MAX_SAMPLE_RATE;
  1593. #if DISTRHO_PLUGIN_WANT_LATENCY
  1594. case kVst3InternalParameterLatency:
  1595. return normalized * DPF_VST3_MAX_LATENCY;
  1596. #endif
  1597. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1598. case kVst3InternalParameterProgram:
  1599. return std::round(normalized * fProgramCountMinusOne);
  1600. #endif
  1601. }
  1602. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1603. if (rindex < kVst3InternalParameterCount)
  1604. return std::round(normalized * 127);
  1605. #endif
  1606. const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
  1607. DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0);
  1608. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  1609. const uint32_t hints = fPlugin.getParameterHints(index);
  1610. float value = ranges.getUnnormalizedValue(normalized);
  1611. if (hints & kParameterIsBoolean)
  1612. {
  1613. const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
  1614. value = value > midRange ? ranges.max : ranges.min;
  1615. }
  1616. else if (hints & kParameterIsInteger)
  1617. {
  1618. value = std::round(value);
  1619. }
  1620. return value;
  1621. }
  1622. double plainParameterToNormalized(const v3_param_id rindex, const double plain)
  1623. {
  1624. switch (rindex)
  1625. {
  1626. case kVst3InternalParameterActive:
  1627. return plain;
  1628. case kVst3InternalParameterBufferSize:
  1629. return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_BUFFER_SIZE));
  1630. case kVst3InternalParameterSampleRate:
  1631. return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_SAMPLE_RATE));
  1632. #if DISTRHO_PLUGIN_WANT_LATENCY
  1633. case kVst3InternalParameterLatency:
  1634. return std::max(0.0, std::min(1.0, plain / DPF_VST3_MAX_LATENCY));
  1635. #endif
  1636. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1637. case kVst3InternalParameterProgram:
  1638. return std::max(0.0, std::min(1.0, plain / fProgramCountMinusOne));
  1639. #endif
  1640. }
  1641. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1642. if (rindex < kVst3InternalParameterCount)
  1643. return std::max(0.0, std::min(1.0, plain / 127));
  1644. #endif
  1645. const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
  1646. DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0);
  1647. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  1648. return ranges.getNormalizedValue(plain);
  1649. }
  1650. double getParameterNormalized(const v3_param_id rindex)
  1651. {
  1652. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1653. // TODO something to do here?
  1654. if (rindex >= kVst3InternalParameterMidiCC_start && rindex <= kVst3InternalParameterMidiCC_end)
  1655. return 0.0;
  1656. #endif
  1657. switch (rindex)
  1658. {
  1659. case kVst3InternalParameterActive:
  1660. case kVst3InternalParameterBufferSize:
  1661. case kVst3InternalParameterSampleRate:
  1662. #if DISTRHO_PLUGIN_WANT_LATENCY
  1663. case kVst3InternalParameterLatency:
  1664. #endif
  1665. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1666. case kVst3InternalParameterProgram:
  1667. #endif
  1668. return plainParameterToNormalized(rindex, fCachedParameterValues[rindex]);
  1669. }
  1670. const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
  1671. DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, 0.0);
  1672. const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
  1673. return ranges.getNormalizedValue(fCachedParameterValues[kVst3InternalParameterBaseCount + index]);
  1674. }
  1675. v3_result setParameterNormalized(const v3_param_id rindex, const double normalized)
  1676. {
  1677. DISTRHO_SAFE_ASSERT_RETURN(normalized >= 0.0 && normalized <= 1.0, V3_INVALID_ARG);
  1678. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1679. // TODO something to do here?
  1680. if (rindex >= kVst3InternalParameterMidiCC_start && rindex <= kVst3InternalParameterMidiCC_end)
  1681. return V3_INVALID_ARG;
  1682. #endif
  1683. if (rindex < kVst3InternalParameterBaseCount)
  1684. {
  1685. fCachedParameterValues[rindex] = normalizedParameterToPlain(rindex, normalized);
  1686. int flags = 0;
  1687. switch (rindex)
  1688. {
  1689. case kVst3InternalParameterActive:
  1690. if (fCachedParameterValues[rindex] > 0.5f)
  1691. {
  1692. if (! fPlugin.isActive())
  1693. fPlugin.activate();
  1694. }
  1695. else
  1696. {
  1697. fPlugin.deactivateIfNeeded();
  1698. }
  1699. break;
  1700. case kVst3InternalParameterBufferSize:
  1701. fPlugin.setBufferSize(fCachedParameterValues[rindex], true);
  1702. break;
  1703. case kVst3InternalParameterSampleRate:
  1704. fPlugin.setSampleRate(fCachedParameterValues[rindex], true);
  1705. break;
  1706. #if DISTRHO_PLUGIN_WANT_LATENCY
  1707. case kVst3InternalParameterLatency:
  1708. flags = V3_RESTART_LATENCY_CHANGED;
  1709. break;
  1710. #endif
  1711. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1712. case kVst3InternalParameterProgram:
  1713. flags = V3_RESTART_PARAM_VALUES_CHANGED;
  1714. fCurrentProgram = fCachedParameterValues[rindex];
  1715. fPlugin.loadProgram(fCurrentProgram);
  1716. for (uint32_t i=0; i<fParameterCount; ++i)
  1717. {
  1718. if (fPlugin.isParameterOutputOrTrigger(i))
  1719. continue;
  1720. fCachedParameterValues[kVst3InternalParameterCount + i] = fPlugin.getParameterValue(i);
  1721. }
  1722. #if DISTRHO_PLUGIN_HAS_UI
  1723. fParameterValueChangesForUI[kVst3InternalParameterProgram] = true;
  1724. #endif
  1725. break;
  1726. #endif
  1727. }
  1728. if (fComponentHandler != nullptr && flags != 0)
  1729. v3_cpp_obj(fComponentHandler)->restart_component(fComponentHandler, flags);
  1730. return V3_OK;
  1731. }
  1732. const uint32_t index = static_cast<uint32_t>(rindex - kVst3InternalParameterCount);
  1733. DISTRHO_SAFE_ASSERT_UINT2_RETURN(index < fParameterCount, index, fParameterCount, V3_INVALID_ARG);
  1734. setNormalizedPluginParameterValue(index, normalized);
  1735. return V3_OK;
  1736. }
  1737. v3_result setComponentHandler(v3_component_handler** const handler) noexcept
  1738. {
  1739. fComponentHandler = handler;
  1740. return V3_OK;
  1741. }
  1742. #if DISTRHO_PLUGIN_HAS_UI
  1743. // ----------------------------------------------------------------------------------------------------------------
  1744. // v3_connection_point interface calls
  1745. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  1746. void comp2ctrl_connect(v3_connection_point** const other)
  1747. {
  1748. fConnectionFromCompToCtrl = other;
  1749. }
  1750. void comp2ctrl_disconnect()
  1751. {
  1752. fConnectionFromCompToCtrl = nullptr;
  1753. }
  1754. v3_result comp2ctrl_notify(v3_message** const message)
  1755. {
  1756. const char* const msgid = v3_cpp_obj(message)->get_message_id(message);
  1757. DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG);
  1758. v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message);
  1759. DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG);
  1760. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1761. if (std::strcmp(msgid, "midi") == 0)
  1762. return notify_midi(attrs);
  1763. #endif
  1764. #if DISTRHO_PLUGIN_WANT_STATE
  1765. if (std::strcmp(msgid, "state-set") == 0)
  1766. return notify_state(attrs);
  1767. #endif
  1768. d_stdout("comp2ctrl_notify received unknown msg '%s'", msgid);
  1769. return V3_NOT_IMPLEMENTED;
  1770. }
  1771. #endif // DPF_VST3_USES_SEPARATE_CONTROLLER
  1772. // ----------------------------------------------------------------------------------------------------------------
  1773. void ctrl2view_connect(v3_connection_point** const other)
  1774. {
  1775. DISTRHO_SAFE_ASSERT(fConnectedToUI == false);
  1776. fConnectionFromCtrlToView = other;
  1777. fConnectedToUI = false;
  1778. }
  1779. void ctrl2view_disconnect()
  1780. {
  1781. fConnectedToUI = false;
  1782. if (fConnectionFromCtrlToView != nullptr)
  1783. {
  1784. v3_cpp_obj_unref(fConnectionFromCtrlToView);
  1785. fConnectionFromCtrlToView = nullptr;
  1786. }
  1787. }
  1788. v3_result ctrl2view_notify(v3_message** const message)
  1789. {
  1790. DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCtrlToView != nullptr, V3_INTERNAL_ERR);
  1791. const char* const msgid = v3_cpp_obj(message)->get_message_id(message);
  1792. DISTRHO_SAFE_ASSERT_RETURN(msgid != nullptr, V3_INVALID_ARG);
  1793. if (std::strcmp(msgid, "init") == 0)
  1794. {
  1795. fConnectedToUI = true;
  1796. fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = false;
  1797. sendParameterSetToUI(kVst3InternalParameterSampleRate,
  1798. fCachedParameterValues[kVst3InternalParameterSampleRate]);
  1799. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1800. fParameterValueChangesForUI[kVst3InternalParameterProgram] = false;
  1801. sendParameterSetToUI(kVst3InternalParameterProgram, fCurrentProgram);
  1802. #endif
  1803. #if DISTRHO_PLUGIN_WANT_FULL_STATE
  1804. // Update current state from plugin side
  1805. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  1806. {
  1807. const String& key = cit->first;
  1808. fStateMap[key] = fPlugin.getState(key);
  1809. }
  1810. #endif
  1811. #if DISTRHO_PLUGIN_WANT_STATE
  1812. // Set state
  1813. for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
  1814. {
  1815. const String& key = cit->first;
  1816. const String& value = cit->second;
  1817. sendStateSetToUI(key, value);
  1818. }
  1819. #endif
  1820. for (uint32_t i=0; i<fParameterCount; ++i)
  1821. {
  1822. fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = false;
  1823. sendParameterSetToUI(kVst3InternalParameterBaseCount + i,
  1824. fCachedParameterValues[kVst3InternalParameterBaseCount + i]);
  1825. }
  1826. sendReadyToUI();
  1827. return V3_OK;
  1828. }
  1829. DISTRHO_SAFE_ASSERT_RETURN(fConnectedToUI, V3_INTERNAL_ERR);
  1830. v3_attribute_list** const attrs = v3_cpp_obj(message)->get_attributes(message);
  1831. DISTRHO_SAFE_ASSERT_RETURN(attrs != nullptr, V3_INVALID_ARG);
  1832. if (std::strcmp(msgid, "idle") == 0)
  1833. {
  1834. if (fParameterValueChangesForUI[kVst3InternalParameterSampleRate])
  1835. {
  1836. fParameterValueChangesForUI[kVst3InternalParameterSampleRate] = false;
  1837. sendParameterSetToUI(kVst3InternalParameterSampleRate,
  1838. fCachedParameterValues[kVst3InternalParameterSampleRate]);
  1839. }
  1840. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  1841. if (fParameterValueChangesForUI[kVst3InternalParameterProgram])
  1842. {
  1843. fParameterValueChangesForUI[kVst3InternalParameterProgram] = false;
  1844. sendParameterSetToUI(kVst3InternalParameterProgram, fCurrentProgram);
  1845. }
  1846. #endif
  1847. for (uint32_t i=0; i<fParameterCount; ++i)
  1848. {
  1849. if (! fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i])
  1850. continue;
  1851. fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = false;
  1852. sendParameterSetToUI(kVst3InternalParameterBaseCount + i,
  1853. fCachedParameterValues[kVst3InternalParameterBaseCount + i]);
  1854. }
  1855. sendReadyToUI();
  1856. return V3_OK;
  1857. }
  1858. if (std::strcmp(msgid, "close") == 0)
  1859. {
  1860. ctrl2view_disconnect();
  1861. return V3_OK;
  1862. }
  1863. if (std::strcmp(msgid, "parameter-edit") == 0)
  1864. {
  1865. DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, V3_INTERNAL_ERR);
  1866. int64_t rindex;
  1867. int64_t started;
  1868. v3_result res;
  1869. res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex);
  1870. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1871. DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex >= kVst3InternalParameterCount,
  1872. rindex, fParameterCount, V3_INTERNAL_ERR);
  1873. DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex < kVst3InternalParameterCount + fParameterCount,
  1874. rindex, fParameterCount, V3_INTERNAL_ERR);
  1875. res = v3_cpp_obj(attrs)->get_int(attrs, "started", &started);
  1876. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1877. DISTRHO_SAFE_ASSERT_INT_RETURN(started == 0 || started == 1, started, V3_INTERNAL_ERR);
  1878. return started != 0 ? v3_cpp_obj(fComponentHandler)->begin_edit(fComponentHandler, rindex)
  1879. : v3_cpp_obj(fComponentHandler)->end_edit(fComponentHandler, rindex);
  1880. }
  1881. if (std::strcmp(msgid, "parameter-set") == 0)
  1882. {
  1883. DISTRHO_SAFE_ASSERT_RETURN(fComponentHandler != nullptr, V3_INTERNAL_ERR);
  1884. int64_t rindex;
  1885. double value;
  1886. v3_result res;
  1887. res = v3_cpp_obj(attrs)->get_int(attrs, "rindex", &rindex);
  1888. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1889. DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex >= kVst3InternalParameterCount,
  1890. rindex, fParameterCount, V3_INTERNAL_ERR);
  1891. DISTRHO_SAFE_ASSERT_INT2_RETURN(rindex < kVst3InternalParameterCount + fParameterCount,
  1892. rindex, fParameterCount, V3_INTERNAL_ERR);
  1893. res = v3_cpp_obj(attrs)->get_float(attrs, "value", &value);
  1894. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1895. const uint32_t index = rindex - kVst3InternalParameterCount;
  1896. const double normalized = fPlugin.getParameterRanges(index).getNormalizedValue(value);
  1897. return v3_cpp_obj(fComponentHandler)->perform_edit(fComponentHandler, rindex, normalized);
  1898. }
  1899. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1900. if (std::strcmp(msgid, "midi") == 0)
  1901. {
  1902. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  1903. DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCompToCtrl != nullptr, V3_INTERNAL_ERR);
  1904. return v3_cpp_obj(fConnectionFromCompToCtrl)->notify(fConnectionFromCompToCtrl, message);
  1905. #else
  1906. return notify_midi(attrs);
  1907. #endif
  1908. }
  1909. #endif
  1910. #if DISTRHO_PLUGIN_WANT_STATE
  1911. if (std::strcmp(msgid, "state-set") == 0)
  1912. {
  1913. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  1914. DISTRHO_SAFE_ASSERT_RETURN(fConnectionFromCompToCtrl != nullptr, V3_INTERNAL_ERR);
  1915. return v3_cpp_obj(fConnectionFromCompToCtrl)->notify(fConnectionFromCompToCtrl, message);
  1916. #else
  1917. return notify_state(attrs);
  1918. #endif
  1919. }
  1920. #endif
  1921. d_stdout("ctrl2view_notify received unknown msg '%s'", msgid);
  1922. return V3_NOT_IMPLEMENTED;
  1923. }
  1924. #if DISTRHO_PLUGIN_WANT_STATE
  1925. v3_result notify_state(v3_attribute_list** const attrs)
  1926. {
  1927. int64_t keyLength = -1;
  1928. int64_t valueLength = -1;
  1929. v3_result res;
  1930. res = v3_cpp_obj(attrs)->get_int(attrs, "key:length", &keyLength);
  1931. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1932. DISTRHO_SAFE_ASSERT_INT_RETURN(keyLength >= 0, keyLength, V3_INTERNAL_ERR);
  1933. res = v3_cpp_obj(attrs)->get_int(attrs, "value:length", &valueLength);
  1934. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1935. DISTRHO_SAFE_ASSERT_INT_RETURN(valueLength >= 0, valueLength, V3_INTERNAL_ERR);
  1936. int16_t* const key16 = (int16_t*)std::malloc(sizeof(int16_t)*(keyLength + 1));
  1937. DISTRHO_SAFE_ASSERT_RETURN(key16 != nullptr, V3_NOMEM);
  1938. int16_t* const value16 = (int16_t*)std::malloc(sizeof(int16_t)*(valueLength + 1));
  1939. DISTRHO_SAFE_ASSERT_RETURN(value16 != nullptr, V3_NOMEM);
  1940. res = v3_cpp_obj(attrs)->get_string(attrs, "key", key16, sizeof(int16_t)*keyLength);
  1941. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1942. res = v3_cpp_obj(attrs)->get_string(attrs, "value", value16, sizeof(int16_t)*valueLength);
  1943. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1944. // do cheap inline conversion
  1945. char* const key = (char*)key16;
  1946. char* const value = (char*)value16;
  1947. for (int64_t i=0; i<keyLength; ++i)
  1948. key[i] = key16[i];
  1949. for (int64_t i=0; i<valueLength; ++i)
  1950. value[i] = value16[i];
  1951. key[keyLength] = '\0';
  1952. value[valueLength] = '\0';
  1953. fPlugin.setState(key, value);
  1954. // save this key as needed
  1955. if (fPlugin.wantStateKey(key))
  1956. {
  1957. for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it)
  1958. {
  1959. const String& dkey(it->first);
  1960. if (dkey == key)
  1961. {
  1962. it->second = value;
  1963. std::free(key16);
  1964. std::free(value16);
  1965. return V3_OK;
  1966. }
  1967. }
  1968. d_stderr("Failed to find plugin state with key \"%s\"", key);
  1969. }
  1970. std::free(key16);
  1971. std::free(value16);
  1972. return V3_OK;
  1973. }
  1974. #endif // DISTRHO_PLUGIN_WANT_STATE
  1975. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1976. v3_result notify_midi(v3_attribute_list** const attrs)
  1977. {
  1978. uint8_t* data;
  1979. uint32_t size;
  1980. v3_result res;
  1981. res = v3_cpp_obj(attrs)->get_binary(attrs, "data", (const void**)&data, &size);
  1982. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_OK, res, res);
  1983. // known maximum size
  1984. DISTRHO_SAFE_ASSERT_UINT_RETURN(size == 3, size, V3_INTERNAL_ERR);
  1985. return fNotesRingBuffer.writeCustomData(data, size) && fNotesRingBuffer.commitWrite() ? V3_OK : V3_NOMEM;
  1986. }
  1987. #endif // DISTRHO_PLUGIN_WANT_MIDI_INPUT
  1988. #endif
  1989. // ----------------------------------------------------------------------------------------------------------------
  1990. private:
  1991. // Plugin
  1992. PluginExporter fPlugin;
  1993. // VST3 stuff
  1994. v3_component_handler** fComponentHandler;
  1995. #if DISTRHO_PLUGIN_HAS_UI
  1996. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  1997. v3_connection_point** fConnectionFromCompToCtrl;
  1998. #endif
  1999. v3_connection_point** fConnectionFromCtrlToView;
  2000. v3_host_application** const fHostApplication;
  2001. #endif
  2002. // Temporary data
  2003. const uint32_t fParameterCount;
  2004. const uint32_t fVst3ParameterCount; // full offset + real
  2005. float* fCachedParameterValues; // basic offset + real
  2006. bool* fParameterValuesChangedDuringProcessing; // basic offset + real
  2007. #if DISTRHO_PLUGIN_HAS_UI
  2008. bool* fParameterValueChangesForUI; // basic offset + real
  2009. bool fConnectedToUI;
  2010. #endif
  2011. #if DISTRHO_PLUGIN_WANT_LATENCY
  2012. uint32_t fLastKnownLatency;
  2013. #endif
  2014. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  2015. MidiEvent fMidiEvents[kMaxMidiEvents];
  2016. # if DISTRHO_PLUGIN_HAS_UI
  2017. SmallStackRingBuffer fNotesRingBuffer;
  2018. # endif
  2019. #endif
  2020. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  2021. v3_event_list** fHostEventOutputHandle;
  2022. #endif
  2023. #if DISTRHO_PLUGIN_WANT_PROGRAMS
  2024. uint32_t fCurrentProgram;
  2025. const uint32_t fProgramCountMinusOne;
  2026. #endif
  2027. #if DISTRHO_PLUGIN_WANT_STATE
  2028. StringMap fStateMap;
  2029. #endif
  2030. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  2031. TimePosition fTimePosition;
  2032. #endif
  2033. // ----------------------------------------------------------------------------------------------------------------
  2034. // helper functions called during process, cannot block
  2035. void updateParametersFromProcessing(v3_param_changes** const outparamsptr, const int32_t offset)
  2036. {
  2037. DISTRHO_SAFE_ASSERT_RETURN(outparamsptr != nullptr,);
  2038. v3_param_id paramId;
  2039. float curValue;
  2040. for (v3_param_id i=kVst3InternalParameterActive; i<=kVst3InternalParameterSampleRate; ++i)
  2041. {
  2042. if (! fParameterValuesChangedDuringProcessing[i])
  2043. continue;
  2044. curValue = plainParameterToNormalized(i, fCachedParameterValues[i]);
  2045. fParameterValuesChangedDuringProcessing[i] = false;
  2046. addParameterDataToHostOutputEvents(outparamsptr, i, curValue);
  2047. }
  2048. for (uint32_t i=0; i<fParameterCount; ++i)
  2049. {
  2050. if (fPlugin.isParameterOutput(i))
  2051. {
  2052. // NOTE: no output parameter support in VST3, simulate it here
  2053. curValue = fPlugin.getParameterValue(i);
  2054. if (d_isEqual(curValue, fCachedParameterValues[kVst3InternalParameterBaseCount + i]))
  2055. continue;
  2056. }
  2057. else if (fPlugin.isParameterTrigger(i))
  2058. {
  2059. // NOTE: no trigger support in VST3 parameters, simulate it here
  2060. curValue = fPlugin.getParameterValue(i);
  2061. if (d_isEqual(curValue, fPlugin.getParameterDefault(i)))
  2062. continue;
  2063. fPlugin.setParameterValue(i, curValue);
  2064. }
  2065. else if (fParameterValuesChangedDuringProcessing[kVst3InternalParameterBaseCount + i])
  2066. {
  2067. fParameterValuesChangedDuringProcessing[kVst3InternalParameterBaseCount + i] = false;
  2068. curValue = fPlugin.getParameterValue(i);
  2069. }
  2070. else
  2071. {
  2072. continue;
  2073. }
  2074. fCachedParameterValues[kVst3InternalParameterBaseCount + i] = curValue;
  2075. #if DISTRHO_PLUGIN_HAS_UI
  2076. fParameterValueChangesForUI[kVst3InternalParameterBaseCount + i] = true;
  2077. #endif
  2078. paramId = kVst3InternalParameterCount + i;
  2079. curValue = fPlugin.getParameterRanges(i).getNormalizedValue(curValue);
  2080. if (! addParameterDataToHostOutputEvents(outparamsptr, paramId, curValue, offset))
  2081. break;
  2082. }
  2083. #if DISTRHO_PLUGIN_WANT_LATENCY
  2084. const uint32_t latency = fPlugin.getLatency();
  2085. if (fLastKnownLatency != latency)
  2086. {
  2087. fLastKnownLatency = latency;
  2088. curValue = plainParameterToNormalized(kVst3InternalParameterLatency,
  2089. fCachedParameterValues[kVst3InternalParameterLatency]);
  2090. addParameterDataToHostOutputEvents(outparamsptr, kVst3InternalParameterLatency, curValue);
  2091. }
  2092. #endif
  2093. }
  2094. bool addParameterDataToHostOutputEvents(v3_param_changes** const outparamsptr,
  2095. v3_param_id paramId,
  2096. const float curValue,
  2097. const int32_t offset = 0)
  2098. {
  2099. int32_t index = 0;
  2100. v3_param_value_queue** const queue = v3_cpp_obj(outparamsptr)->add_param_data(outparamsptr,
  2101. &paramId, &index);
  2102. DISTRHO_SAFE_ASSERT_RETURN(queue != nullptr, false);
  2103. DISTRHO_SAFE_ASSERT_RETURN(v3_cpp_obj(queue)->add_point(queue, 0, curValue, &index) == V3_OK, false);
  2104. if (offset != 0)
  2105. v3_cpp_obj(queue)->add_point(queue, offset, curValue, &index);
  2106. return true;
  2107. }
  2108. #if DISTRHO_PLUGIN_HAS_UI
  2109. // ----------------------------------------------------------------------------------------------------------------
  2110. // helper functions called during message passing, can block
  2111. v3_message** createMessage(const char* const id) const
  2112. {
  2113. DISTRHO_SAFE_ASSERT_RETURN(fHostApplication != nullptr, nullptr);
  2114. v3_tuid iid;
  2115. memcpy(iid, v3_message_iid, sizeof(v3_tuid));
  2116. v3_message** msg = nullptr;
  2117. const v3_result res = v3_cpp_obj(fHostApplication)->create_instance(fHostApplication, iid, iid, (void**)&msg);
  2118. DISTRHO_SAFE_ASSERT_INT_RETURN(res == V3_TRUE, res, nullptr);
  2119. DISTRHO_SAFE_ASSERT_RETURN(msg != nullptr, nullptr);
  2120. v3_cpp_obj(msg)->set_message_id(msg, id);
  2121. return msg;
  2122. }
  2123. void sendParameterSetToUI(const v3_param_id rindex, const double value) const
  2124. {
  2125. v3_message** const message = createMessage("parameter-set");
  2126. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  2127. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  2128. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  2129. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2);
  2130. v3_cpp_obj(attrlist)->set_int(attrlist, "rindex", rindex);
  2131. v3_cpp_obj(attrlist)->set_float(attrlist, "value", value);
  2132. v3_cpp_obj(fConnectionFromCtrlToView)->notify(fConnectionFromCtrlToView, message);
  2133. v3_cpp_obj_unref(message);
  2134. }
  2135. void sendStateSetToUI(const char* const key, const char* const value) const
  2136. {
  2137. v3_message** const message = createMessage("state-set");
  2138. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  2139. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  2140. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  2141. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2);
  2142. v3_cpp_obj(attrlist)->set_int(attrlist, "key:length", std::strlen(key));
  2143. v3_cpp_obj(attrlist)->set_int(attrlist, "value:length", std::strlen(value));
  2144. v3_cpp_obj(attrlist)->set_string(attrlist, "key", ScopedUTF16String(key));
  2145. v3_cpp_obj(attrlist)->set_string(attrlist, "value", ScopedUTF16String(value));
  2146. v3_cpp_obj(fConnectionFromCtrlToView)->notify(fConnectionFromCtrlToView, message);
  2147. v3_cpp_obj_unref(message);
  2148. }
  2149. void sendReadyToUI() const
  2150. {
  2151. v3_message** const message = createMessage("ready");
  2152. DISTRHO_SAFE_ASSERT_RETURN(message != nullptr,);
  2153. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  2154. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr,);
  2155. v3_cpp_obj(attrlist)->set_int(attrlist, "__dpf_msg_target__", 2);
  2156. v3_cpp_obj(fConnectionFromCtrlToView)->notify(fConnectionFromCtrlToView, message);
  2157. v3_cpp_obj_unref(message);
  2158. }
  2159. #endif
  2160. // ----------------------------------------------------------------------------------------------------------------
  2161. // DPF callbacks
  2162. bool requestParameterValueChange(const uint32_t index, float)
  2163. {
  2164. fParameterValuesChangedDuringProcessing[kVst3InternalParameterBaseCount + index] = true;
  2165. return true;
  2166. }
  2167. #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST
  2168. static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value)
  2169. {
  2170. return ((PluginVst3*)ptr)->requestParameterValueChange(index, value);
  2171. }
  2172. #endif
  2173. #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
  2174. bool writeMidi(const MidiEvent& midiEvent)
  2175. {
  2176. DISTRHO_CUSTOM_SAFE_ASSERT_ONCE_RETURN("MIDI output unsupported", fHostEventOutputHandle != nullptr, false);
  2177. v3_event event;
  2178. std::memset(&event, 0, sizeof(event));
  2179. event.sample_offset = midiEvent.frame;
  2180. const uint8_t* const data = midiEvent.size > MidiEvent::kDataSize ? midiEvent.dataExt : midiEvent.data;
  2181. switch (data[0] & 0xf0)
  2182. {
  2183. case 0x80:
  2184. event.type = V3_EVENT_NOTE_OFF;
  2185. event.note_off.channel = data[0] & 0xf;
  2186. event.note_off.pitch = data[1];
  2187. event.note_off.velocity = (float)data[2] / 127.0f;
  2188. // int32_t note_id;
  2189. // float tuning;
  2190. break;
  2191. case 0x90:
  2192. event.type = V3_EVENT_NOTE_ON;
  2193. event.note_on.channel = data[0] & 0xf;
  2194. event.note_on.pitch = data[1];
  2195. // float tuning;
  2196. event.note_on.velocity = (float)data[2] / 127.0f;
  2197. // int32_t length;
  2198. // int32_t note_id;
  2199. break;
  2200. case 0xA0:
  2201. event.type = V3_EVENT_POLY_PRESSURE;
  2202. event.poly_pressure.channel = data[0] & 0xf;
  2203. event.poly_pressure.pitch = data[1];
  2204. event.poly_pressure.pressure = (float)data[2] / 127.0f;
  2205. // int32_t note_id;
  2206. break;
  2207. case 0xB0:
  2208. event.type = V3_EVENT_LEGACY_MIDI_CC_OUT;
  2209. event.midi_cc_out.channel = data[0] & 0xf;
  2210. event.midi_cc_out.cc_number = data[1];
  2211. event.midi_cc_out.value = data[2];
  2212. if (midiEvent.size == 4)
  2213. event.midi_cc_out.value2 = midiEvent.size == 4;
  2214. break;
  2215. /* TODO how do we deal with program changes??
  2216. case 0xC0:
  2217. break;
  2218. */
  2219. case 0xD0:
  2220. event.type = V3_EVENT_LEGACY_MIDI_CC_OUT;
  2221. event.midi_cc_out.channel = data[0] & 0xf;
  2222. event.midi_cc_out.cc_number = 128;
  2223. event.midi_cc_out.value = data[1];
  2224. break;
  2225. case 0xE0:
  2226. event.type = V3_EVENT_LEGACY_MIDI_CC_OUT;
  2227. event.midi_cc_out.channel = data[0] & 0xf;
  2228. event.midi_cc_out.cc_number = 129;
  2229. event.midi_cc_out.value = data[1];
  2230. event.midi_cc_out.value2 = data[2];
  2231. break;
  2232. default:
  2233. return true;
  2234. }
  2235. return v3_cpp_obj(fHostEventOutputHandle)->add_event(fHostEventOutputHandle, &event) == V3_OK;
  2236. }
  2237. static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent)
  2238. {
  2239. return ((PluginVst3*)ptr)->writeMidi(midiEvent);
  2240. }
  2241. #endif
  2242. };
  2243. // --------------------------------------------------------------------------------------------------------------------
  2244. /**
  2245. * VST3 low-level pointer thingies follow, proceed with care.
  2246. */
  2247. // --------------------------------------------------------------------------------------------------------------------
  2248. // v3_funknown for static instances
  2249. static uint32_t V3_API dpf_static_ref(void*) { return 1; }
  2250. static uint32_t V3_API dpf_static_unref(void*) { return 0; }
  2251. // --------------------------------------------------------------------------------------------------------------------
  2252. // v3_funknown for classes with a single instance
  2253. template<class T>
  2254. static uint32_t V3_API dpf_single_instance_ref(void* const self)
  2255. {
  2256. return ++(*static_cast<T**>(self))->refcounter;
  2257. }
  2258. template<class T>
  2259. static uint32_t V3_API dpf_single_instance_unref(void* const self)
  2260. {
  2261. return --(*static_cast<T**>(self))->refcounter;
  2262. }
  2263. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2264. // --------------------------------------------------------------------------------------------------------------------
  2265. // dpf_comp2ctrl_connection_point
  2266. struct dpf_comp2ctrl_connection_point : v3_connection_point_cpp {
  2267. std::atomic_int refcounter;
  2268. ScopedPointer<PluginVst3>& vst3;
  2269. v3_connection_point** other;
  2270. dpf_comp2ctrl_connection_point(ScopedPointer<PluginVst3>& v)
  2271. : refcounter(1),
  2272. vst3(v),
  2273. other(nullptr)
  2274. {
  2275. // v3_funknown, single instance
  2276. query_interface = query_interface_connection_point;
  2277. ref = dpf_single_instance_ref<dpf_comp2ctrl_connection_point>;
  2278. unref = dpf_single_instance_unref<dpf_comp2ctrl_connection_point>;
  2279. // v3_connection_point
  2280. point.connect = connect;
  2281. point.disconnect = disconnect;
  2282. point.notify = notify;
  2283. }
  2284. // ----------------------------------------------------------------------------------------------------------------
  2285. // v3_funknown
  2286. static v3_result V3_API query_interface_connection_point(void* const self, const v3_tuid iid, void** const iface)
  2287. {
  2288. dpf_comp2ctrl_connection_point* const point = *static_cast<dpf_comp2ctrl_connection_point**>(self);
  2289. if (v3_tuid_match(iid, v3_funknown_iid) ||
  2290. v3_tuid_match(iid, v3_connection_point_iid))
  2291. {
  2292. d_stdout("dpf_comp2ctrl_connection_point => %p %s %p | OK", self, tuid2str(iid), iface);
  2293. ++point->refcounter;
  2294. *iface = self;
  2295. return V3_OK;
  2296. }
  2297. d_stdout("dpf_comp2ctrl_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
  2298. *iface = nullptr;
  2299. return V3_NO_INTERFACE;
  2300. }
  2301. // ----------------------------------------------------------------------------------------------------------------
  2302. // v3_connection_point
  2303. static v3_result V3_API connect(void* const self, v3_connection_point** const other)
  2304. {
  2305. d_stdout("dpf_comp2ctrl_connection_point::connect => %p %p", self, other);
  2306. dpf_comp2ctrl_connection_point* const point = *static_cast<dpf_comp2ctrl_connection_point**>(self);
  2307. DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG);
  2308. DISTRHO_SAFE_ASSERT_RETURN(point->other != other, V3_INVALID_ARG);
  2309. point->other = other;
  2310. if (PluginVst3* const vst3 = point->vst3)
  2311. vst3->comp2ctrl_connect(other);
  2312. return V3_OK;
  2313. }
  2314. static v3_result V3_API disconnect(void* const self, v3_connection_point** const other)
  2315. {
  2316. d_stdout("dpf_comp2ctrl_connection_point => %p %p", self, other);
  2317. dpf_comp2ctrl_connection_point* const point = *static_cast<dpf_comp2ctrl_connection_point**>(self);
  2318. DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG);
  2319. DISTRHO_SAFE_ASSERT_RETURN(point->other == other, V3_INVALID_ARG);
  2320. if (PluginVst3* const vst3 = point->vst3)
  2321. vst3->comp2ctrl_disconnect();
  2322. point->other = nullptr;
  2323. return V3_OK;
  2324. }
  2325. static v3_result V3_API notify(void* const self, v3_message** const message)
  2326. {
  2327. dpf_comp2ctrl_connection_point* const point = *static_cast<dpf_comp2ctrl_connection_point**>(self);
  2328. PluginVst3* const vst3 = point->vst3;
  2329. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2330. v3_connection_point** const other = point->other;
  2331. DISTRHO_SAFE_ASSERT_RETURN(other != nullptr, V3_NOT_INITIALIZED);
  2332. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  2333. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr, V3_INVALID_ARG);
  2334. int64_t target = 0;
  2335. const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target);
  2336. DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res);
  2337. DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1, target, V3_INTERNAL_ERR);
  2338. // view -> edit controller -> component
  2339. return vst3->comp2ctrl_notify(message);
  2340. }
  2341. };
  2342. #endif // DPF_VST3_USES_SEPARATE_CONTROLLER
  2343. #if DISTRHO_PLUGIN_HAS_UI
  2344. // --------------------------------------------------------------------------------------------------------------------
  2345. // dpf_comp2ctrl_connection_point
  2346. struct dpf_ctrl2view_connection_point : v3_connection_point_cpp {
  2347. std::atomic_int refcounter;
  2348. ScopedPointer<PluginVst3>& vst3;
  2349. v3_connection_point** other;
  2350. dpf_ctrl2view_connection_point(ScopedPointer<PluginVst3>& v)
  2351. : refcounter(1),
  2352. vst3(v),
  2353. other(nullptr)
  2354. {
  2355. // v3_funknown, single instance
  2356. query_interface = query_interface_connection_point;
  2357. ref = dpf_single_instance_ref<dpf_ctrl2view_connection_point>;
  2358. unref = dpf_single_instance_unref<dpf_ctrl2view_connection_point>;
  2359. // v3_connection_point
  2360. point.connect = connect;
  2361. point.disconnect = disconnect;
  2362. point.notify = notify;
  2363. }
  2364. // ----------------------------------------------------------------------------------------------------------------
  2365. // v3_funknown
  2366. static v3_result V3_API query_interface_connection_point(void* const self, const v3_tuid iid, void** const iface)
  2367. {
  2368. dpf_ctrl2view_connection_point* const point = *static_cast<dpf_ctrl2view_connection_point**>(self);
  2369. if (v3_tuid_match(iid, v3_funknown_iid) ||
  2370. v3_tuid_match(iid, v3_connection_point_iid))
  2371. {
  2372. d_stdout("dpf_ctrl2view_connection_point => %p %s %p | OK", self, tuid2str(iid), iface);
  2373. ++point->refcounter;
  2374. *iface = self;
  2375. return V3_OK;
  2376. }
  2377. d_stdout("dpf_ctrl2view_connection_point => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
  2378. *iface = nullptr;
  2379. return V3_NO_INTERFACE;
  2380. }
  2381. // ----------------------------------------------------------------------------------------------------------------
  2382. // v3_connection_point
  2383. static v3_result V3_API connect(void* const self, v3_connection_point** const other)
  2384. {
  2385. d_stdout("dpf_ctrl2view_connection_point::connect => %p %p", self, other);
  2386. dpf_ctrl2view_connection_point* const point = *static_cast<dpf_ctrl2view_connection_point**>(self);
  2387. DISTRHO_SAFE_ASSERT_RETURN(point->other == nullptr, V3_INVALID_ARG);
  2388. DISTRHO_SAFE_ASSERT_RETURN(point->other != other, V3_INVALID_ARG);
  2389. point->other = other;
  2390. if (PluginVst3* const vst3 = point->vst3)
  2391. vst3->ctrl2view_connect(other);
  2392. return V3_OK;
  2393. }
  2394. static v3_result V3_API disconnect(void* const self, v3_connection_point** const other)
  2395. {
  2396. d_stdout("dpf_ctrl2view_connection_point::disconnect => %p %p", self, other);
  2397. dpf_ctrl2view_connection_point* const point = *static_cast<dpf_ctrl2view_connection_point**>(self);
  2398. DISTRHO_SAFE_ASSERT_RETURN(point->other != nullptr, V3_INVALID_ARG);
  2399. DISTRHO_SAFE_ASSERT_RETURN(point->other == other, V3_INVALID_ARG);
  2400. if (PluginVst3* const vst3 = point->vst3)
  2401. vst3->ctrl2view_disconnect();
  2402. point->other = nullptr;
  2403. return V3_OK;
  2404. }
  2405. static v3_result V3_API notify(void* const self, v3_message** const message)
  2406. {
  2407. dpf_ctrl2view_connection_point* const point = *static_cast<dpf_ctrl2view_connection_point**>(self);
  2408. PluginVst3* const vst3 = point->vst3;
  2409. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2410. v3_connection_point** const other = point->other;
  2411. DISTRHO_SAFE_ASSERT_RETURN(other != nullptr, V3_NOT_INITIALIZED);
  2412. v3_attribute_list** const attrlist = v3_cpp_obj(message)->get_attributes(message);
  2413. DISTRHO_SAFE_ASSERT_RETURN(attrlist != nullptr, V3_INVALID_ARG);
  2414. int64_t target = 0;
  2415. const v3_result res = v3_cpp_obj(attrlist)->get_int(attrlist, "__dpf_msg_target__", &target);
  2416. DISTRHO_SAFE_ASSERT_RETURN(res == V3_OK, res);
  2417. DISTRHO_SAFE_ASSERT_INT_RETURN(target == 1 || target == 2, target, V3_INTERNAL_ERR);
  2418. if (target == 1)
  2419. {
  2420. // view -> edit controller
  2421. return vst3->ctrl2view_notify(message);
  2422. }
  2423. else
  2424. {
  2425. // edit controller -> view
  2426. return v3_cpp_obj(other)->notify(other, message);
  2427. }
  2428. }
  2429. };
  2430. #endif // DISTRHO_PLUGIN_HAS_UI
  2431. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  2432. // --------------------------------------------------------------------------------------------------------------------
  2433. // dpf_midi_mapping
  2434. struct dpf_midi_mapping : v3_midi_mapping_cpp {
  2435. dpf_midi_mapping()
  2436. {
  2437. // v3_funknown, static
  2438. query_interface = query_interface_midi_mapping;
  2439. ref = dpf_static_ref;
  2440. unref = dpf_static_unref;
  2441. // v3_midi_mapping
  2442. map.get_midi_controller_assignment = get_midi_controller_assignment;
  2443. }
  2444. // ----------------------------------------------------------------------------------------------------------------
  2445. // v3_funknown
  2446. static v3_result V3_API query_interface_midi_mapping(void* const self, const v3_tuid iid, void** const iface)
  2447. {
  2448. if (v3_tuid_match(iid, v3_funknown_iid) ||
  2449. v3_tuid_match(iid, v3_midi_mapping_iid))
  2450. {
  2451. d_stdout("query_interface_midi_mapping => %p %s %p | OK", self, tuid2str(iid), iface);
  2452. *iface = self;
  2453. return V3_OK;
  2454. }
  2455. d_stdout("query_interface_midi_mapping => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
  2456. *iface = nullptr;
  2457. return V3_NO_INTERFACE;
  2458. }
  2459. // ----------------------------------------------------------------------------------------------------------------
  2460. // v3_midi_mapping
  2461. static v3_result V3_API get_midi_controller_assignment(void*, const int32_t bus, const int16_t channel, const int16_t cc, v3_param_id* const id)
  2462. {
  2463. DISTRHO_SAFE_ASSERT_INT_RETURN(bus == 0, bus, V3_FALSE);
  2464. DISTRHO_SAFE_ASSERT_INT_RETURN(channel >= 0 && channel < 16, channel, V3_FALSE);
  2465. DISTRHO_SAFE_ASSERT_INT_RETURN(cc >= 0 && cc < 130, cc, V3_FALSE);
  2466. *id = kVst3InternalParameterMidiCC_start + channel * 130 + cc;
  2467. return V3_TRUE;
  2468. }
  2469. DISTRHO_PREVENT_HEAP_ALLOCATION
  2470. };
  2471. #endif // DISTRHO_PLUGIN_WANT_MIDI_INPUT
  2472. // --------------------------------------------------------------------------------------------------------------------
  2473. // dpf_edit_controller
  2474. struct dpf_edit_controller : v3_edit_controller_cpp {
  2475. std::atomic_int refcounter;
  2476. #if DISTRHO_PLUGIN_HAS_UI
  2477. ScopedPointer<dpf_ctrl2view_connection_point> connectionCtrl2View;
  2478. #endif
  2479. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2480. ScopedPointer<dpf_comp2ctrl_connection_point> connectionComp2Ctrl;
  2481. ScopedPointer<PluginVst3> vst3;
  2482. #else
  2483. ScopedPointer<PluginVst3>& vst3;
  2484. bool initialized;
  2485. #endif
  2486. // cached values
  2487. v3_component_handler** handler;
  2488. v3_host_application** const hostApplicationFromFactory;
  2489. v3_host_application** hostApplicationFromInitialize;
  2490. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2491. dpf_edit_controller(v3_host_application** const host)
  2492. : refcounter(1),
  2493. vst3(nullptr),
  2494. #else
  2495. dpf_edit_controller(ScopedPointer<PluginVst3>& v, v3_host_application** const host)
  2496. : refcounter(1),
  2497. vst3(v),
  2498. initialized(false),
  2499. #endif
  2500. handler(nullptr),
  2501. hostApplicationFromFactory(host),
  2502. hostApplicationFromInitialize(nullptr)
  2503. {
  2504. d_stdout("dpf_edit_controller() with hostApplication %p", hostApplicationFromFactory);
  2505. // make sure host application is valid through out this controller lifetime
  2506. if (hostApplicationFromFactory != nullptr)
  2507. v3_cpp_obj_ref(hostApplicationFromFactory);
  2508. // v3_funknown, everything custom
  2509. query_interface = query_interface_edit_controller;
  2510. ref = ref_edit_controller;
  2511. unref = unref_edit_controller;
  2512. // v3_plugin_base
  2513. base.initialize = initialize;
  2514. base.terminate = terminate;
  2515. // v3_edit_controller
  2516. ctrl.set_component_state = set_component_state;
  2517. ctrl.set_state = set_state;
  2518. ctrl.get_state = get_state;
  2519. ctrl.get_parameter_count = get_parameter_count;
  2520. ctrl.get_parameter_info = get_parameter_info;
  2521. ctrl.get_parameter_string_for_value = get_parameter_string_for_value;
  2522. ctrl.get_parameter_value_for_string = get_parameter_value_for_string;
  2523. ctrl.normalised_parameter_to_plain = normalised_parameter_to_plain;
  2524. ctrl.plain_parameter_to_normalised = plain_parameter_to_normalised;
  2525. ctrl.get_parameter_normalised = get_parameter_normalised;
  2526. ctrl.set_parameter_normalised = set_parameter_normalised;
  2527. ctrl.set_component_handler = set_component_handler;
  2528. ctrl.create_view = create_view;
  2529. }
  2530. ~dpf_edit_controller()
  2531. {
  2532. d_stdout("~dpf_edit_controller()");
  2533. #if DISTRHO_PLUGIN_HAS_UI
  2534. connectionCtrl2View = nullptr;
  2535. #endif
  2536. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2537. connectionComp2Ctrl = nullptr;
  2538. vst3 = nullptr;
  2539. #endif
  2540. if (hostApplicationFromFactory != nullptr)
  2541. v3_cpp_obj_unref(hostApplicationFromFactory);
  2542. }
  2543. // ----------------------------------------------------------------------------------------------------------------
  2544. // v3_funknown
  2545. static v3_result V3_API query_interface_edit_controller(void* const self, const v3_tuid iid, void** const iface)
  2546. {
  2547. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2548. if (v3_tuid_match(iid, v3_funknown_iid) ||
  2549. v3_tuid_match(iid, v3_plugin_base_iid) ||
  2550. v3_tuid_match(iid, v3_edit_controller_iid))
  2551. {
  2552. d_stdout("query_interface_edit_controller => %p %s %p | OK", self, tuid2str(iid), iface);
  2553. ++controller->refcounter;
  2554. *iface = self;
  2555. return V3_OK;
  2556. }
  2557. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  2558. if (v3_tuid_match(iid, v3_midi_mapping_iid))
  2559. {
  2560. d_stdout("query_interface_edit_controller => %p %s %p | OK convert static", self, tuid2str(iid), iface);
  2561. static dpf_midi_mapping midi_mapping;
  2562. static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping;
  2563. *iface = &midi_mapping_ptr;
  2564. return V3_OK;
  2565. }
  2566. #endif
  2567. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2568. if (v3_tuid_match(iid, v3_connection_point_iid))
  2569. {
  2570. d_stdout("query_interface_edit_controller => %p %s %p | OK convert %p",
  2571. self, tuid2str(iid), iface, controller->connectionComp2Ctrl.get());
  2572. if (controller->connectionComp2Ctrl == nullptr)
  2573. controller->connectionComp2Ctrl = new dpf_comp2ctrl_connection_point(controller->vst3);
  2574. else
  2575. ++controller->connectionComp2Ctrl->refcounter;
  2576. *iface = &controller->connectionComp2Ctrl;
  2577. return V3_OK;
  2578. }
  2579. #endif
  2580. d_stdout("query_interface_edit_controller => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
  2581. *iface = nullptr;
  2582. return V3_NO_INTERFACE;
  2583. }
  2584. static uint32_t V3_API ref_edit_controller(void* const self)
  2585. {
  2586. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2587. const int refcount = ++controller->refcounter;
  2588. d_stdout("dpf_edit_controller::ref => %p | refcount %i", self, refcount);
  2589. return refcount;
  2590. }
  2591. static uint32_t V3_API unref_edit_controller(void* const self)
  2592. {
  2593. dpf_edit_controller** const controllerptr = static_cast<dpf_edit_controller**>(self);
  2594. dpf_edit_controller* const controller = *controllerptr;
  2595. if (const int refcount = --controller->refcounter)
  2596. {
  2597. d_stdout("dpf_edit_controller::unref => %p | refcount %i", self, refcount);
  2598. return refcount;
  2599. }
  2600. #if DISTRHO_PLUGIN_HAS_UI
  2601. if (dpf_ctrl2view_connection_point* const point = controller->connectionCtrl2View)
  2602. {
  2603. if (const int refcount = point->refcounter)
  2604. {
  2605. d_stderr("DPF warning: asked to delete controller while view connection point still active (refcount %d)", refcount);
  2606. }
  2607. }
  2608. #endif
  2609. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2610. if (dpf_comp2ctrl_connection_point* const point = controller->connectionComp2Ctrl)
  2611. {
  2612. if (const int refcount = point->refcounter)
  2613. {
  2614. d_stderr("DPF warning: asked to delete controller while component connection point still active (refcount %d)", refcount);
  2615. }
  2616. }
  2617. #endif
  2618. d_stdout("dpf_edit_controller::unref => %p | refcount is zero, deleting everything now!", self);
  2619. delete controller;
  2620. delete controllerptr;
  2621. return 0;
  2622. }
  2623. // ----------------------------------------------------------------------------------------------------------------
  2624. // v3_plugin_base
  2625. static v3_result V3_API initialize(void* const self, v3_plugin_base::v3_funknown** const context)
  2626. {
  2627. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2628. // check if already initialized
  2629. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2630. DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 == nullptr, V3_INVALID_ARG);
  2631. #else
  2632. DISTRHO_SAFE_ASSERT_RETURN(! controller->initialized, V3_INVALID_ARG);
  2633. #endif
  2634. // query for host application
  2635. v3_host_application** hostApplication = nullptr;
  2636. if (context != nullptr)
  2637. v3_cpp_obj_query_interface(context, v3_host_application_iid, &hostApplication);
  2638. d_stdout("dpf_edit_controller::initialize => %p %p | host %p", self, context, hostApplication);
  2639. // save it for later so we can unref it
  2640. controller->hostApplicationFromInitialize = hostApplication;
  2641. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2642. // provide the factory application to the plugin if this new one is missing
  2643. if (hostApplication == nullptr)
  2644. hostApplication = controller->hostApplicationFromFactory;
  2645. // default early values
  2646. if (d_nextBufferSize == 0)
  2647. d_nextBufferSize = 1024;
  2648. if (d_nextSampleRate <= 0.0)
  2649. d_nextSampleRate = 44100.0;
  2650. d_nextCanRequestParameterValueChanges = true;
  2651. // create the actual plugin
  2652. controller->vst3 = new PluginVst3(hostApplication);
  2653. // set connection point if needed
  2654. if (dpf_comp2ctrl_connection_point* const point = controller->connectionComp2Ctrl)
  2655. {
  2656. if (point->other != nullptr)
  2657. controller->vst3->comp2ctrl_connect(point->other);
  2658. }
  2659. #else
  2660. // mark as initialized
  2661. controller->initialized = true;
  2662. #endif
  2663. return V3_OK;
  2664. }
  2665. static v3_result V3_API terminate(void* self)
  2666. {
  2667. d_stdout("dpf_edit_controller::terminate => %p", self);
  2668. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2669. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2670. // check if already terminated
  2671. DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_INVALID_ARG);
  2672. // delete actual plugin
  2673. controller->vst3 = nullptr;
  2674. #else
  2675. // check if already terminated
  2676. DISTRHO_SAFE_ASSERT_RETURN(controller->initialized, V3_INVALID_ARG);
  2677. // mark as uninitialzed
  2678. controller->initialized = false;
  2679. #endif
  2680. // unref host application received during initialize
  2681. if (controller->hostApplicationFromInitialize != nullptr)
  2682. {
  2683. v3_cpp_obj_unref(controller->hostApplicationFromInitialize);
  2684. controller->hostApplicationFromInitialize = nullptr;
  2685. }
  2686. return V3_OK;
  2687. }
  2688. // ----------------------------------------------------------------------------------------------------------------
  2689. // v3_edit_controller
  2690. static v3_result V3_API set_component_state(void* const self, v3_bstream** const stream)
  2691. {
  2692. d_stdout("dpf_edit_controller::set_component_state => %p %p", self, stream);
  2693. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2694. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2695. PluginVst3* const vst3 = controller->vst3;
  2696. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2697. return vst3->setState(stream);
  2698. #else
  2699. return V3_OK;
  2700. // unused
  2701. (void)self;
  2702. (void)stream;
  2703. #endif
  2704. }
  2705. static v3_result V3_API set_state(void* const self, v3_bstream** const stream)
  2706. {
  2707. d_stdout("dpf_edit_controller::set_state => %p %p", self, stream);
  2708. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2709. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2710. DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED);
  2711. #else
  2712. DISTRHO_SAFE_ASSERT_RETURN(controller->initialized, V3_INVALID_ARG);
  2713. #endif
  2714. return V3_NOT_IMPLEMENTED;
  2715. // unused
  2716. (void)stream;
  2717. }
  2718. static v3_result V3_API get_state(void* const self, v3_bstream** const stream)
  2719. {
  2720. d_stdout("dpf_edit_controller::get_state => %p %p", self, stream);
  2721. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2722. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  2723. DISTRHO_SAFE_ASSERT_RETURN(controller->vst3 != nullptr, V3_NOT_INITIALIZED);
  2724. #else
  2725. DISTRHO_SAFE_ASSERT_RETURN(controller->initialized, V3_INVALID_ARG);
  2726. #endif
  2727. return V3_NOT_IMPLEMENTED;
  2728. // unused
  2729. (void)stream;
  2730. }
  2731. static int32_t V3_API get_parameter_count(void* self)
  2732. {
  2733. // d_stdout("dpf_edit_controller::get_parameter_count => %p", self);
  2734. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2735. PluginVst3* const vst3 = controller->vst3;
  2736. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2737. return vst3->getParameterCount();
  2738. }
  2739. static v3_result V3_API get_parameter_info(void* self, int32_t param_idx, v3_param_info* param_info)
  2740. {
  2741. // d_stdout("dpf_edit_controller::get_parameter_info => %p %i", self, param_idx);
  2742. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2743. PluginVst3* const vst3 = controller->vst3;
  2744. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2745. return vst3->getParameterInfo(param_idx, param_info);
  2746. }
  2747. static v3_result V3_API get_parameter_string_for_value(void* self, v3_param_id index, double normalised, v3_str_128 output)
  2748. {
  2749. // NOTE very noisy, called many times
  2750. // d_stdout("dpf_edit_controller::get_parameter_string_for_value => %p %u %f %p", self, index, normalised, output);
  2751. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2752. PluginVst3* const vst3 = controller->vst3;
  2753. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2754. return vst3->getParameterStringForValue(index, normalised, output);
  2755. }
  2756. static v3_result V3_API get_parameter_value_for_string(void* self, v3_param_id index, int16_t* input, double* output)
  2757. {
  2758. d_stdout("dpf_edit_controller::get_parameter_value_for_string => %p %u %p %p", self, index, input, output);
  2759. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2760. PluginVst3* const vst3 = controller->vst3;
  2761. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2762. return vst3->getParameterValueForString(index, input, output);
  2763. }
  2764. static double V3_API normalised_parameter_to_plain(void* self, v3_param_id index, double normalised)
  2765. {
  2766. d_stdout("dpf_edit_controller::normalised_parameter_to_plain => %p %u %f", self, index, normalised);
  2767. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2768. PluginVst3* const vst3 = controller->vst3;
  2769. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2770. return vst3->normalizedParameterToPlain(index, normalised);
  2771. }
  2772. static double V3_API plain_parameter_to_normalised(void* self, v3_param_id index, double plain)
  2773. {
  2774. d_stdout("dpf_edit_controller::plain_parameter_to_normalised => %p %u %f", self, index, plain);
  2775. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2776. PluginVst3* const vst3 = controller->vst3;
  2777. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2778. return vst3->plainParameterToNormalized(index, plain);
  2779. }
  2780. static double V3_API get_parameter_normalised(void* self, v3_param_id index)
  2781. {
  2782. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2783. PluginVst3* const vst3 = controller->vst3;
  2784. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0.0);
  2785. return vst3->getParameterNormalized(index);
  2786. }
  2787. static v3_result V3_API set_parameter_normalised(void* const self, const v3_param_id index, const double normalised)
  2788. {
  2789. // d_stdout("dpf_edit_controller::set_parameter_normalised => %p %u %f", self, index, normalised);
  2790. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2791. PluginVst3* const vst3 = controller->vst3;
  2792. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2793. return vst3->setParameterNormalized(index, normalised);
  2794. }
  2795. static v3_result V3_API set_component_handler(void* self, v3_component_handler** handler)
  2796. {
  2797. d_stdout("dpf_edit_controller::set_component_handler => %p %p", self, handler);
  2798. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2799. controller->handler = handler;
  2800. if (PluginVst3* const vst3 = controller->vst3)
  2801. return vst3->setComponentHandler(handler);
  2802. return V3_NOT_INITIALIZED;
  2803. }
  2804. static v3_plugin_view** V3_API create_view(void* self, const char* name)
  2805. {
  2806. d_stdout("dpf_edit_controller::create_view => %p %s", self, name);
  2807. dpf_edit_controller* const controller = *static_cast<dpf_edit_controller**>(self);
  2808. d_stdout("create_view has contexts %p %p",
  2809. controller->hostApplicationFromFactory, controller->hostApplicationFromInitialize);
  2810. #if DISTRHO_PLUGIN_HAS_UI
  2811. // plugin must be initialized
  2812. PluginVst3* const vst3 = controller->vst3;
  2813. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, nullptr);
  2814. d_stdout("dpf_edit_controller::create_view => %p %s | edit-ctrl %p, factory %p",
  2815. self, name,
  2816. controller->hostApplicationFromInitialize,
  2817. controller->hostApplicationFromFactory);
  2818. // we require a host application for message creation
  2819. v3_host_application** const host = controller->hostApplicationFromInitialize != nullptr
  2820. ? controller->hostApplicationFromInitialize
  2821. : controller->hostApplicationFromFactory;
  2822. DISTRHO_SAFE_ASSERT_RETURN(host != nullptr, nullptr);
  2823. v3_plugin_view** const view = dpf_plugin_view_create(host,
  2824. vst3->getInstancePointer(),
  2825. vst3->getSampleRate());
  2826. DISTRHO_SAFE_ASSERT_RETURN(view != nullptr, nullptr);
  2827. v3_connection_point** uiconn = nullptr;
  2828. if (v3_cpp_obj_query_interface(view, v3_connection_point_iid, &uiconn) == V3_OK)
  2829. {
  2830. d_stdout("view connection query ok %p", uiconn);
  2831. controller->connectionCtrl2View = new dpf_ctrl2view_connection_point(controller->vst3);
  2832. v3_connection_point** const ctrlconn = (v3_connection_point**)&controller->connectionCtrl2View;
  2833. v3_cpp_obj(uiconn)->connect(uiconn, ctrlconn);
  2834. v3_cpp_obj(ctrlconn)->connect(ctrlconn, uiconn);
  2835. }
  2836. else
  2837. {
  2838. controller->connectionCtrl2View = nullptr;
  2839. }
  2840. return view;
  2841. #else
  2842. return nullptr;
  2843. #endif
  2844. }
  2845. };
  2846. // --------------------------------------------------------------------------------------------------------------------
  2847. // dpf_process_context_requirements
  2848. struct dpf_process_context_requirements : v3_process_context_requirements_cpp {
  2849. dpf_process_context_requirements()
  2850. {
  2851. // v3_funknown, static
  2852. query_interface = query_interface_process_context_requirements;
  2853. ref = dpf_static_ref;
  2854. unref = dpf_static_unref;
  2855. // v3_process_context_requirements
  2856. req.get_process_context_requirements = get_process_context_requirements;
  2857. }
  2858. // ----------------------------------------------------------------------------------------------------------------
  2859. // v3_funknown
  2860. static v3_result V3_API query_interface_process_context_requirements(void* const self, const v3_tuid iid, void** const iface)
  2861. {
  2862. if (v3_tuid_match(iid, v3_funknown_iid) ||
  2863. v3_tuid_match(iid, v3_process_context_requirements_iid))
  2864. {
  2865. d_stdout("query_interface_process_context_requirements => %p %s %p | OK", self, tuid2str(iid), iface);
  2866. *iface = self;
  2867. return V3_OK;
  2868. }
  2869. d_stdout("query_interface_process_context_requirements => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
  2870. *iface = nullptr;
  2871. return V3_NO_INTERFACE;
  2872. }
  2873. // ----------------------------------------------------------------------------------------------------------------
  2874. // v3_process_context_requirements
  2875. static uint32_t V3_API get_process_context_requirements(void*)
  2876. {
  2877. #if DISTRHO_PLUGIN_WANT_TIMEPOS
  2878. return 0x0
  2879. | V3_PROCESS_CTX_NEED_CONTINUOUS_TIME // V3_PROCESS_CTX_CONT_TIME_VALID
  2880. | V3_PROCESS_CTX_NEED_PROJECT_TIME // V3_PROCESS_CTX_PROJECT_TIME_VALID
  2881. | V3_PROCESS_CTX_NEED_TEMPO // V3_PROCESS_CTX_TEMPO_VALID
  2882. | V3_PROCESS_CTX_NEED_TIME_SIG // V3_PROCESS_CTX_TIME_SIG_VALID
  2883. | V3_PROCESS_CTX_NEED_TRANSPORT_STATE; // V3_PROCESS_CTX_PLAYING
  2884. #else
  2885. return 0x0;
  2886. #endif
  2887. }
  2888. DISTRHO_PREVENT_HEAP_ALLOCATION
  2889. };
  2890. // --------------------------------------------------------------------------------------------------------------------
  2891. // dpf_audio_processor
  2892. struct dpf_audio_processor : v3_audio_processor_cpp {
  2893. std::atomic_int refcounter;
  2894. ScopedPointer<PluginVst3>& vst3;
  2895. dpf_audio_processor(ScopedPointer<PluginVst3>& v)
  2896. : refcounter(1),
  2897. vst3(v)
  2898. {
  2899. // v3_funknown, single instance
  2900. query_interface = query_interface_audio_processor;
  2901. ref = dpf_single_instance_ref<dpf_audio_processor>;
  2902. unref = dpf_single_instance_unref<dpf_audio_processor>;
  2903. // v3_audio_processor
  2904. proc.set_bus_arrangements = set_bus_arrangements;
  2905. proc.get_bus_arrangement = get_bus_arrangement;
  2906. proc.can_process_sample_size = can_process_sample_size;
  2907. proc.get_latency_samples = get_latency_samples;
  2908. proc.setup_processing = setup_processing;
  2909. proc.set_processing = set_processing;
  2910. proc.process = process;
  2911. proc.get_tail_samples = get_tail_samples;
  2912. }
  2913. // ----------------------------------------------------------------------------------------------------------------
  2914. // v3_funknown
  2915. static v3_result V3_API query_interface_audio_processor(void* const self, const v3_tuid iid, void** const iface)
  2916. {
  2917. dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
  2918. if (v3_tuid_match(iid, v3_funknown_iid) ||
  2919. v3_tuid_match(iid, v3_audio_processor_iid))
  2920. {
  2921. d_stdout("query_interface_audio_processor => %p %s %p | OK", self, tuid2str(iid), iface);
  2922. ++processor->refcounter;
  2923. *iface = self;
  2924. return V3_OK;
  2925. }
  2926. if (v3_tuid_match(iid, v3_process_context_requirements_iid))
  2927. {
  2928. d_stdout("query_interface_audio_processor => %p %s %p | OK convert static", self, tuid2str(iid), iface);
  2929. static dpf_process_context_requirements context_req;
  2930. static dpf_process_context_requirements* context_req_ptr = &context_req;
  2931. *iface = &context_req_ptr;
  2932. return V3_OK;
  2933. }
  2934. d_stdout("query_interface_audio_processor => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
  2935. *iface = nullptr;
  2936. return V3_NO_INTERFACE;
  2937. }
  2938. // ----------------------------------------------------------------------------------------------------------------
  2939. // v3_audio_processor
  2940. static v3_result V3_API set_bus_arrangements(void* const self,
  2941. v3_speaker_arrangement* const inputs, const int32_t num_inputs,
  2942. v3_speaker_arrangement* const outputs, const int32_t num_outputs)
  2943. {
  2944. // NOTE this is called a bunch of times
  2945. // d_stdout("dpf_audio_processor::set_bus_arrangements => %p %p %i %p %i",
  2946. // self, inputs, num_inputs, outputs, num_outputs);
  2947. dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
  2948. PluginVst3* const vst3 = processor->vst3;
  2949. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2950. return processor->vst3->setBusArrangements(inputs, num_inputs, outputs, num_outputs);
  2951. }
  2952. static v3_result V3_API get_bus_arrangement(void* const self, const int32_t bus_direction,
  2953. const int32_t idx, v3_speaker_arrangement* const arr)
  2954. {
  2955. d_stdout("dpf_audio_processor::get_bus_arrangement => %p %s %p",
  2956. self, v3_bus_direction_str(bus_direction), idx, arr);
  2957. dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
  2958. PluginVst3* const vst3 = processor->vst3;
  2959. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2960. return processor->vst3->getBusArrangement(bus_direction, idx, arr);
  2961. }
  2962. static v3_result V3_API can_process_sample_size(void*, const int32_t symbolic_sample_size)
  2963. {
  2964. // NOTE runs during RT
  2965. // d_stdout("dpf_audio_processor::can_process_sample_size => %i", symbolic_sample_size);
  2966. return symbolic_sample_size == V3_SAMPLE_32 ? V3_OK : V3_NOT_IMPLEMENTED;
  2967. }
  2968. static uint32_t V3_API get_latency_samples(void* const self)
  2969. {
  2970. d_stdout("dpf_audio_processor::get_latency_samples => %p", self);
  2971. dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
  2972. PluginVst3* const vst3 = processor->vst3;
  2973. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0);
  2974. return processor->vst3->getLatencySamples();
  2975. }
  2976. static v3_result V3_API setup_processing(void* const self, v3_process_setup* const setup)
  2977. {
  2978. d_stdout("dpf_audio_processor::setup_processing => %p %p", self, setup);
  2979. dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
  2980. PluginVst3* const vst3 = processor->vst3;
  2981. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2982. d_nextBufferSize = setup->max_block_size;
  2983. d_nextSampleRate = setup->sample_rate;
  2984. return processor->vst3->setupProcessing(setup);
  2985. }
  2986. static v3_result V3_API set_processing(void* const self, const v3_bool state)
  2987. {
  2988. d_stdout("dpf_audio_processor::set_processing => %p %u", self, state);
  2989. dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
  2990. PluginVst3* const vst3 = processor->vst3;
  2991. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  2992. return processor->vst3->setProcessing(state);
  2993. }
  2994. static v3_result V3_API process(void* const self, v3_process_data* const data)
  2995. {
  2996. // NOTE runs during RT
  2997. // d_stdout("dpf_audio_processor::process => %p", self);
  2998. dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
  2999. PluginVst3* const vst3 = processor->vst3;
  3000. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  3001. return processor->vst3->process(data);
  3002. }
  3003. static uint32_t V3_API get_tail_samples(void* const self)
  3004. {
  3005. d_stdout("dpf_audio_processor::get_tail_samples => %p", self);
  3006. dpf_audio_processor* const processor = *static_cast<dpf_audio_processor**>(self);
  3007. PluginVst3* const vst3 = processor->vst3;
  3008. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, 0);
  3009. return processor->vst3->getTailSamples();
  3010. }
  3011. };
  3012. // --------------------------------------------------------------------------------------------------------------------
  3013. // Store components that we can't delete properly, to be cleaned up on module unload
  3014. struct dpf_component;
  3015. std::vector<dpf_component**> gComponentGarbage;
  3016. static v3_result handleUncleanComponent(dpf_component** const componentptr)
  3017. {
  3018. gComponentGarbage.push_back(componentptr);
  3019. return V3_INVALID_ARG;
  3020. }
  3021. // --------------------------------------------------------------------------------------------------------------------
  3022. // dpf_component
  3023. struct dpf_component : v3_component_cpp {
  3024. std::atomic_int refcounter;
  3025. ScopedPointer<dpf_audio_processor> processor;
  3026. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  3027. ScopedPointer<dpf_comp2ctrl_connection_point> connectionComp2Ctrl;
  3028. #else
  3029. ScopedPointer<dpf_edit_controller> controller;
  3030. #endif
  3031. ScopedPointer<PluginVst3> vst3;
  3032. v3_host_application** const hostApplicationFromFactory;
  3033. v3_host_application** hostApplicationFromInitialize;
  3034. dpf_component(v3_host_application** const host)
  3035. : refcounter(1),
  3036. hostApplicationFromFactory(host),
  3037. hostApplicationFromInitialize(nullptr)
  3038. {
  3039. d_stdout("dpf_component() with hostApplication %p", hostApplicationFromFactory);
  3040. // make sure host application is valid through out this component lifetime
  3041. if (hostApplicationFromFactory != nullptr)
  3042. v3_cpp_obj_ref(hostApplicationFromFactory);
  3043. // v3_funknown, everything custom
  3044. query_interface = query_interface_component;
  3045. ref = ref_component;
  3046. unref = unref_component;
  3047. // v3_plugin_base
  3048. base.initialize = initialize;
  3049. base.terminate = terminate;
  3050. // v3_component
  3051. comp.get_controller_class_id = get_controller_class_id;
  3052. comp.set_io_mode = set_io_mode;
  3053. comp.get_bus_count = get_bus_count;
  3054. comp.get_bus_info = get_bus_info;
  3055. comp.get_routing_info = get_routing_info;
  3056. comp.activate_bus = activate_bus;
  3057. comp.set_active = set_active;
  3058. comp.set_state = set_state;
  3059. comp.get_state = get_state;
  3060. }
  3061. ~dpf_component()
  3062. {
  3063. d_stdout("~dpf_component()");
  3064. processor = nullptr;
  3065. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  3066. connectionComp2Ctrl = nullptr;
  3067. #else
  3068. controller = nullptr;
  3069. #endif
  3070. vst3 = nullptr;
  3071. if (hostApplicationFromFactory != nullptr)
  3072. v3_cpp_obj_unref(hostApplicationFromFactory);
  3073. }
  3074. // ----------------------------------------------------------------------------------------------------------------
  3075. // v3_funknown
  3076. static v3_result V3_API query_interface_component(void* const self, const v3_tuid iid, void** const iface)
  3077. {
  3078. dpf_component* const component = *static_cast<dpf_component**>(self);
  3079. if (v3_tuid_match(iid, v3_funknown_iid) ||
  3080. v3_tuid_match(iid, v3_plugin_base_iid) ||
  3081. v3_tuid_match(iid, v3_component_iid))
  3082. {
  3083. d_stdout("query_interface_component => %p %s %p | OK", self, tuid2str(iid), iface);
  3084. ++component->refcounter;
  3085. *iface = self;
  3086. return V3_OK;
  3087. }
  3088. #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
  3089. if (v3_tuid_match(iid, v3_midi_mapping_iid))
  3090. {
  3091. d_stdout("query_interface_component => %p %s %p | OK convert static", self, tuid2str(iid), iface);
  3092. static dpf_midi_mapping midi_mapping;
  3093. static dpf_midi_mapping* midi_mapping_ptr = &midi_mapping;
  3094. *iface = &midi_mapping_ptr;
  3095. return V3_OK;
  3096. }
  3097. #endif
  3098. if (v3_tuid_match(iid, v3_audio_processor_iid))
  3099. {
  3100. d_stdout("query_interface_component => %p %s %p | OK convert %p",
  3101. self, tuid2str(iid), iface, component->processor.get());
  3102. if (component->processor == nullptr)
  3103. component->processor = new dpf_audio_processor(component->vst3);
  3104. else
  3105. ++component->processor->refcounter;
  3106. *iface = &component->processor;
  3107. return V3_OK;
  3108. }
  3109. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  3110. if (v3_tuid_match(iid, v3_connection_point_iid))
  3111. {
  3112. d_stdout("query_interface_component => %p %s %p | OK convert %p",
  3113. self, tuid2str(iid), iface, component->connectionComp2Ctrl.get());
  3114. if (component->connectionComp2Ctrl == nullptr)
  3115. component->connectionComp2Ctrl = new dpf_comp2ctrl_connection_point(component->vst3);
  3116. else
  3117. ++component->connectionComp2Ctrl->refcounter;
  3118. *iface = &component->connectionComp2Ctrl;
  3119. return V3_OK;
  3120. }
  3121. #else
  3122. if (v3_tuid_match(iid, v3_edit_controller_iid))
  3123. {
  3124. d_stdout("query_interface_component => %p %s %p | OK convert %p",
  3125. self, tuid2str(iid), iface, component->controller.get());
  3126. if (component->controller == nullptr)
  3127. component->controller = new dpf_edit_controller(component->vst3,
  3128. component->hostApplicationFromFactory);
  3129. else
  3130. ++component->controller->refcounter;
  3131. *iface = &component->controller;
  3132. return V3_OK;
  3133. }
  3134. #endif
  3135. d_stdout("query_interface_component => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
  3136. *iface = nullptr;
  3137. return V3_NO_INTERFACE;
  3138. }
  3139. static uint32_t V3_API ref_component(void* const self)
  3140. {
  3141. dpf_component* const component = *static_cast<dpf_component**>(self);
  3142. const int refcount = ++component->refcounter;
  3143. d_stdout("dpf_component::ref => %p | refcount %i", self, refcount);
  3144. return refcount;
  3145. }
  3146. static uint32_t V3_API unref_component(void* const self)
  3147. {
  3148. dpf_component** const componentptr = static_cast<dpf_component**>(self);
  3149. dpf_component* const component = *componentptr;
  3150. if (const int refcount = --component->refcounter)
  3151. {
  3152. d_stdout("dpf_component::unref => %p | refcount %i", self, refcount);
  3153. return refcount;
  3154. }
  3155. /**
  3156. * Some hosts will have unclean instances of a few of the component child classes at this point.
  3157. * We check for those here, going through the whole possible chain to see if it is safe to delete.
  3158. * If not, we add this component to the `gComponentGarbage` global which will take care of it during unload.
  3159. */
  3160. bool unclean = false;
  3161. if (dpf_audio_processor* const proc = component->processor)
  3162. {
  3163. if (const int refcount = proc->refcounter)
  3164. {
  3165. unclean = true;
  3166. d_stderr("DPF warning: asked to delete component while audio processor still active (refcount %d)", refcount);
  3167. }
  3168. }
  3169. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  3170. if (dpf_comp2ctrl_connection_point* const point = component->connectionComp2Ctrl)
  3171. {
  3172. if (const int refcount = point->refcounter)
  3173. {
  3174. unclean = true;
  3175. d_stderr("DPF warning: asked to delete component while connection point still active (refcount %d)", refcount);
  3176. }
  3177. }
  3178. #else
  3179. if (dpf_edit_controller* const controller = component->controller)
  3180. {
  3181. if (const int refcount = controller->refcounter)
  3182. {
  3183. unclean = true;
  3184. d_stderr("DPF warning: asked to delete component while edit controller still active (refcount %d)", refcount);
  3185. }
  3186. # if DISTRHO_PLUGIN_HAS_UI
  3187. if (dpf_dsp_connection_point* const controller = ctrl->connectionCtrl2View)
  3188. {
  3189. if (const int refcount = controller->refcounter)
  3190. {
  3191. unclean = true;
  3192. d_stderr("DPF warning: asked to delete component while view connection point still active (refcount %d)", refcount);
  3193. }
  3194. }
  3195. # endif
  3196. }
  3197. #endif
  3198. if (unclean)
  3199. return handleUncleanComponent(componentptr);
  3200. d_stdout("dpf_component::unref => %p | refcount is zero, deleting everything now!", self);
  3201. delete component;
  3202. delete componentptr;
  3203. return 0;
  3204. }
  3205. // ----------------------------------------------------------------------------------------------------------------
  3206. // v3_plugin_base
  3207. static v3_result V3_API initialize(void* const self, v3_plugin_base::v3_funknown** const context)
  3208. {
  3209. dpf_component* const component = *static_cast<dpf_component**>(self);
  3210. // check if already initialized
  3211. DISTRHO_SAFE_ASSERT_RETURN(component->vst3 == nullptr, V3_INVALID_ARG);
  3212. // query for host application
  3213. v3_host_application** hostApplication = nullptr;
  3214. if (context != nullptr)
  3215. v3_cpp_obj_query_interface(context, v3_host_application_iid, &hostApplication);
  3216. d_stdout("dpf_component::initialize => %p %p | hostApplication %p", self, context, hostApplication);
  3217. // save it for later so we can unref it
  3218. component->hostApplicationFromInitialize = hostApplication;
  3219. // provide the factory application to the plugin if this new one is missing
  3220. if (hostApplication == nullptr)
  3221. hostApplication = component->hostApplicationFromFactory;
  3222. // default early values
  3223. if (d_nextBufferSize == 0)
  3224. d_nextBufferSize = 1024;
  3225. if (d_nextSampleRate <= 0.0)
  3226. d_nextSampleRate = 44100.0;
  3227. d_nextCanRequestParameterValueChanges = true;
  3228. // create the actual plugin
  3229. component->vst3 = new PluginVst3(hostApplication);
  3230. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  3231. // set connection point if needed
  3232. if (dpf_comp2ctrl_connection_point* const point = component->connectionComp2Ctrl)
  3233. {
  3234. if (point->other != nullptr)
  3235. component->vst3->comp2ctrl_connect(point->other);
  3236. }
  3237. #endif
  3238. return V3_OK;
  3239. }
  3240. static v3_result V3_API terminate(void* const self)
  3241. {
  3242. d_stdout("dpf_component::terminate => %p", self);
  3243. dpf_component* const component = *static_cast<dpf_component**>(self);
  3244. // check if already terminated
  3245. DISTRHO_SAFE_ASSERT_RETURN(component->vst3 != nullptr, V3_INVALID_ARG);
  3246. // delete actual plugin
  3247. component->vst3 = nullptr;
  3248. // unref host application received during initialize
  3249. if (component->hostApplicationFromInitialize != nullptr)
  3250. {
  3251. v3_cpp_obj_unref(component->hostApplicationFromInitialize);
  3252. component->hostApplicationFromInitialize = nullptr;
  3253. }
  3254. return V3_OK;
  3255. }
  3256. // ----------------------------------------------------------------------------------------------------------------
  3257. // v3_component
  3258. static v3_result V3_API get_controller_class_id(void*, v3_tuid class_id)
  3259. {
  3260. d_stdout("dpf_component::get_controller_class_id => %p", class_id);
  3261. std::memcpy(class_id, dpf_tuid_controller, sizeof(v3_tuid));
  3262. return V3_OK;
  3263. }
  3264. static v3_result V3_API set_io_mode(void* const self, const int32_t io_mode)
  3265. {
  3266. d_stdout("dpf_component::set_io_mode => %p %i", self, io_mode);
  3267. dpf_component* const component = *static_cast<dpf_component**>(self);
  3268. PluginVst3* const vst3 = component->vst3;
  3269. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  3270. // TODO
  3271. return V3_NOT_IMPLEMENTED;
  3272. }
  3273. static int32_t V3_API get_bus_count(void* const self, const int32_t media_type, const int32_t bus_direction)
  3274. {
  3275. // NOTE runs during RT
  3276. // d_stdout("dpf_component::get_bus_count => %p %s %s",
  3277. // self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction));
  3278. dpf_component* const component = *static_cast<dpf_component**>(self);
  3279. PluginVst3* const vst3 = component->vst3;
  3280. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  3281. const int32_t ret = vst3->getBusCount(media_type, bus_direction);
  3282. // d_stdout("dpf_component::get_bus_count returns %i", ret);
  3283. return ret;
  3284. }
  3285. static v3_result V3_API get_bus_info(void* const self, const int32_t media_type, const int32_t bus_direction,
  3286. const int32_t bus_idx, v3_bus_info* const info)
  3287. {
  3288. d_stdout("dpf_component::get_bus_info => %p %s %s %i %p",
  3289. self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, info);
  3290. dpf_component* const component = *static_cast<dpf_component**>(self);
  3291. PluginVst3* const vst3 = component->vst3;
  3292. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  3293. return vst3->getBusInfo(media_type, bus_direction, bus_idx, info);
  3294. }
  3295. static v3_result V3_API get_routing_info(void* const self, v3_routing_info* const input, v3_routing_info* const output)
  3296. {
  3297. d_stdout("dpf_component::get_routing_info => %p %p %p", self, input, output);
  3298. dpf_component* const component = *static_cast<dpf_component**>(self);
  3299. PluginVst3* const vst3 = component->vst3;
  3300. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  3301. return vst3->getRoutingInfo(input, output);
  3302. }
  3303. static v3_result V3_API activate_bus(void* const self, const int32_t media_type, const int32_t bus_direction,
  3304. const int32_t bus_idx, const v3_bool state)
  3305. {
  3306. // NOTE this is called a bunch of times
  3307. // d_stdout("dpf_component::activate_bus => %p %s %s %i %u",
  3308. // self, v3_media_type_str(media_type), v3_bus_direction_str(bus_direction), bus_idx, state);
  3309. dpf_component* const component = *static_cast<dpf_component**>(self);
  3310. PluginVst3* const vst3 = component->vst3;
  3311. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  3312. return vst3->activateBus(media_type, bus_direction, bus_idx, state);
  3313. }
  3314. static v3_result V3_API set_active(void* const self, const v3_bool state)
  3315. {
  3316. d_stdout("dpf_component::set_active => %p %u", self, state);
  3317. dpf_component* const component = *static_cast<dpf_component**>(self);
  3318. PluginVst3* const vst3 = component->vst3;
  3319. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  3320. return component->vst3->setActive(state);
  3321. }
  3322. static v3_result V3_API set_state(void* const self, v3_bstream** const stream)
  3323. {
  3324. d_stdout("dpf_component::set_state => %p", self);
  3325. dpf_component* const component = *static_cast<dpf_component**>(self);
  3326. PluginVst3* const vst3 = component->vst3;
  3327. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  3328. return vst3->setState(stream);
  3329. }
  3330. static v3_result V3_API get_state(void* const self, v3_bstream** const stream)
  3331. {
  3332. d_stdout("dpf_component::get_state => %p %p", self, stream);
  3333. dpf_component* const component = *static_cast<dpf_component**>(self);
  3334. PluginVst3* const vst3 = component->vst3;
  3335. DISTRHO_SAFE_ASSERT_RETURN(vst3 != nullptr, V3_NOT_INITIALIZED);
  3336. return vst3->getState(stream);
  3337. }
  3338. };
  3339. // --------------------------------------------------------------------------------------------------------------------
  3340. // Dummy plugin to get data from
  3341. static const PluginExporter& _getPluginInfo()
  3342. {
  3343. d_nextBufferSize = 1024;
  3344. d_nextSampleRate = 44100.0;
  3345. d_nextPluginIsDummy = true;
  3346. d_nextCanRequestParameterValueChanges = true;
  3347. static const PluginExporter gPluginInfo(nullptr, nullptr, nullptr);
  3348. d_nextBufferSize = 0;
  3349. d_nextSampleRate = 0.0;
  3350. d_nextPluginIsDummy = false;
  3351. d_nextCanRequestParameterValueChanges = false;
  3352. return gPluginInfo;
  3353. }
  3354. static const PluginExporter& getPluginInfo()
  3355. {
  3356. static const PluginExporter& info(_getPluginInfo());
  3357. return info;
  3358. }
  3359. static const char* getPluginCategories()
  3360. {
  3361. static String categories;
  3362. static bool firstInit = true;
  3363. if (firstInit)
  3364. {
  3365. #ifdef DISTRHO_PLUGIN_VST3_CATEGORIES
  3366. categories = DISTRHO_PLUGIN_VST3_CATEGORIES;
  3367. #elif DISTRHO_PLUGIN_IS_SYNTH
  3368. categories = "Instrument";
  3369. #endif
  3370. #if (DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_INPUTS == 1) && DISTRHO_PLUGIN_NUM_OUTPUTS == 1
  3371. if (categories.isNotEmpty())
  3372. categories += "|";
  3373. categories += "Mono";
  3374. #elif (DISTRHO_PLUGIN_NUM_INPUTS == 0 || DISTRHO_PLUGIN_NUM_INPUTS == 2) && DISTRHO_PLUGIN_NUM_OUTPUTS == 2
  3375. if (categories.isNotEmpty())
  3376. categories += "|";
  3377. categories += "Stereo";
  3378. #endif
  3379. firstInit = false;
  3380. }
  3381. return categories.buffer();
  3382. }
  3383. static const char* getPluginVersion()
  3384. {
  3385. static String version;
  3386. if (version.isEmpty())
  3387. {
  3388. const uint32_t versionNum = getPluginInfo().getVersion();
  3389. char versionBuf[64];
  3390. snprintf(versionBuf, sizeof(versionBuf)-1, "%d.%d.%d",
  3391. (versionNum >> 16) & 0xff,
  3392. (versionNum >> 8) & 0xff,
  3393. (versionNum >> 0) & 0xff);
  3394. versionBuf[sizeof(versionBuf)-1] = '\0';
  3395. version = versionBuf;
  3396. }
  3397. return version.buffer();
  3398. }
  3399. // --------------------------------------------------------------------------------------------------------------------
  3400. // dpf_factory
  3401. struct dpf_factory : v3_plugin_factory_cpp {
  3402. std::atomic_int refcounter;
  3403. // cached values
  3404. v3_funknown** hostContext;
  3405. dpf_factory()
  3406. : refcounter(1),
  3407. hostContext(nullptr)
  3408. {
  3409. // v3_funknown, static
  3410. query_interface = query_interface_factory;
  3411. ref = ref_factory;
  3412. unref = unref_factory;
  3413. // v3_plugin_factory
  3414. v1.get_factory_info = get_factory_info;
  3415. v1.num_classes = num_classes;
  3416. v1.get_class_info = get_class_info;
  3417. v1.create_instance = create_instance;
  3418. // v3_plugin_factory_2
  3419. v2.get_class_info_2 = get_class_info_2;
  3420. // v3_plugin_factory_3
  3421. v3.get_class_info_utf16 = get_class_info_utf16;
  3422. v3.set_host_context = set_host_context;
  3423. }
  3424. ~dpf_factory()
  3425. {
  3426. // unref old context if there is one
  3427. if (hostContext != nullptr)
  3428. v3_cpp_obj_unref(hostContext);
  3429. if (gComponentGarbage.size() == 0)
  3430. return;
  3431. d_stdout("DPF notice: cleaning up previously undeleted components now");
  3432. for (std::vector<dpf_component**>::iterator it = gComponentGarbage.begin();
  3433. it != gComponentGarbage.end(); ++it)
  3434. {
  3435. dpf_component** const componentptr = *it;
  3436. dpf_component* const component = *componentptr;
  3437. delete component;
  3438. delete componentptr;
  3439. }
  3440. gComponentGarbage.clear();
  3441. }
  3442. // ----------------------------------------------------------------------------------------------------------------
  3443. // v3_funknown
  3444. static v3_result V3_API query_interface_factory(void* const self, const v3_tuid iid, void** const iface)
  3445. {
  3446. dpf_factory* const factory = *static_cast<dpf_factory**>(self);
  3447. if (v3_tuid_match(iid, v3_funknown_iid) ||
  3448. v3_tuid_match(iid, v3_plugin_factory_iid) ||
  3449. v3_tuid_match(iid, v3_plugin_factory_2_iid) ||
  3450. v3_tuid_match(iid, v3_plugin_factory_3_iid))
  3451. {
  3452. d_stdout("query_interface_factory => %p %s %p | OK", self, tuid2str(iid), iface);
  3453. ++factory->refcounter;
  3454. *iface = self;
  3455. return V3_OK;
  3456. }
  3457. d_stdout("query_interface_factory => %p %s %p | WARNING UNSUPPORTED", self, tuid2str(iid), iface);
  3458. *iface = nullptr;
  3459. return V3_NO_INTERFACE;
  3460. }
  3461. static uint32_t V3_API ref_factory(void* const self)
  3462. {
  3463. dpf_factory* const factory = *static_cast<dpf_factory**>(self);
  3464. const int refcount = ++factory->refcounter;
  3465. d_stdout("ref_factory::ref => %p | refcount %i", self, refcount);
  3466. return refcount;
  3467. }
  3468. static uint32_t V3_API unref_factory(void* const self)
  3469. {
  3470. dpf_factory** const factoryptr = static_cast<dpf_factory**>(self);
  3471. dpf_factory* const factory = *factoryptr;
  3472. if (const int refcount = --factory->refcounter)
  3473. {
  3474. d_stdout("unref_factory::unref => %p | refcount %i", self, refcount);
  3475. return refcount;
  3476. }
  3477. d_stdout("unref_factory::unref => %p | refcount is zero, deleting factory", self);
  3478. delete factory;
  3479. delete factoryptr;
  3480. return 0;
  3481. }
  3482. // ----------------------------------------------------------------------------------------------------------------
  3483. // v3_plugin_factory
  3484. static v3_result V3_API get_factory_info(void*, v3_factory_info* const info)
  3485. {
  3486. d_stdout("dpf_factory::get_factory_info => %p", info);
  3487. std::memset(info, 0, sizeof(*info));
  3488. info->flags = 0x10;
  3489. DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor));
  3490. DISTRHO_NAMESPACE::strncpy(info->url, getPluginInfo().getHomePage(), ARRAY_SIZE(info->url));
  3491. // DISTRHO_NAMESPACE::strncpy(info->email, "", ARRAY_SIZE(info->email)); // TODO
  3492. return V3_OK;
  3493. }
  3494. static int32_t V3_API num_classes(void*)
  3495. {
  3496. d_stdout("dpf_factory::num_classes");
  3497. return 1;
  3498. }
  3499. static v3_result V3_API get_class_info(void*, const int32_t idx, v3_class_info* const info)
  3500. {
  3501. d_stdout("dpf_factory::get_class_info => %i %p", idx, info);
  3502. std::memset(info, 0, sizeof(*info));
  3503. DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG);
  3504. info->cardinality = 0x7FFFFFFF;
  3505. std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid));
  3506. DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category));
  3507. DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name));
  3508. return V3_OK;
  3509. }
  3510. static v3_result V3_API create_instance(void* self, const v3_tuid class_id, const v3_tuid iid, void** const instance)
  3511. {
  3512. d_stdout("dpf_factory::create_instance => %p %s %s %p", self, tuid2str(class_id), tuid2str(iid), instance);
  3513. dpf_factory* const factory = *static_cast<dpf_factory**>(self);
  3514. // query for host application
  3515. v3_host_application** hostApplication = nullptr;
  3516. if (factory->hostContext != nullptr)
  3517. v3_cpp_obj_query_interface(factory->hostContext, v3_host_application_iid, &hostApplication);
  3518. // create component
  3519. if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_class) && v3_tuid_match(iid, v3_component_iid))
  3520. {
  3521. dpf_component** const componentptr = new dpf_component*;
  3522. *componentptr = new dpf_component(hostApplication);
  3523. *instance = static_cast<void*>(componentptr);
  3524. return V3_OK;
  3525. }
  3526. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  3527. // create edit controller
  3528. if (v3_tuid_match(class_id, *(const v3_tuid*)&dpf_tuid_controller) && v3_tuid_match(iid, v3_edit_controller_iid))
  3529. {
  3530. dpf_edit_controller** const controllerptr = new dpf_edit_controller*;
  3531. *controllerptr = new dpf_edit_controller(hostApplication);
  3532. *instance = static_cast<void*>(controllerptr);
  3533. return V3_OK;
  3534. }
  3535. #endif
  3536. // unsupported, roll back host application
  3537. if (hostApplication != nullptr)
  3538. v3_cpp_obj_unref(hostApplication);
  3539. return V3_NO_INTERFACE;
  3540. }
  3541. // ----------------------------------------------------------------------------------------------------------------
  3542. // v3_plugin_factory_2
  3543. static v3_result V3_API get_class_info_2(void*, const int32_t idx, v3_class_info_2* const info)
  3544. {
  3545. d_stdout("dpf_factory::get_class_info_2 => %i %p", idx, info);
  3546. std::memset(info, 0, sizeof(*info));
  3547. DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG);
  3548. info->cardinality = 0x7FFFFFFF;
  3549. // TODO FIXME
  3550. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  3551. info->class_flags = V3_DISTRIBUTABLE;
  3552. #endif
  3553. std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid));
  3554. DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category));
  3555. DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories));
  3556. DISTRHO_NAMESPACE::strncpy(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name));
  3557. DISTRHO_NAMESPACE::strncpy(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor));
  3558. DISTRHO_NAMESPACE::strncpy(info->version, getPluginVersion(), ARRAY_SIZE(info->version));
  3559. DISTRHO_NAMESPACE::strncpy(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version));
  3560. return V3_OK;
  3561. }
  3562. // ------------------------------------------------------------------------------------------------------------
  3563. // v3_plugin_factory_3
  3564. static v3_result V3_API get_class_info_utf16(void*, const int32_t idx, v3_class_info_3* const info)
  3565. {
  3566. d_stdout("dpf_factory::get_class_info_utf16 => %i %p", idx, info);
  3567. std::memset(info, 0, sizeof(*info));
  3568. DISTRHO_SAFE_ASSERT_RETURN(idx == 0, V3_INVALID_ARG);
  3569. info->cardinality = 0x7FFFFFFF;
  3570. // TODO FIXME
  3571. #ifdef DPF_VST3_USES_SEPARATE_CONTROLLER
  3572. info->class_flags = V3_DISTRIBUTABLE;
  3573. #endif
  3574. std::memcpy(info->class_id, dpf_tuid_class, sizeof(v3_tuid));
  3575. DISTRHO_NAMESPACE::strncpy(info->category, "Audio Module Class", ARRAY_SIZE(info->category));
  3576. DISTRHO_NAMESPACE::strncpy(info->sub_categories, getPluginCategories(), ARRAY_SIZE(info->sub_categories));
  3577. DISTRHO_NAMESPACE::strncpy_utf16(info->name, getPluginInfo().getName(), ARRAY_SIZE(info->name));
  3578. DISTRHO_NAMESPACE::strncpy_utf16(info->vendor, getPluginInfo().getMaker(), ARRAY_SIZE(info->vendor));
  3579. DISTRHO_NAMESPACE::strncpy_utf16(info->version, getPluginVersion(), ARRAY_SIZE(info->version));
  3580. DISTRHO_NAMESPACE::strncpy_utf16(info->sdk_version, "Travesty", ARRAY_SIZE(info->sdk_version));
  3581. return V3_OK;
  3582. }
  3583. static v3_result V3_API set_host_context(void* const self, v3_funknown** const context)
  3584. {
  3585. d_stdout("dpf_factory::set_host_context => %p %p", self, context);
  3586. dpf_factory* const factory = *static_cast<dpf_factory**>(self);
  3587. // unref old context if there is one
  3588. if (factory->hostContext != nullptr)
  3589. v3_cpp_obj_unref(factory->hostContext);
  3590. // store new context
  3591. factory->hostContext = context;
  3592. // make sure the object keeps being valid for a while
  3593. if (context != nullptr)
  3594. v3_cpp_obj_ref(context);
  3595. return V3_OK;
  3596. }
  3597. };
  3598. END_NAMESPACE_DISTRHO
  3599. // --------------------------------------------------------------------------------------------------------------------
  3600. // VST3 entry point
  3601. DISTRHO_PLUGIN_EXPORT
  3602. const void* GetPluginFactory(void);
  3603. const void* GetPluginFactory(void)
  3604. {
  3605. USE_NAMESPACE_DISTRHO;
  3606. dpf_factory** const factoryptr = new dpf_factory*;
  3607. *factoryptr = new dpf_factory;
  3608. return static_cast<void*>(factoryptr);
  3609. }
  3610. // --------------------------------------------------------------------------------------------------------------------
  3611. // OS specific module load
  3612. #if defined(DISTRHO_OS_MAC)
  3613. # define ENTRYFNNAME bundleEntry
  3614. # define EXITFNNAME bundleExit
  3615. #elif defined(DISTRHO_OS_WINDOWS)
  3616. # define ENTRYFNNAME InitDll
  3617. # define EXITFNNAME ExitDll
  3618. #else
  3619. # define ENTRYFNNAME ModuleEntry
  3620. # define EXITFNNAME ModuleExit
  3621. #endif
  3622. DISTRHO_PLUGIN_EXPORT
  3623. bool ENTRYFNNAME(void*);
  3624. bool ENTRYFNNAME(void*)
  3625. {
  3626. USE_NAMESPACE_DISTRHO;
  3627. // find plugin bundle
  3628. static String bundlePath;
  3629. if (bundlePath.isEmpty())
  3630. {
  3631. String tmpPath(getBinaryFilename());
  3632. tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
  3633. tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP));
  3634. DISTRHO_SAFE_ASSERT_RETURN(tmpPath.endsWith("/Contents"), true);
  3635. tmpPath.truncate(tmpPath.rfind('/'));
  3636. bundlePath = tmpPath;
  3637. d_nextBundlePath = bundlePath.buffer();
  3638. }
  3639. // init dummy plugin and set uniqueId
  3640. dpf_tuid_class[2] = dpf_tuid_component[2] = dpf_tuid_controller[2]
  3641. = dpf_tuid_processor[2] = dpf_tuid_view[2] = getPluginInfo().getUniqueId();
  3642. return true;
  3643. }
  3644. DISTRHO_PLUGIN_EXPORT
  3645. bool EXITFNNAME(void);
  3646. bool EXITFNNAME(void)
  3647. {
  3648. return true;
  3649. }
  3650. #undef ENTRYFNNAME
  3651. #undef EXITFNNAME
  3652. // --------------------------------------------------------------------------------------------------------------------