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.

4972 lines
185KB

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