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

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