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.

4896 lines
182KB

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