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