comparison DPF-Prymula-audioplugins/dpf/distrho/src/DistrhoPluginVST3.cpp @ 3:84e66ea83026

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