Mercurial > hg > pub > prymula > com
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 ¶mId, &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 // -------------------------------------------------------------------------------------------------------------------- |