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.

5001 lines
187KB

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