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.

4245 lines
156KB

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