DistrhoPluginVST2.cpp (57357B)
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 #include "DistrhoPluginInternal.hpp" 18 #include "DistrhoPluginVST.hpp" 19 #include "../DistrhoPluginUtils.hpp" 20 #include "../extra/ScopedSafeLocale.hpp" 21 #include "../extra/ScopedPointer.hpp" 22 23 #if DISTRHO_PLUGIN_HAS_UI 24 # include "DistrhoUIInternal.hpp" 25 # include "../extra/RingBuffer.hpp" 26 #endif 27 28 #include <clocale> 29 #include <map> 30 #include <string> 31 #include <vector> 32 33 #ifndef __cdecl 34 # define __cdecl 35 #endif 36 37 #include "xaymar-vst2/vst.h" 38 39 START_NAMESPACE_DISTRHO 40 41 // -------------------------------------------------------------------------------------------------------------------- 42 43 extern "C" { 44 45 // define the midi stuff ourselves 46 typedef struct _VstMidiEvent { 47 int32_t type; 48 int32_t byteSize; 49 int32_t deltaFrames; 50 int32_t _ignore1[3]; 51 char midiData[4]; 52 char _ignore2[4]; 53 } VstMidiEvent; 54 55 typedef union _VstEvent { 56 int32_t type; 57 VstMidiEvent midi; // type 1 58 } VstEvent; 59 60 typedef struct _HostVstEvents { 61 int32_t numEvents; 62 void* reserved; 63 const VstEvent* events[]; 64 } HostVstEvents; 65 66 typedef struct _PluginVstEvents { 67 int32_t numEvents; 68 void* reserved; 69 VstEvent* events[1]; 70 } PluginVstEvents; 71 72 // info from online documentation of VST provided by Steinberg 73 typedef struct _VstTimeInfo { 74 double samplePos; 75 double sampleRate; 76 double nanoSeconds; 77 double ppqPos; 78 double tempo; 79 double barStartPos; 80 double cycleStartPos; 81 double cycleEndPos; 82 int32_t timeSigNumerator; 83 int32_t timeSigDenominator; 84 int32_t smpteOffset; 85 int32_t smpteFrameRate; 86 int32_t samplesToNextClock; 87 int32_t flags; 88 } VstTimeInfo; 89 90 } // extern "C" 91 92 // -------------------------------------------------------------------------------------------------------------------- 93 94 typedef std::map<const String, String> StringMap; 95 96 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 97 static constexpr const writeMidiFunc writeMidiCallback = nullptr; 98 #endif 99 #if ! DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 100 static constexpr const requestParameterValueChangeFunc requestParameterValueChangeCallback = nullptr; 101 #endif 102 103 // -------------------------------------------------------------------------------------------------------------------- 104 105 struct ParameterAndNotesHelper 106 { 107 float* parameterValues; 108 #if DISTRHO_PLUGIN_HAS_UI 109 bool* parameterChecks; 110 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 111 SmallStackBuffer notesRingBuffer; 112 #endif 113 #endif 114 115 ParameterAndNotesHelper() 116 : parameterValues(nullptr) 117 #if DISTRHO_PLUGIN_HAS_UI 118 , parameterChecks(nullptr) 119 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 120 , notesRingBuffer(StackBuffer_INIT) 121 #endif 122 #endif 123 { 124 #if DISTRHO_PLUGIN_HAS_UI && DISTRHO_PLUGIN_WANT_MIDI_INPUT && ! defined(DISTRHO_PROPER_CPP11_SUPPORT) 125 std::memset(¬esRingBuffer, 0, sizeof(notesRingBuffer)); 126 #endif 127 } 128 129 virtual ~ParameterAndNotesHelper() 130 { 131 if (parameterValues != nullptr) 132 { 133 delete[] parameterValues; 134 parameterValues = nullptr; 135 } 136 #if DISTRHO_PLUGIN_HAS_UI 137 if (parameterChecks != nullptr) 138 { 139 delete[] parameterChecks; 140 parameterChecks = nullptr; 141 } 142 #endif 143 } 144 145 #if DISTRHO_PLUGIN_WANT_STATE 146 virtual void setStateFromUI(const char* key, const char* value) = 0; 147 #endif 148 }; 149 150 #if DISTRHO_PLUGIN_HAS_UI 151 // -------------------------------------------------------------------------------------------------------------------- 152 153 #if ! DISTRHO_PLUGIN_WANT_MIDI_INPUT 154 static const sendNoteFunc sendNoteCallback = nullptr; 155 #endif 156 #if ! DISTRHO_PLUGIN_WANT_STATE 157 static const setStateFunc setStateCallback = nullptr; 158 #endif 159 160 class UIVst 161 { 162 public: 163 UIVst(const vst_host_callback audioMaster, 164 vst_effect* const effect, 165 ParameterAndNotesHelper* const uiHelper, 166 PluginExporter* const plugin, 167 const intptr_t winId, const float scaleFactor) 168 : fAudioMaster(audioMaster), 169 fEffect(effect), 170 fUiHelper(uiHelper), 171 fPlugin(plugin), 172 fUI(this, winId, plugin->getSampleRate(), 173 editParameterCallback, 174 setParameterCallback, 175 setStateCallback, 176 sendNoteCallback, 177 setSizeCallback, 178 nullptr, // TODO file request 179 d_nextBundlePath, 180 plugin->getInstancePointer(), 181 scaleFactor) 182 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI 183 , fKeyboardModifiers(0) 184 #endif 185 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 186 , fNotesRingBuffer() 187 #endif 188 { 189 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 190 fNotesRingBuffer.setRingBuffer(&uiHelper->notesRingBuffer, false); 191 #endif 192 } 193 194 // ---------------------------------------------------------------------------------------------------------------- 195 196 void idle() 197 { 198 for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i) 199 { 200 if (fUiHelper->parameterChecks[i]) 201 { 202 fUiHelper->parameterChecks[i] = false; 203 fUI.parameterChanged(i, fUiHelper->parameterValues[i]); 204 } 205 } 206 207 fUI.plugin_idle(); 208 } 209 210 int16_t getWidth() const 211 { 212 return fUI.getWidth(); 213 } 214 215 int16_t getHeight() const 216 { 217 return fUI.getHeight(); 218 } 219 220 double getScaleFactor() const 221 { 222 return fUI.getScaleFactor(); 223 } 224 225 void setSampleRate(const double newSampleRate) 226 { 227 fUI.setSampleRate(newSampleRate, true); 228 } 229 230 void notifyScaleFactorChanged(const double scaleFactor) 231 { 232 fUI.notifyScaleFactorChanged(scaleFactor); 233 } 234 235 // ---------------------------------------------------------------------------------------------------------------- 236 // functions called from the plugin side, may block 237 238 #if DISTRHO_PLUGIN_WANT_STATE 239 void setStateFromPlugin(const char* const key, const char* const value) 240 { 241 fUI.stateChanged(key, value); 242 } 243 #endif 244 245 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI 246 int handlePluginKeyEvent(const bool down, const int32_t index, const intptr_t value) 247 { 248 d_stdout("handlePluginKeyEvent %i %i %li\n", down, index, (long int)value); 249 250 using namespace DGL_NAMESPACE; 251 252 bool special; 253 const uint key = translateVstKeyCode(special, index, static_cast<int32_t>(value)); 254 255 switch (key) 256 { 257 case kKeyShiftL: 258 case kKeyShiftR: 259 if (down) 260 fKeyboardModifiers |= kModifierShift; 261 else 262 fKeyboardModifiers &= ~kModifierShift; 263 break; 264 case kKeyControlL: 265 case kKeyControlR: 266 if (down) 267 fKeyboardModifiers |= kModifierControl; 268 else 269 fKeyboardModifiers &= ~kModifierControl; 270 break; 271 case kKeyAltL: 272 case kKeyAltR: 273 if (down) 274 fKeyboardModifiers |= kModifierAlt; 275 else 276 fKeyboardModifiers &= ~kModifierAlt; 277 break; 278 } 279 280 return fUI.handlePluginKeyboardVST(down, special, key, 281 value >= 0 ? static_cast<uint>(value) : 0, 282 fKeyboardModifiers) ? 1 : 0; 283 } 284 #endif // !DISTRHO_PLUGIN_HAS_EXTERNAL_UI 285 286 // ---------------------------------------------------------------------------------------------------------------- 287 288 protected: 289 inline intptr_t hostCallback(const VST_HOST_OPCODE opcode, 290 const int32_t index = 0, 291 const intptr_t value = 0, 292 void* const ptr = nullptr, 293 const float opt = 0.0f) const 294 { 295 return fAudioMaster(fEffect, opcode, index, value, ptr, opt); 296 } 297 298 void editParameter(const uint32_t index, const bool started) const 299 { 300 hostCallback(started ? VST_HOST_OPCODE_2B : VST_HOST_OPCODE_2C, index); 301 } 302 303 void setParameterValue(const uint32_t index, const float realValue) 304 { 305 const ParameterRanges& ranges(fPlugin->getParameterRanges(index)); 306 const float perValue = ranges.getNormalizedValue(realValue); 307 308 fPlugin->setParameterValue(index, realValue); 309 hostCallback(VST_HOST_OPCODE_00, index, 0, nullptr, perValue); 310 } 311 312 void setSize(uint width, uint height) 313 { 314 #ifdef DISTRHO_OS_MAC 315 const double scaleFactor = fUI.getScaleFactor(); 316 width /= scaleFactor; 317 height /= scaleFactor; 318 #endif 319 hostCallback(VST_HOST_OPCODE_0F, width, height); 320 } 321 322 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 323 void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity) 324 { 325 uint8_t midiData[3]; 326 midiData[0] = (velocity != 0 ? 0x90 : 0x80) | channel; 327 midiData[1] = note; 328 midiData[2] = velocity; 329 fNotesRingBuffer.writeCustomData(midiData, 3); 330 fNotesRingBuffer.commitWrite(); 331 } 332 #endif 333 334 #if DISTRHO_PLUGIN_WANT_STATE 335 void setState(const char* const key, const char* const value) 336 { 337 fUiHelper->setStateFromUI(key, value); 338 } 339 #endif 340 341 private: 342 // Vst stuff 343 const vst_host_callback fAudioMaster; 344 vst_effect* const fEffect; 345 ParameterAndNotesHelper* const fUiHelper; 346 PluginExporter* const fPlugin; 347 348 // Plugin UI 349 UIExporter fUI; 350 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI 351 uint16_t fKeyboardModifiers; 352 #endif 353 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 354 RingBufferControl<SmallStackBuffer> fNotesRingBuffer; 355 #endif 356 357 // ---------------------------------------------------------------------------------------------------------------- 358 // Callbacks 359 360 static void editParameterCallback(void* const ptr, const uint32_t index, const bool started) 361 { 362 static_cast<UIVst*>(ptr)->editParameter(index, started); 363 } 364 365 static void setParameterCallback(void* const ptr, const uint32_t rindex, const float value) 366 { 367 static_cast<UIVst*>(ptr)->setParameterValue(rindex, value); 368 } 369 370 static void setSizeCallback(void* const ptr, const uint width, const uint height) 371 { 372 static_cast<UIVst*>(ptr)->setSize(width, height); 373 } 374 375 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 376 static void sendNoteCallback(void* const ptr, const uint8_t channel, const uint8_t note, const uint8_t velocity) 377 { 378 static_cast<UIVst*>(ptr)->sendNote(channel, note, velocity); 379 } 380 #endif 381 382 #if DISTRHO_PLUGIN_WANT_STATE 383 static void setStateCallback(void* const ptr, const char* const key, const char* const value) 384 { 385 static_cast<UIVst*>(ptr)->setState(key, value); 386 } 387 #endif 388 }; 389 #endif // DISTRHO_PLUGIN_HAS_UI 390 391 // -------------------------------------------------------------------------------------------------------------------- 392 393 class PluginVst : public ParameterAndNotesHelper 394 { 395 public: 396 PluginVst(const vst_host_callback audioMaster, vst_effect* const effect) 397 : fPlugin(this, writeMidiCallback, requestParameterValueChangeCallback, nullptr), 398 fAudioMaster(audioMaster), 399 fEffect(effect) 400 { 401 std::memset(fProgramName, 0, sizeof(fProgramName)); 402 std::strcpy(fProgramName, "Default"); 403 404 const uint32_t parameterCount = fPlugin.getParameterCount(); 405 406 if (parameterCount != 0) 407 { 408 parameterValues = new float[parameterCount]; 409 410 for (uint32_t i=0; i < parameterCount; ++i) 411 parameterValues[i] = NAN; 412 } 413 414 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 415 fMidiEventCount = 0; 416 #endif 417 418 #if DISTRHO_PLUGIN_HAS_UI 419 fVstUI = nullptr; 420 fVstRect.top = 0; 421 fVstRect.left = 0; 422 fVstRect.bottom = 0; 423 fVstRect.right = 0; 424 fLastScaleFactor = 0.0f; 425 426 if (parameterCount != 0) 427 { 428 parameterChecks = new bool[parameterCount]; 429 memset(parameterChecks, 0, sizeof(bool)*parameterCount); 430 } 431 432 #ifdef DISTRHO_OS_MAC 433 #ifdef __LP64__ 434 fUsingNsView = true; 435 #else 436 #ifndef DISTRHO_NO_WARNINGS 437 #warning 32bit VST UIs on macOS only work if the host supports "hasCockosViewAsConfig" 438 #endif 439 fUsingNsView = false; 440 #endif 441 #endif // DISTRHO_OS_MAC 442 443 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 444 fNotesRingBuffer.setRingBuffer(¬esRingBuffer, true); 445 #endif 446 #endif // DISTRHO_PLUGIN_HAS_UI 447 448 #if DISTRHO_PLUGIN_WANT_STATE 449 fStateChunk = nullptr; 450 451 for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i) 452 { 453 const String& dkey(fPlugin.getStateKey(i)); 454 fStateMap[dkey] = fPlugin.getStateDefaultValue(i); 455 } 456 #endif 457 } 458 459 ~PluginVst() 460 { 461 #if DISTRHO_PLUGIN_WANT_STATE 462 if (fStateChunk != nullptr) 463 { 464 delete[] fStateChunk; 465 fStateChunk = nullptr; 466 } 467 468 fStateMap.clear(); 469 #endif 470 } 471 472 intptr_t vst_dispatcher(const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt) 473 { 474 #if DISTRHO_PLUGIN_WANT_STATE 475 intptr_t ret = 0; 476 #endif 477 478 switch (opcode) 479 { 480 case VST_EFFECT_OPCODE_03: // get program 481 return 0; 482 483 case VST_EFFECT_OPCODE_04: // set program name 484 if (char* const programName = (char*)ptr) 485 { 486 d_strncpy(fProgramName, programName, 32); 487 return 1; 488 } 489 break; 490 491 case VST_EFFECT_OPCODE_05: // get program name 492 if (char* const programName = (char*)ptr) 493 { 494 d_strncpy(programName, fProgramName, 24); 495 return 1; 496 } 497 break; 498 499 case VST_EFFECT_OPCODE_1D: // get program name indexed 500 if (char* const programName = (char*)ptr) 501 { 502 d_strncpy(programName, fProgramName, 24); 503 return 1; 504 } 505 break; 506 507 case VST_EFFECT_OPCODE_PARAM_GETVALUE: 508 if (ptr != nullptr && index < static_cast<int32_t>(fPlugin.getParameterCount())) 509 { 510 const uint32_t hints = fPlugin.getParameterHints(index); 511 float value = fPlugin.getParameterValue(index); 512 513 if (hints & kParameterIsBoolean) 514 { 515 const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); 516 const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; 517 518 value = value > midRange ? ranges.max : ranges.min; 519 } 520 else if (hints & kParameterIsInteger) 521 { 522 value = std::round(value); 523 } 524 525 const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index)); 526 527 for (uint8_t i = 0; i < enumValues.count; ++i) 528 { 529 if (d_isNotEqual(value, enumValues.values[i].value)) 530 continue; 531 532 strncpy((char*)ptr, enumValues.values[i].label.buffer(), 24); 533 return 1; 534 } 535 536 if (hints & kParameterIsInteger) 537 snprintf_i32((char*)ptr, (int32_t)value, 24); 538 else 539 snprintf_f32((char*)ptr, value, 24); 540 541 return 1; 542 } 543 break; 544 545 case VST_EFFECT_OPCODE_SET_SAMPLE_RATE: 546 fPlugin.setSampleRate(opt, true); 547 548 #if DISTRHO_PLUGIN_HAS_UI 549 if (fVstUI != nullptr) 550 fVstUI->setSampleRate(opt); 551 #endif 552 break; 553 554 case VST_EFFECT_OPCODE_SET_BLOCK_SIZE: 555 fPlugin.setBufferSize(value, true); 556 break; 557 558 case VST_EFFECT_OPCODE_SUSPEND: 559 if (value != 0) 560 { 561 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 562 fMidiEventCount = 0; 563 564 // tell host we want MIDI events 565 hostCallback(VST_HOST_OPCODE_06); 566 #endif 567 568 // deactivate for possible changes 569 fPlugin.deactivateIfNeeded(); 570 571 // check if something changed 572 const uint32_t bufferSize = static_cast<uint32_t>(hostCallback(VST_HOST_OPCODE_11)); 573 const double sampleRate = static_cast<double>(hostCallback(VST_HOST_OPCODE_10)); 574 575 if (bufferSize != 0) 576 fPlugin.setBufferSize(bufferSize, true); 577 578 if (sampleRate != 0.0) 579 fPlugin.setSampleRate(sampleRate, true); 580 581 fPlugin.activate(); 582 } 583 else 584 { 585 fPlugin.deactivate(); 586 } 587 break; 588 589 #if DISTRHO_PLUGIN_HAS_UI 590 case VST_EFFECT_OPCODE_WINDOW_GETRECT: 591 if (fVstUI != nullptr) 592 { 593 fVstRect.right = fVstUI->getWidth(); 594 fVstRect.bottom = fVstUI->getHeight(); 595 #ifdef DISTRHO_OS_MAC 596 const double scaleFactor = fVstUI->getScaleFactor(); 597 fVstRect.right /= scaleFactor; 598 fVstRect.bottom /= scaleFactor; 599 #endif 600 } 601 else 602 { 603 double scaleFactor = fLastScaleFactor; 604 #if defined(DISTRHO_UI_DEFAULT_WIDTH) && defined(DISTRHO_UI_DEFAULT_HEIGHT) 605 if (d_isZero(scaleFactor)) 606 scaleFactor = 1.0; 607 fVstRect.right = DISTRHO_UI_DEFAULT_WIDTH * scaleFactor; 608 fVstRect.bottom = DISTRHO_UI_DEFAULT_HEIGHT * scaleFactor; 609 #else 610 UIExporter tmpUI(nullptr, 0, fPlugin.getSampleRate(), 611 nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, d_nextBundlePath, 612 fPlugin.getInstancePointer(), scaleFactor); 613 fVstRect.right = tmpUI.getWidth(); 614 fVstRect.bottom = tmpUI.getHeight(); 615 scaleFactor = tmpUI.getScaleFactor(); 616 tmpUI.quit(); 617 #endif 618 #ifdef DISTRHO_OS_MAC 619 fVstRect.right /= scaleFactor; 620 fVstRect.bottom /= scaleFactor; 621 #endif 622 } 623 *(vst_rect**)ptr = &fVstRect; 624 return 1; 625 626 case VST_EFFECT_OPCODE_WINDOW_CREATE: 627 delete fVstUI; // for hosts which don't pair create/destroy calls (Minihost Modular) 628 fVstUI = nullptr; 629 630 #ifdef DISTRHO_OS_MAC 631 if (! fUsingNsView) 632 { 633 d_stderr("Host doesn't support hasCockosViewAsConfig, cannot use UI"); 634 return 0; 635 } 636 #endif 637 fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr, fLastScaleFactor); 638 639 #if DISTRHO_PLUGIN_WANT_FULL_STATE 640 // Update current state from plugin side 641 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) 642 { 643 const String& key = cit->first; 644 fStateMap[key] = fPlugin.getStateValue(key); 645 } 646 #endif 647 648 #if DISTRHO_PLUGIN_WANT_STATE 649 // Set state 650 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) 651 { 652 const String& key(cit->first); 653 const String& value(cit->second); 654 655 // TODO skip DSP only states 656 657 fVstUI->setStateFromPlugin(key, value); 658 } 659 #endif 660 661 for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) 662 setParameterValueFromPlugin(i, fPlugin.getParameterValue(i)); 663 664 fVstUI->idle(); 665 return 1; 666 667 case VST_EFFECT_OPCODE_WINDOW_DESTROY: 668 if (fVstUI != nullptr) 669 { 670 delete fVstUI; 671 fVstUI = nullptr; 672 return 1; 673 } 674 break; 675 676 case VST_EFFECT_OPCODE_13: // window idle 677 if (fVstUI != nullptr) 678 fVstUI->idle(); 679 break; 680 681 #if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI 682 case VST_EFFECT_OPCODE_3B: // key down 683 if (fVstUI != nullptr) 684 return fVstUI->handlePluginKeyEvent(true, index, value); 685 break; 686 687 case VST_EFFECT_OPCODE_3C: // key up 688 if (fVstUI != nullptr) 689 return fVstUI->handlePluginKeyEvent(false, index, value); 690 break; 691 #endif 692 #endif // DISTRHO_PLUGIN_HAS_UI 693 694 #if DISTRHO_PLUGIN_WANT_STATE 695 case VST_EFFECT_OPCODE_17: // get chunk 696 { 697 if (ptr == nullptr) 698 return 0; 699 700 if (fStateChunk != nullptr) 701 { 702 delete[] fStateChunk; 703 fStateChunk = nullptr; 704 } 705 706 const uint32_t paramCount = fPlugin.getParameterCount(); 707 708 if (fPlugin.getStateCount() == 0 && paramCount == 0) 709 { 710 fStateChunk = new char[1]; 711 fStateChunk[0] = '\0'; 712 ret = 1; 713 } 714 else 715 { 716 #if DISTRHO_PLUGIN_WANT_FULL_STATE 717 // Update current state 718 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) 719 { 720 const String& key = cit->first; 721 fStateMap[key] = fPlugin.getStateValue(key); 722 } 723 #endif 724 725 String chunkStr; 726 727 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit) 728 { 729 const String& key = cit->first; 730 const String& value = cit->second; 731 732 // join key and value 733 String tmpStr; 734 tmpStr = key; 735 tmpStr += "\xff"; 736 tmpStr += value; 737 tmpStr += "\xff"; 738 739 chunkStr += tmpStr; 740 } 741 742 if (paramCount != 0) 743 { 744 // add another separator 745 chunkStr += "\xff"; 746 747 for (uint32_t i=0; i<paramCount; ++i) 748 { 749 if (fPlugin.isParameterOutputOrTrigger(i)) 750 continue; 751 752 // join key and value 753 String tmpStr; 754 tmpStr = fPlugin.getParameterSymbol(i); 755 tmpStr += "\xff"; 756 tmpStr += String(fPlugin.getParameterValue(i)); 757 tmpStr += "\xff"; 758 759 chunkStr += tmpStr; 760 } 761 } 762 763 const std::size_t chunkSize = chunkStr.length()+1; 764 765 fStateChunk = new char[chunkSize]; 766 std::memcpy(fStateChunk, chunkStr.buffer(), chunkStr.length()); 767 fStateChunk[chunkSize-1] = '\0'; 768 769 for (std::size_t i=0; i<chunkSize; ++i) 770 { 771 if (fStateChunk[i] == '\xff') 772 fStateChunk[i] = '\0'; 773 } 774 775 ret = chunkSize; 776 } 777 778 *(void**)ptr = fStateChunk; 779 return ret; 780 } 781 782 case VST_EFFECT_OPCODE_18: // set chunk 783 { 784 if (value <= 1 || ptr == nullptr) 785 return 0; 786 787 const size_t chunkSize = static_cast<size_t>(value); 788 789 const char* key = (const char*)ptr; 790 const char* value = nullptr; 791 size_t size, bytesRead = 0; 792 793 while (bytesRead < chunkSize) 794 { 795 if (key[0] == '\0') 796 break; 797 798 size = std::strlen(key)+1; 799 value = key + size; 800 bytesRead += size; 801 802 setStateFromUI(key, value); 803 804 #if DISTRHO_PLUGIN_HAS_UI 805 if (fVstUI != nullptr) 806 { 807 // TODO skip DSP only states 808 fVstUI->setStateFromPlugin(key, value); 809 } 810 #endif 811 812 // get next key 813 size = std::strlen(value)+1; 814 key = value + size; 815 bytesRead += size; 816 } 817 818 const uint32_t paramCount = fPlugin.getParameterCount(); 819 820 if (bytesRead+4 < chunkSize && paramCount != 0) 821 { 822 ++key; 823 float fvalue; 824 825 while (bytesRead < chunkSize) 826 { 827 if (key[0] == '\0') 828 break; 829 830 size = std::strlen(key)+1; 831 value = key + size; 832 bytesRead += size; 833 834 // find parameter with this symbol, and set its value 835 for (uint32_t i=0; i<paramCount; ++i) 836 { 837 if (fPlugin.isParameterOutputOrTrigger(i)) 838 continue; 839 if (fPlugin.getParameterSymbol(i) != key) 840 continue; 841 842 if (fPlugin.getParameterHints(i) & kParameterIsInteger) 843 { 844 fvalue = std::atoi(value); 845 } 846 else 847 { 848 const ScopedSafeLocale ssl; 849 fvalue = std::atof(value); 850 } 851 852 fPlugin.setParameterValue(i, fvalue); 853 #if DISTRHO_PLUGIN_HAS_UI 854 if (fVstUI != nullptr) 855 setParameterValueFromPlugin(i, fvalue); 856 #endif 857 break; 858 } 859 860 // get next key 861 size = std::strlen(value)+1; 862 key = value + size; 863 bytesRead += size; 864 } 865 } 866 867 return 1; 868 } 869 #endif // DISTRHO_PLUGIN_WANT_STATE 870 871 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 872 case VST_EFFECT_OPCODE_19: // process events 873 if (! fPlugin.isActive()) 874 { 875 // host has not activated the plugin yet, nasty! 876 vst_dispatcher(VST_EFFECT_OPCODE_SUSPEND, 0, 1, nullptr, 0.0f); 877 } 878 879 if (const HostVstEvents* const events = (const HostVstEvents*)ptr) 880 { 881 if (events->numEvents == 0) 882 break; 883 884 for (int i=0, count=events->numEvents; i < count; ++i) 885 { 886 const VstEvent* const vstEvent = events->events[i]; 887 888 if (vstEvent == nullptr) 889 break; 890 if (vstEvent->type != 1) 891 continue; 892 if (fMidiEventCount >= kMaxMidiEvents) 893 break; 894 895 const VstMidiEvent& vstMidiEvent(events->events[i]->midi); 896 897 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); 898 midiEvent.frame = vstMidiEvent.deltaFrames; 899 midiEvent.size = 3; 900 std::memcpy(midiEvent.data, vstMidiEvent.midiData, sizeof(uint8_t)*3); 901 } 902 } 903 break; 904 #endif 905 906 case VST_EFFECT_OPCODE_PARAM_ISAUTOMATABLE: 907 if (index < static_cast<int32_t>(fPlugin.getParameterCount())) 908 { 909 const uint32_t hints(fPlugin.getParameterHints(index)); 910 911 // must be automatable, and not output 912 if ((hints & kParameterIsAutomatable) != 0 && (hints & kParameterIsOutput) == 0) 913 return 1; 914 } 915 break; 916 917 case VST_EFFECT_OPCODE_SUPPORTS: 918 if (const char* const canDo = (const char*)ptr) 919 { 920 #if defined(DISTRHO_OS_MAC) && DISTRHO_PLUGIN_HAS_UI 921 if (std::strcmp(canDo, "hasCockosViewAsConfig") == 0) 922 { 923 fUsingNsView = true; 924 return 0xbeef0000; 925 } 926 #endif 927 #ifndef DISTRHO_OS_MAC 928 if (std::strcmp(canDo, "supportsViewDpiScaling") == 0) 929 return 1; 930 #endif 931 if (std::strcmp(canDo, "receiveVstEvents") == 0 || 932 std::strcmp(canDo, "receiveVstMidiEvent") == 0) 933 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 934 return 1; 935 #else 936 return -1; 937 #endif 938 if (std::strcmp(canDo, "sendVstEvents") == 0 || 939 std::strcmp(canDo, "sendVstMidiEvent") == 0) 940 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 941 return 1; 942 #else 943 return -1; 944 #endif 945 if (std::strcmp(canDo, "receiveVstTimeInfo") == 0) 946 #if DISTRHO_PLUGIN_WANT_TIMEPOS 947 return 1; 948 #else 949 return -1; 950 #endif 951 if (std::strcmp(canDo, "offline") == 0) 952 return -1; 953 } 954 break; 955 956 case VST_EFFECT_OPCODE_CUSTOM: 957 #if DISTRHO_PLUGIN_HAS_UI && !defined(DISTRHO_OS_MAC) 958 if (index == d_cconst('P', 'r', 'e', 'S') && value == d_cconst('A', 'e', 'C', 's')) 959 { 960 if (d_isEqual(fLastScaleFactor, opt)) 961 break; 962 963 fLastScaleFactor = opt; 964 965 if (fVstUI != nullptr) 966 fVstUI->notifyScaleFactorChanged(opt); 967 } 968 #endif 969 break; 970 971 //case effStartProcess: 972 //case effStopProcess: 973 // unused 974 // break; 975 } 976 977 return 0; 978 } 979 980 float vst_getParameter(const uint32_t index) 981 { 982 const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); 983 return ranges.getNormalizedValue(fPlugin.getParameterValue(index)); 984 } 985 986 void vst_setParameter(const uint32_t index, const float value) 987 { 988 const uint32_t hints = fPlugin.getParameterHints(index); 989 const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); 990 991 // TODO figure out how to detect kVstParameterUsesIntegerMinMax host support, and skip normalization 992 float realValue = ranges.getUnnormalizedValue(value); 993 994 if (hints & kParameterIsBoolean) 995 { 996 const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f; 997 realValue = realValue > midRange ? ranges.max : ranges.min; 998 } 999 1000 if (hints & kParameterIsInteger) 1001 { 1002 realValue = std::round(realValue); 1003 } 1004 1005 fPlugin.setParameterValue(index, realValue); 1006 1007 #if DISTRHO_PLUGIN_HAS_UI 1008 if (fVstUI != nullptr) 1009 setParameterValueFromPlugin(index, realValue); 1010 #endif 1011 } 1012 1013 void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames) 1014 { 1015 if (! fPlugin.isActive()) 1016 { 1017 // host has not activated the plugin yet, nasty! 1018 vst_dispatcher(VST_EFFECT_OPCODE_SUSPEND, 0, 1, nullptr, 0.0f); 1019 } 1020 1021 if (sampleFrames <= 0) 1022 { 1023 updateParameterOutputsAndTriggers(); 1024 return; 1025 } 1026 1027 #if DISTRHO_PLUGIN_WANT_TIMEPOS 1028 static constexpr const int kWantVstTimeFlags = 0x2602; 1029 1030 if (const VstTimeInfo* const vstTimeInfo = (const VstTimeInfo*)hostCallback(VST_HOST_OPCODE_07, 0, kWantVstTimeFlags)) 1031 { 1032 fTimePosition.frame = vstTimeInfo->samplePos; 1033 fTimePosition.playing = vstTimeInfo->flags & 0x2; 1034 1035 // ticksPerBeat is not possible with VST2 1036 fTimePosition.bbt.ticksPerBeat = 1920.0; 1037 1038 if (vstTimeInfo->flags & 0x400) 1039 fTimePosition.bbt.beatsPerMinute = vstTimeInfo->tempo; 1040 else 1041 fTimePosition.bbt.beatsPerMinute = 120.0; 1042 1043 if ((vstTimeInfo->flags & 0x2200) == 0x2200) 1044 { 1045 const double ppqPos = std::abs(vstTimeInfo->ppqPos); 1046 const int ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator; 1047 const double barBeats = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * vstTimeInfo->timeSigNumerator; 1048 const double rest = std::fmod(barBeats, 1.0); 1049 1050 fTimePosition.bbt.valid = true; 1051 fTimePosition.bbt.bar = static_cast<int32_t>(ppqPos) / ppqPerBar + 1; 1052 fTimePosition.bbt.beat = static_cast<int32_t>(barBeats - rest + 0.5) + 1; 1053 fTimePosition.bbt.tick = rest * fTimePosition.bbt.ticksPerBeat; 1054 fTimePosition.bbt.beatsPerBar = vstTimeInfo->timeSigNumerator; 1055 fTimePosition.bbt.beatType = vstTimeInfo->timeSigDenominator; 1056 1057 if (vstTimeInfo->ppqPos < 0.0) 1058 { 1059 --fTimePosition.bbt.bar; 1060 fTimePosition.bbt.beat = vstTimeInfo->timeSigNumerator - fTimePosition.bbt.beat + 1; 1061 fTimePosition.bbt.tick = fTimePosition.bbt.ticksPerBeat - fTimePosition.bbt.tick - 1; 1062 } 1063 } 1064 else 1065 { 1066 fTimePosition.bbt.valid = false; 1067 fTimePosition.bbt.bar = 1; 1068 fTimePosition.bbt.beat = 1; 1069 fTimePosition.bbt.tick = 0.0; 1070 fTimePosition.bbt.beatsPerBar = 4.0f; 1071 fTimePosition.bbt.beatType = 4.0f; 1072 } 1073 1074 fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat* 1075 fTimePosition.bbt.beatsPerBar* 1076 (fTimePosition.bbt.bar-1); 1077 1078 fPlugin.setTimePosition(fTimePosition); 1079 } 1080 #endif 1081 1082 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 1083 #if DISTRHO_PLUGIN_HAS_UI 1084 if (fMidiEventCount != kMaxMidiEvents && fNotesRingBuffer.isDataAvailableForReading()) 1085 { 1086 uint8_t midiData[3]; 1087 const uint32_t frame = fMidiEventCount != 0 ? fMidiEvents[fMidiEventCount - 1].frame : 0; 1088 1089 while (fNotesRingBuffer.isDataAvailableForReading()) 1090 { 1091 if (! fNotesRingBuffer.readCustomData(midiData, 3)) 1092 break; 1093 1094 MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]); 1095 midiEvent.frame = frame; 1096 midiEvent.size = 3; 1097 std::memcpy(midiEvent.data, midiData, 3); 1098 1099 if (fMidiEventCount == kMaxMidiEvents) 1100 break; 1101 } 1102 } 1103 #endif 1104 1105 fPlugin.run(inputs, outputs, sampleFrames, fMidiEvents, fMidiEventCount); 1106 fMidiEventCount = 0; 1107 #else 1108 fPlugin.run(inputs, outputs, sampleFrames); 1109 #endif 1110 1111 updateParameterOutputsAndTriggers(); 1112 } 1113 1114 // ---------------------------------------------------------------------------------------------------------------- 1115 1116 friend class UIVst; 1117 1118 private: 1119 // Plugin 1120 PluginExporter fPlugin; 1121 1122 // VST stuff 1123 const vst_host_callback fAudioMaster; 1124 vst_effect* const fEffect; 1125 1126 // Temporary data 1127 char fProgramName[32]; 1128 1129 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 1130 uint32_t fMidiEventCount; 1131 MidiEvent fMidiEvents[kMaxMidiEvents]; 1132 #endif 1133 1134 #if DISTRHO_PLUGIN_WANT_TIMEPOS 1135 TimePosition fTimePosition; 1136 #endif 1137 1138 // UI stuff 1139 #if DISTRHO_PLUGIN_HAS_UI 1140 UIVst* fVstUI; 1141 vst_rect fVstRect; 1142 float fLastScaleFactor; 1143 #ifdef DISTRHO_OS_MAC 1144 bool fUsingNsView; 1145 #endif 1146 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT 1147 RingBufferControl<SmallStackBuffer> fNotesRingBuffer; 1148 #endif 1149 #endif 1150 1151 #if DISTRHO_PLUGIN_WANT_STATE 1152 char* fStateChunk; 1153 StringMap fStateMap; 1154 #endif 1155 1156 // ---------------------------------------------------------------------------------------------------------------- 1157 // host callback 1158 1159 intptr_t hostCallback(const VST_HOST_OPCODE opcode, 1160 const int32_t index = 0, 1161 const intptr_t value = 0, 1162 void* const ptr = nullptr, 1163 const float opt = 0.0f) 1164 { 1165 return fAudioMaster(fEffect, opcode, index, value, ptr, opt); 1166 } 1167 1168 // ---------------------------------------------------------------------------------------------------------------- 1169 // functions called from the plugin side, RT no block 1170 1171 void updateParameterOutputsAndTriggers() 1172 { 1173 float curValue, defValue; 1174 1175 for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i) 1176 { 1177 if (fPlugin.isParameterOutput(i)) 1178 { 1179 // NOTE: no output parameter support in VST2, simulate it here 1180 curValue = fPlugin.getParameterValue(i); 1181 1182 if (d_isEqual(curValue, parameterValues[i])) 1183 continue; 1184 1185 #if DISTRHO_PLUGIN_HAS_UI 1186 if (fVstUI != nullptr) 1187 setParameterValueFromPlugin(i, curValue); 1188 else 1189 #endif 1190 parameterValues[i] = curValue; 1191 1192 #ifndef DPF_VST_SHOW_PARAMETER_OUTPUTS 1193 // skip automating parameter outputs from plugin if we disable them on VST 1194 continue; 1195 #endif 1196 } 1197 else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger) 1198 { 1199 // NOTE: no trigger parameter support in VST2, simulate it here 1200 defValue = fPlugin.getParameterDefault(i); 1201 curValue = fPlugin.getParameterValue(i); 1202 1203 if (d_isEqual(curValue, defValue)) 1204 continue; 1205 1206 #if DISTRHO_PLUGIN_HAS_UI 1207 if (fVstUI != nullptr) 1208 setParameterValueFromPlugin(i, defValue); 1209 #endif 1210 fPlugin.setParameterValue(i, defValue); 1211 } 1212 else 1213 { 1214 continue; 1215 } 1216 1217 const ParameterRanges& ranges(fPlugin.getParameterRanges(i)); 1218 hostCallback(VST_HOST_OPCODE_00, i, 0, nullptr, ranges.getNormalizedValue(curValue)); 1219 } 1220 1221 #if DISTRHO_PLUGIN_WANT_LATENCY 1222 fEffect->delay = fPlugin.getLatency(); 1223 #endif 1224 } 1225 1226 #if DISTRHO_PLUGIN_HAS_UI 1227 void setParameterValueFromPlugin(const uint32_t index, const float realValue) 1228 { 1229 parameterValues[index] = realValue; 1230 parameterChecks[index] = true; 1231 } 1232 #endif 1233 1234 #if DISTRHO_PLUGIN_WANT_PARAMETER_VALUE_CHANGE_REQUEST 1235 bool requestParameterValueChange(const uint32_t index, const float value) 1236 { 1237 const ParameterRanges& ranges(fPlugin.getParameterRanges(index)); 1238 hostCallback(VST_HOST_OPCODE_00, index, 0, nullptr, ranges.getNormalizedValue(value)); 1239 return true; 1240 } 1241 1242 static bool requestParameterValueChangeCallback(void* const ptr, const uint32_t index, const float value) 1243 { 1244 return ((PluginVst*)ptr)->requestParameterValueChange(index, value); 1245 } 1246 #endif 1247 1248 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT 1249 bool writeMidi(const MidiEvent& midiEvent) 1250 { 1251 if (midiEvent.size > 4) 1252 return true; 1253 1254 PluginVstEvents vstEvents = {}; 1255 VstMidiEvent vstMidiEvent = {}; 1256 1257 vstEvents.numEvents = 1; 1258 vstEvents.events[0] = (VstEvent*)&vstMidiEvent; 1259 1260 vstMidiEvent.type = 1; 1261 vstMidiEvent.byteSize = static_cast<int32_t>(sizeof(VstMidiEvent)); 1262 vstMidiEvent.deltaFrames = midiEvent.frame; 1263 1264 for (uint8_t i=0; i<midiEvent.size; ++i) 1265 vstMidiEvent.midiData[i] = midiEvent.data[i]; 1266 1267 return hostCallback(VST_HOST_OPCODE_08, 0, 0, &vstEvents) == 1; 1268 } 1269 1270 static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent) 1271 { 1272 return static_cast<PluginVst*>(ptr)->writeMidi(midiEvent); 1273 } 1274 #endif 1275 1276 #if DISTRHO_PLUGIN_WANT_STATE 1277 // ---------------------------------------------------------------------------------------------------------------- 1278 // functions called from the UI side, may block 1279 1280 #if DISTRHO_PLUGIN_HAS_UI 1281 void setStateFromUI(const char* const key, const char* const value) override 1282 #else 1283 void setStateFromUI(const char* const key, const char* const value) 1284 #endif 1285 { 1286 fPlugin.setState(key, value); 1287 1288 // check if we want to save this key 1289 if (fPlugin.wantStateKey(key)) 1290 { 1291 const String dkey(key); 1292 fStateMap[dkey] = value; 1293 } 1294 } 1295 #endif 1296 }; 1297 1298 // -------------------------------------------------------------------------------------------------------------------- 1299 1300 struct ExtendedAEffect : vst_effect { 1301 char _padding[63]; 1302 char valid; 1303 vst_host_callback audioMaster; 1304 PluginVst* pluginPtr; 1305 }; 1306 1307 static ScopedPointer<PluginExporter> sPlugin; 1308 1309 static struct Cleanup { 1310 std::vector<ExtendedAEffect*> effects; 1311 1312 ~Cleanup() 1313 { 1314 for (std::vector<ExtendedAEffect*>::iterator it = effects.begin(), end = effects.end(); it != end; ++it) 1315 { 1316 ExtendedAEffect* const exteffect = *it; 1317 delete exteffect->pluginPtr; 1318 delete exteffect; 1319 } 1320 1321 sPlugin = nullptr; 1322 } 1323 } sCleanup; 1324 1325 // -------------------------------------------------------------------------------------------------------------------- 1326 1327 static inline 1328 ExtendedAEffect* getExtendedEffect(vst_effect* const effect) 1329 { 1330 if (effect == nullptr) 1331 return nullptr; 1332 1333 ExtendedAEffect* const exteffect = static_cast<ExtendedAEffect*>(effect); 1334 DISTRHO_SAFE_ASSERT_RETURN(exteffect->valid == 101, nullptr); 1335 DISTRHO_SAFE_ASSERT_RETURN(exteffect->audioMaster != nullptr, nullptr); 1336 1337 return exteffect; 1338 } 1339 1340 static inline 1341 PluginVst* getEffectPlugin(vst_effect* const effect) 1342 { 1343 if (effect == nullptr) 1344 return nullptr; 1345 1346 ExtendedAEffect* const exteffect = static_cast<ExtendedAEffect*>(effect); 1347 DISTRHO_SAFE_ASSERT_RETURN(exteffect->valid == 101, nullptr); 1348 DISTRHO_SAFE_ASSERT_RETURN(exteffect->audioMaster != nullptr, nullptr); 1349 1350 return exteffect->pluginPtr; 1351 } 1352 1353 // -------------------------------------------------------------------------------------------------------------------- 1354 1355 static intptr_t VST_FUNCTION_INTERFACE vst_dispatcherCallback(vst_effect* const effect, 1356 const VST_EFFECT_OPCODE opcode, 1357 const int32_t index, 1358 const intptr_t value, 1359 void* const ptr, 1360 const float opt) 1361 { 1362 // handle base opcodes 1363 switch (opcode) 1364 { 1365 case VST_EFFECT_OPCODE_CREATE: 1366 if (ExtendedAEffect* const exteffect = getExtendedEffect(effect)) 1367 { 1368 // some hosts call open/create twice 1369 if (exteffect->pluginPtr != nullptr) 1370 return 1; 1371 1372 const vst_host_callback audioMaster = exteffect->audioMaster; 1373 1374 d_nextBufferSize = audioMaster(effect, VST_HOST_OPCODE_11, 0, 0, nullptr, 0.0f); 1375 d_nextSampleRate = audioMaster(effect, VST_HOST_OPCODE_10, 0, 0, nullptr, 0.0f); 1376 d_nextCanRequestParameterValueChanges = true; 1377 1378 // some hosts are not ready at this point or return 0 buffersize/samplerate 1379 if (d_nextBufferSize == 0) 1380 d_nextBufferSize = 2048; 1381 if (d_nextSampleRate <= 0.0) 1382 d_nextSampleRate = 44100.0; 1383 1384 exteffect->pluginPtr = new PluginVst(audioMaster, effect); 1385 return 1; 1386 } 1387 return 0; 1388 1389 case VST_EFFECT_OPCODE_DESTROY: 1390 if (ExtendedAEffect* const exteffect = getExtendedEffect(effect)) 1391 { 1392 // delete plugin object 1393 if (exteffect->pluginPtr != nullptr) 1394 { 1395 delete exteffect->pluginPtr; 1396 exteffect->pluginPtr = nullptr; 1397 } 1398 1399 // delete effect too, if it comes from us 1400 const std::vector<ExtendedAEffect*>::iterator it = std::find(sCleanup.effects.begin(), sCleanup.effects.end(), exteffect); 1401 if (it != sCleanup.effects.end()) 1402 { 1403 delete exteffect; 1404 sCleanup.effects.erase(it); 1405 } 1406 1407 // delete global plugin instance too if this is the last loaded effect 1408 if (sCleanup.effects.empty()) 1409 sPlugin = nullptr; 1410 return 1; 1411 } 1412 return 0; 1413 1414 case VST_EFFECT_OPCODE_PARAM_GETLABEL: 1415 if (ptr != nullptr && index < static_cast<int32_t>(sPlugin->getParameterCount())) 1416 { 1417 d_strncpy((char*)ptr, sPlugin->getParameterUnit(index), 8); 1418 return 1; 1419 } 1420 return 0; 1421 1422 case VST_EFFECT_OPCODE_PARAM_GETNAME: 1423 if (ptr != nullptr && index < static_cast<int32_t>(sPlugin->getParameterCount())) 1424 { 1425 const String& shortName(sPlugin->getParameterShortName(index)); 1426 if (shortName.isNotEmpty()) 1427 d_strncpy((char*)ptr, shortName, 16); 1428 else 1429 d_strncpy((char*)ptr, sPlugin->getParameterName(index), 16); 1430 return 1; 1431 } 1432 return 0; 1433 1434 case VST_EFFECT_OPCODE_38: // FIXME VST_EFFECT_OPCODE_GET_PARAMETER_PROPERTIES is wrong by 1 1435 if (ptr != nullptr && index < static_cast<int32_t>(sPlugin->getParameterCount())) 1436 { 1437 if (vst_parameter_properties* const properties = (vst_parameter_properties*)ptr) 1438 { 1439 memset(properties, 0, sizeof(vst_parameter_properties)); 1440 1441 // full name 1442 d_strncpy(properties->name, 1443 sPlugin->getParameterName(index), 1444 sizeof(properties->name)); 1445 1446 // short name 1447 const String& shortName(sPlugin->getParameterShortName(index)); 1448 1449 if (shortName.isNotEmpty()) 1450 d_strncpy(properties->label, 1451 sPlugin->getParameterShortName(index), 1452 sizeof(properties->label)); 1453 1454 // parameter hints 1455 const uint32_t hints = sPlugin->getParameterHints(index); 1456 1457 if (hints & kParameterIsOutput) 1458 return 1; 1459 1460 if (hints & kParameterIsBoolean) 1461 { 1462 properties->flags |= VST_PARAMETER_FLAGS_SWITCH; 1463 } 1464 1465 if (hints & kParameterIsInteger) 1466 { 1467 const ParameterRanges& ranges(sPlugin->getParameterRanges(index)); 1468 properties->flags |= VST_PARAMETER_FLAGS_INTEGER_LIMITS; 1469 properties->min_value_i32 = static_cast<int32_t>(ranges.min); 1470 properties->max_value_i32 = static_cast<int32_t>(ranges.max); 1471 } 1472 1473 if (hints & kParameterIsLogarithmic) 1474 { 1475 properties->flags |= VST_PARAMETER_FLAGS_UNKNOWN6; // can ramp 1476 } 1477 1478 // parameter group (category in vst) 1479 const uint32_t groupId = sPlugin->getParameterGroupId(index); 1480 1481 if (groupId != kPortGroupNone) 1482 { 1483 // we can't use groupId directly, so use the index array where this group is stored in 1484 for (uint32_t i=0, count=sPlugin->getPortGroupCount(); i < count; ++i) 1485 { 1486 const PortGroupWithId& portGroup(sPlugin->getPortGroupByIndex(i)); 1487 1488 if (portGroup.groupId == groupId) 1489 { 1490 properties->flags |= VST_PARAMETER_FLAGS_CATEGORY; 1491 properties->category = i + 1; 1492 d_strncpy(properties->category_label, 1493 portGroup.name.buffer(), 1494 sizeof(properties->category_label)); 1495 break; 1496 } 1497 } 1498 1499 if (properties->category != 0) 1500 { 1501 for (uint32_t i=0, count=sPlugin->getParameterCount(); i < count; ++i) 1502 if (sPlugin->getParameterGroupId(i) == groupId) 1503 ++properties->num_parameters_in_category; 1504 } 1505 } 1506 1507 return 1; 1508 } 1509 } 1510 return 0; 1511 1512 case VST_EFFECT_OPCODE_EFFECT_CATEGORY: 1513 #if DISTRHO_PLUGIN_IS_SYNTH 1514 return VST_CATEGORY_02; 1515 #else 1516 return VST_CATEGORY_01; 1517 #endif 1518 1519 case VST_EFFECT_OPCODE_EFFECT_NAME: 1520 if (char* const cptr = (char*)ptr) 1521 { 1522 d_strncpy(cptr, sPlugin->getName(), 32); 1523 return 1; 1524 } 1525 return 0; 1526 1527 case VST_EFFECT_OPCODE_VENDOR_NAME: 1528 if (char* const cptr = (char*)ptr) 1529 { 1530 d_strncpy(cptr, sPlugin->getMaker(), 32); 1531 return 1; 1532 } 1533 return 0; 1534 1535 case VST_EFFECT_OPCODE_PRODUCT_NAME: 1536 if (char* const cptr = (char*)ptr) 1537 { 1538 d_strncpy(cptr, sPlugin->getLabel(), 32); 1539 return 1; 1540 } 1541 return 0; 1542 1543 case VST_EFFECT_OPCODE_VENDOR_VERSION: 1544 return sPlugin->getVersion(); 1545 1546 case VST_EFFECT_OPCODE_VST_VERSION: 1547 return VST_VERSION_2_4_0_0; 1548 1549 default: 1550 break; 1551 } 1552 1553 // handle advanced opcodes 1554 if (PluginVst* const pluginPtr = getEffectPlugin(effect)) 1555 return pluginPtr->vst_dispatcher(opcode, index, value, ptr, opt); 1556 1557 return 0; 1558 } 1559 1560 static float VST_FUNCTION_INTERFACE vst_getParameterCallback(vst_effect* const effect, 1561 const uint32_t index) 1562 { 1563 if (PluginVst* const pluginPtr = getEffectPlugin(effect)) 1564 return pluginPtr->vst_getParameter(index); 1565 return 0.0f; 1566 } 1567 1568 static void VST_FUNCTION_INTERFACE vst_setParameterCallback(vst_effect* const effect, 1569 const uint32_t index, 1570 const float value) 1571 { 1572 if (PluginVst* const pluginPtr = getEffectPlugin(effect)) 1573 pluginPtr->vst_setParameter(index, value); 1574 } 1575 1576 static void VST_FUNCTION_INTERFACE vst_processCallback(vst_effect* const effect, 1577 const float* const* const inputs, 1578 float** const outputs, 1579 const int32_t sampleFrames) 1580 { 1581 if (PluginVst* const pluginPtr = getEffectPlugin(effect)) 1582 pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames); 1583 } 1584 1585 static void VST_FUNCTION_INTERFACE vst_processReplacingCallback(vst_effect* const effect, 1586 const float* const* const inputs, 1587 float** const outputs, 1588 const int32_t sampleFrames) 1589 { 1590 if (PluginVst* const pluginPtr = getEffectPlugin(effect)) 1591 pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames); 1592 } 1593 1594 // -------------------------------------------------------------------------------------------------------------------- 1595 1596 END_NAMESPACE_DISTRHO 1597 1598 DISTRHO_PLUGIN_EXPORT 1599 const vst_effect* VSTPluginMain(vst_host_callback); 1600 1601 DISTRHO_PLUGIN_EXPORT 1602 const vst_effect* VSTPluginMain(const vst_host_callback audioMaster) 1603 { 1604 USE_NAMESPACE_DISTRHO 1605 1606 // old version 1607 if (audioMaster(nullptr, VST_HOST_OPCODE_01 /* version */, 0, 0, nullptr, 0.0f) == 0) 1608 return nullptr; 1609 1610 // find plugin bundle 1611 static String bundlePath; 1612 if (bundlePath.isEmpty()) 1613 { 1614 String tmpPath(getBinaryFilename()); 1615 tmpPath.truncate(tmpPath.rfind(DISTRHO_OS_SEP)); 1616 #ifdef DISTRHO_OS_MAC 1617 if (tmpPath.endsWith("/MacOS")) 1618 { 1619 tmpPath.truncate(tmpPath.rfind('/')); 1620 if (tmpPath.endsWith("/Contents")) 1621 { 1622 tmpPath.truncate(tmpPath.rfind('/')); 1623 bundlePath = tmpPath; 1624 d_nextBundlePath = bundlePath.buffer(); 1625 } 1626 } 1627 #else 1628 if (tmpPath.endsWith(".vst")) 1629 { 1630 bundlePath = tmpPath; 1631 d_nextBundlePath = bundlePath.buffer(); 1632 } 1633 #endif 1634 } 1635 1636 // first internal init 1637 if (sPlugin == nullptr) 1638 { 1639 // set valid but dummy values 1640 d_nextBufferSize = 512; 1641 d_nextSampleRate = 44100.0; 1642 d_nextPluginIsDummy = true; 1643 d_nextCanRequestParameterValueChanges = true; 1644 1645 // Create dummy plugin to get data from 1646 sPlugin = new PluginExporter(nullptr, nullptr, nullptr, nullptr); 1647 1648 // unset 1649 d_nextBufferSize = 0; 1650 d_nextSampleRate = 0.0; 1651 d_nextPluginIsDummy = false; 1652 d_nextCanRequestParameterValueChanges = false; 1653 } 1654 1655 ExtendedAEffect* const effect = new ExtendedAEffect; 1656 std::memset(effect, 0, sizeof(ExtendedAEffect)); 1657 1658 // vst fields 1659 #ifdef WORDS_BIGENDIAN 1660 effect->magic_number = 0x50747356; 1661 #else 1662 effect->magic_number = 0x56737450; 1663 #endif 1664 effect->unique_id = sPlugin->getUniqueId(); 1665 effect->version = sPlugin->getVersion(); 1666 1667 // VST doesn't support parameter outputs. we can fake them, but it is a hack. Disabled by default. 1668 #ifdef DPF_VST_SHOW_PARAMETER_OUTPUTS 1669 const int numParams = sPlugin->getParameterCount(); 1670 #else 1671 int numParams = 0; 1672 bool outputsReached = false; 1673 1674 for (uint32_t i=0, count=sPlugin->getParameterCount(); i < count; ++i) 1675 { 1676 if (sPlugin->isParameterInput(i)) 1677 { 1678 // parameter outputs must be all at the end 1679 DISTRHO_SAFE_ASSERT_BREAK(! outputsReached); 1680 ++numParams; 1681 continue; 1682 } 1683 outputsReached = true; 1684 } 1685 #endif 1686 1687 // plugin fields 1688 effect->num_params = numParams; 1689 effect->num_programs = 1; 1690 effect->num_inputs = DISTRHO_PLUGIN_NUM_INPUTS; 1691 effect->num_outputs = DISTRHO_PLUGIN_NUM_OUTPUTS; 1692 1693 // plugin flags 1694 effect->flags |= 1 << 4; // uses process_float 1695 #if DISTRHO_PLUGIN_IS_SYNTH 1696 effect->flags |= 1 << 8; 1697 #endif 1698 #if DISTRHO_PLUGIN_HAS_UI 1699 effect->flags |= 1 << 0; 1700 #endif 1701 #if DISTRHO_PLUGIN_WANT_STATE 1702 effect->flags |= 1 << 5; 1703 #endif 1704 1705 // static calls 1706 effect->control = vst_dispatcherCallback; 1707 effect->process = vst_processCallback; 1708 effect->get_parameter = vst_getParameterCallback; 1709 effect->set_parameter = vst_setParameterCallback; 1710 effect->process_float = vst_processReplacingCallback; 1711 1712 // special values 1713 effect->valid = 101; 1714 effect->audioMaster = audioMaster; 1715 effect->pluginPtr = nullptr; 1716 1717 // done 1718 sCleanup.effects.push_back(effect); 1719 1720 return effect; 1721 } 1722 1723 #if !(defined(DISTRHO_OS_MAC) || defined(DISTRHO_OS_WASM) || defined(DISTRHO_OS_WINDOWS) || DISTRHO_PLUGIN_WANT_WEBVIEW) 1724 DISTRHO_PLUGIN_EXPORT 1725 const vst_effect* VSTPluginMainCompat(vst_host_callback) asm ("main"); 1726 1727 DISTRHO_PLUGIN_EXPORT 1728 const vst_effect* VSTPluginMainCompat(const vst_host_callback audioMaster) 1729 { 1730 // protect main symbol against running as executable 1731 if (reinterpret_cast<uintptr_t>(audioMaster) < 0xff) 1732 return nullptr; 1733 1734 return VSTPluginMain(audioMaster); 1735 } 1736 #endif 1737 1738 // --------------------------------------------------------------------------------------------------------------------