1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2012-2020 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 
19 #if DISTRHO_PLUGIN_HAS_UI && ! DISTRHO_PLUGIN_HAS_EMBED_UI
20 # undef DISTRHO_PLUGIN_HAS_UI
21 # define DISTRHO_PLUGIN_HAS_UI 0
22 #endif
23 
24 #if DISTRHO_PLUGIN_HAS_UI
25 # include "DistrhoUIInternal.hpp"
26 #endif
27 
28 #ifndef __cdecl
29 # define __cdecl
30 #endif
31 
32 #define VESTIGE_HEADER
33 #define VST_FORCE_DEPRECATED 0
34 
35 #include <clocale>
36 #include <map>
37 #include <string>
38 
39 #ifdef VESTIGE_HEADER
40 # include "vestige/vestige.h"
41 #define effFlagsProgramChunks (1 << 5)
42 #define effSetProgramName 4
43 #define effGetParamLabel 6
44 #define effGetParamDisplay 7
45 #define effGetChunk 23
46 #define effSetChunk 24
47 #define effCanBeAutomated 26
48 #define effGetProgramNameIndexed 29
49 #define effGetPlugCategory 35
50 #define effVendorSpecific 50
51 #define effEditKeyDown 59
52 #define effEditKeyUp 60
53 #define kVstVersion 2400
54 struct ERect {
55     int16_t top, left, bottom, right;
56 };
57 #else
58 # include "vst/aeffectx.h"
59 #endif
60 
61 START_NAMESPACE_DISTRHO
62 
63 typedef std::map<const String, String> StringMap;
64 
65 static const int kVstMidiEventSize = static_cast<int>(sizeof(VstMidiEvent));
66 
67 #if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
68 static const writeMidiFunc writeMidiCallback = nullptr;
69 #endif
70 
71 // -----------------------------------------------------------------------
72 
strncpy(char * const dst,const char * const src,const size_t size)73 void strncpy(char* const dst, const char* const src, const size_t size)
74 {
75     DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
76 
77     if (const size_t len = std::min(std::strlen(src), size-1U))
78     {
79         std::memcpy(dst, src, len);
80         dst[len] = '\0';
81     }
82     else
83     {
84         dst[0] = '\0';
85     }
86 }
87 
snprintf_param(char * const dst,const float value,const size_t size)88 void snprintf_param(char* const dst, const float value, const size_t size)
89 {
90     DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
91     std::snprintf(dst, size-1, "%f", value);
92     dst[size-1] = '\0';
93 }
94 
snprintf_iparam(char * const dst,const int32_t value,const size_t size)95 void snprintf_iparam(char* const dst, const int32_t value, const size_t size)
96 {
97     DISTRHO_SAFE_ASSERT_RETURN(size > 0,);
98     std::snprintf(dst, size-1, "%d", value);
99     dst[size-1] = '\0';
100 }
101 
102 // -----------------------------------------------------------------------
103 
104 class ScopedSafeLocale {
105 public:
ScopedSafeLocale()106     ScopedSafeLocale() noexcept
107         : locale(::strdup(::setlocale(LC_NUMERIC, nullptr)))
108     {
109         ::setlocale(LC_NUMERIC, "C");
110     }
111 
~ScopedSafeLocale()112     ~ScopedSafeLocale() noexcept
113     {
114         if (locale != nullptr)
115         {
116             ::setlocale(LC_NUMERIC, locale);
117             std::free(locale);
118         }
119     }
120 
121 private:
122     char* const locale;
123 
124     DISTRHO_DECLARE_NON_COPY_CLASS(ScopedSafeLocale)
125     DISTRHO_PREVENT_HEAP_ALLOCATION
126 };
127 
128 // -----------------------------------------------------------------------
129 
130 class ParameterCheckHelper
131 {
132 public:
ParameterCheckHelper()133     ParameterCheckHelper()
134         : parameterChecks(nullptr),
135           parameterValues(nullptr) {}
136 
~ParameterCheckHelper()137     virtual ~ParameterCheckHelper()
138     {
139         if (parameterChecks != nullptr)
140         {
141             delete[] parameterChecks;
142             parameterChecks = nullptr;
143         }
144         if (parameterValues != nullptr)
145         {
146             delete[] parameterValues;
147             parameterValues = nullptr;
148         }
149     }
150 
151     bool*  parameterChecks;
152     float* parameterValues;
153 
154 #if DISTRHO_PLUGIN_WANT_STATE
155     virtual void setStateFromUI(const char* const newKey, const char* const newValue) = 0;
156 #endif
157 };
158 
159 #if DISTRHO_PLUGIN_HAS_UI
160 // -----------------------------------------------------------------------
161 
162 class UIVst
163 {
164 public:
UIVst(const audioMasterCallback audioMaster,AEffect * const effect,ParameterCheckHelper * const uiHelper,PluginExporter * const plugin,const intptr_t winId,const float scaleFactor)165     UIVst(const audioMasterCallback audioMaster,
166           AEffect* const effect,
167           ParameterCheckHelper* const uiHelper,
168           PluginExporter* const plugin,
169           const intptr_t winId, const float scaleFactor)
170         : fAudioMaster(audioMaster),
171           fEffect(effect),
172           fUiHelper(uiHelper),
173           fPlugin(plugin),
174           fUI(this, winId,
175               editParameterCallback,
176               setParameterCallback,
177               setStateCallback,
178               sendNoteCallback,
179               setSizeCallback,
180               nullptr, // TODO file request
181               nullptr,
182               plugin->getInstancePointer(),
183               scaleFactor),
184           fShouldCaptureVstKeys(false)
185     {
186         // FIXME only needed for windows?
187 //#ifdef DISTRHO_OS_WINDOWS
188         char strBuf[0xff+1];
189         std::memset(strBuf, 0, sizeof(char)*(0xff+1));
190         hostCallback(audioMasterGetProductString, 0, 0, strBuf);
191         d_stdout("Plugin UI running in '%s'", strBuf);
192 
193         // TODO make a white-list of needed hosts
194         if (/*std::strcmp(strBuf, "") == 0*/ true)
195             fShouldCaptureVstKeys = true;
196 //#endif
197     }
198 
199     // -------------------------------------------------------------------
200 
idle()201     void idle()
202     {
203         for (uint32_t i=0, count = fPlugin->getParameterCount(); i < count; ++i)
204         {
205             if (fUiHelper->parameterChecks[i])
206             {
207                 fUiHelper->parameterChecks[i] = false;
208                 fUI.parameterChanged(i, fUiHelper->parameterValues[i]);
209             }
210         }
211 
212         fUI.idle();
213     }
214 
getWidth() const215     int16_t getWidth() const
216     {
217         return fUI.getWidth();
218     }
219 
getHeight() const220     int16_t getHeight() const
221     {
222         return fUI.getHeight();
223     }
224 
setSampleRate(const double newSampleRate)225     void setSampleRate(const double newSampleRate)
226     {
227         fUI.setSampleRate(newSampleRate, true);
228     }
229 
230     // -------------------------------------------------------------------
231     // functions called from the plugin side, may block
232 
233 # if DISTRHO_PLUGIN_WANT_STATE
setStateFromPlugin(const char * const key,const char * const value)234     void setStateFromPlugin(const char* const key, const char* const value)
235     {
236         fUI.stateChanged(key, value);
237     }
238 # endif
239 
handlePluginKeyEvent(const bool down,int32_t index,const intptr_t value)240     int handlePluginKeyEvent(const bool down, int32_t index, const intptr_t value)
241     {
242 # if !DISTRHO_PLUGIN_HAS_EXTERNAL_UI
243         if (! fShouldCaptureVstKeys)
244             return 0;
245 
246         d_stdout("handlePluginKeyEvent %i %i %li\n", down, index, (long int)value);
247 
248         using namespace DGL_NAMESPACE;
249 
250         int special = 0;
251         switch (value)
252         {
253         // convert some specials to normal keys
254         case  1: index = kCharBackspace; break;
255         case  6: index = kCharEscape;    break;
256         case  7: index = ' ';            break;
257         case 22: index = kCharDelete;    break;
258 
259         // handle rest of special keys
260         case 40: special = kKeyF1;       break;
261         case 41: special = kKeyF2;       break;
262         case 42: special = kKeyF3;       break;
263         case 43: special = kKeyF4;       break;
264         case 44: special = kKeyF5;       break;
265         case 45: special = kKeyF6;       break;
266         case 46: special = kKeyF7;       break;
267         case 47: special = kKeyF8;       break;
268         case 48: special = kKeyF9;       break;
269         case 49: special = kKeyF10;      break;
270         case 50: special = kKeyF11;      break;
271         case 51: special = kKeyF12;      break;
272         case 11: special = kKeyLeft;     break;
273         case 12: special = kKeyUp;       break;
274         case 13: special = kKeyRight;    break;
275         case 14: special = kKeyDown;     break;
276         case 15: special = kKeyPageUp;   break;
277         case 16: special = kKeyPageDown; break;
278         case 10: special = kKeyHome;     break;
279         case  9: special = kKeyEnd;      break;
280         case 21: special = kKeyInsert;   break;
281         case 54: special = kKeyShift;    break;
282         case 55: special = kKeyControl;  break;
283         case 56: special = kKeyAlt;      break;
284         }
285 
286         if (special != 0)
287             return fUI.handlePluginSpecial(down, static_cast<Key>(special));
288 
289         if (index >= 0)
290             return fUI.handlePluginKeyboard(down, static_cast<uint>(index));
291 # endif
292 
293         return 0;
294     }
295 
296     // -------------------------------------------------------------------
297 
298 protected:
hostCallback(const int32_t opcode,const int32_t index=0,const intptr_t value=0,void * const ptr=nullptr,const float opt=0.0f)299     intptr_t hostCallback(const int32_t opcode,
300                           const int32_t index = 0,
301                           const intptr_t value = 0,
302                           void* const ptr = nullptr,
303                           const float opt = 0.0f)
304     {
305         return fAudioMaster(fEffect, opcode, index, value, ptr, opt);
306     }
307 
editParameter(const uint32_t index,const bool started)308     void editParameter(const uint32_t index, const bool started)
309     {
310         hostCallback(started ? audioMasterBeginEdit : audioMasterEndEdit, index);
311     }
312 
setParameterValue(const uint32_t index,const float realValue)313     void setParameterValue(const uint32_t index, const float realValue)
314     {
315         const ParameterRanges& ranges(fPlugin->getParameterRanges(index));
316         const float perValue(ranges.getNormalizedValue(realValue));
317 
318         fPlugin->setParameterValue(index, realValue);
319         hostCallback(audioMasterAutomate, index, 0, nullptr, perValue);
320     }
321 
setState(const char * const key,const char * const value)322     void setState(const char* const key, const char* const value)
323     {
324 # if DISTRHO_PLUGIN_WANT_STATE
325         fUiHelper->setStateFromUI(key, value);
326 # else
327         return; // unused
328         (void)key;
329         (void)value;
330 # endif
331     }
332 
sendNote(const uint8_t channel,const uint8_t note,const uint8_t velocity)333     void sendNote(const uint8_t channel, const uint8_t note, const uint8_t velocity)
334     {
335 # if 0 //DISTRHO_PLUGIN_WANT_MIDI_INPUT
336         // TODO
337 # else
338         return; // unused
339         (void)channel;
340         (void)note;
341         (void)velocity;
342 # endif
343     }
344 
setSize(const uint width,const uint height)345     void setSize(const uint width, const uint height)
346     {
347         fUI.setWindowSize(width, height);
348         hostCallback(audioMasterSizeWindow, width, height);
349     }
350 
351 private:
352     // Vst stuff
353     const audioMasterCallback fAudioMaster;
354     AEffect* const fEffect;
355     ParameterCheckHelper* const fUiHelper;
356     PluginExporter* const fPlugin;
357 
358     // Plugin UI
359     UIExporter fUI;
360     bool fShouldCaptureVstKeys;
361 
362     // -------------------------------------------------------------------
363     // Callbacks
364 
365     #define handlePtr ((UIVst*)ptr)
366 
editParameterCallback(void * ptr,uint32_t index,bool started)367     static void editParameterCallback(void* ptr, uint32_t index, bool started)
368     {
369         handlePtr->editParameter(index, started);
370     }
371 
setParameterCallback(void * ptr,uint32_t rindex,float value)372     static void setParameterCallback(void* ptr, uint32_t rindex, float value)
373     {
374         handlePtr->setParameterValue(rindex, value);
375     }
376 
setStateCallback(void * ptr,const char * key,const char * value)377     static void setStateCallback(void* ptr, const char* key, const char* value)
378     {
379         handlePtr->setState(key, value);
380     }
381 
sendNoteCallback(void * ptr,uint8_t channel,uint8_t note,uint8_t velocity)382     static void sendNoteCallback(void* ptr, uint8_t channel, uint8_t note, uint8_t velocity)
383     {
384         handlePtr->sendNote(channel, note, velocity);
385     }
386 
setSizeCallback(void * ptr,uint width,uint height)387     static void setSizeCallback(void* ptr, uint width, uint height)
388     {
389         handlePtr->setSize(width, height);
390     }
391 
392     #undef handlePtr
393 };
394 #endif
395 
396 // -----------------------------------------------------------------------
397 
398 class PluginVst : public ParameterCheckHelper
399 {
400 public:
PluginVst(const audioMasterCallback audioMaster,AEffect * const effect)401     PluginVst(const audioMasterCallback audioMaster, AEffect* const effect)
402         : fPlugin(this, writeMidiCallback),
403           fAudioMaster(audioMaster),
404           fEffect(effect)
405     {
406         std::memset(fProgramName, 0, sizeof(char)*(32+1));
407         std::strcpy(fProgramName, "Default");
408 
409 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
410         fMidiEventCount = 0;
411 #endif
412 
413 #if DISTRHO_PLUGIN_HAS_UI
414         fVstUI           = nullptr;
415         fVstRect.top     = 0;
416         fVstRect.left    = 0;
417         fVstRect.bottom  = 0;
418         fVstRect.right   = 0;
419         fLastScaleFactor = 1.0f;
420 
421         if (const uint32_t paramCount = fPlugin.getParameterCount())
422         {
423             parameterChecks = new bool[paramCount];
424             parameterValues = new float[paramCount];
425 
426             for (uint32_t i=0; i < paramCount; ++i)
427             {
428                 parameterChecks[i] = false;
429                 parameterValues[i] = NAN;
430             }
431         }
432 # if DISTRHO_OS_MAC
433 #  ifdef __LP64__
434         fUsingNsView = true;
435 #  else
436 #   ifndef DISTRHO_NO_WARNINGS
437 #    warning 32bit VST UIs on OSX only work if the host supports "hasCockosViewAsConfig"
438 #   endif
439         fUsingNsView = false;
440 #  endif
441 # endif // DISTRHO_OS_MAC
442 #endif // DISTRHO_PLUGIN_HAS_UI
443 
444 #if DISTRHO_PLUGIN_WANT_STATE
445         fStateChunk = nullptr;
446 
447         for (uint32_t i=0, count=fPlugin.getStateCount(); i<count; ++i)
448         {
449             const String& dkey(fPlugin.getStateKey(i));
450             fStateMap[dkey] = fPlugin.getStateDefaultValue(i);
451         }
452 #endif
453     }
454 
~PluginVst()455     ~PluginVst()
456     {
457 #if DISTRHO_PLUGIN_WANT_STATE
458         if (fStateChunk != nullptr)
459         {
460             delete[] fStateChunk;
461             fStateChunk = nullptr;
462         }
463 
464         fStateMap.clear();
465 #endif
466     }
467 
vst_dispatcher(const int32_t opcode,const int32_t index,const intptr_t value,void * const ptr,const float opt)468     intptr_t vst_dispatcher(const int32_t opcode, const int32_t index, const intptr_t value, void* const ptr, const float opt)
469     {
470 #if DISTRHO_PLUGIN_WANT_STATE
471         intptr_t ret = 0;
472 #endif
473 
474         switch (opcode)
475         {
476         case effGetProgram:
477             return 0;
478 
479         case effSetProgramName:
480             if (char* const programName = (char*)ptr)
481             {
482                 DISTRHO_NAMESPACE::strncpy(fProgramName, programName, 32);
483                 return 1;
484             }
485             break;
486 
487         case effGetProgramName:
488             if (char* const programName = (char*)ptr)
489             {
490                 DISTRHO_NAMESPACE::strncpy(programName, fProgramName, 24);
491                 return 1;
492             }
493             break;
494 
495         case effGetProgramNameIndexed:
496             if (char* const programName = (char*)ptr)
497             {
498                 DISTRHO_NAMESPACE::strncpy(programName, fProgramName, 24);
499                 return 1;
500             }
501             break;
502 
503         case effGetParamDisplay:
504             if (ptr != nullptr && index < static_cast<int32_t>(fPlugin.getParameterCount()))
505             {
506                 const uint32_t hints = fPlugin.getParameterHints(index);
507                 float value = fPlugin.getParameterValue(index);
508 
509                 if (hints & kParameterIsBoolean)
510                 {
511                     const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
512                     const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
513 
514                     value = value > midRange ? ranges.max : ranges.min;
515                 }
516                 else if (hints & kParameterIsInteger)
517                 {
518                     value = std::round(value);
519                 }
520 
521                 const ParameterEnumerationValues& enumValues(fPlugin.getParameterEnumValues(index));
522 
523                 for (uint8_t i = 0; i < enumValues.count; ++i)
524                 {
525                     if (d_isNotEqual(value, enumValues.values[i].value))
526                         continue;
527 
528                     DISTRHO_NAMESPACE::strncpy((char*)ptr, enumValues.values[i].label.buffer(), 24);
529                     return 1;
530                 }
531 
532                 if (hints & kParameterIsInteger)
533                     DISTRHO_NAMESPACE::snprintf_iparam((char*)ptr, (int32_t)value, 24);
534                 else
535                     DISTRHO_NAMESPACE::snprintf_param((char*)ptr, value, 24);
536 
537                 return 1;
538             }
539             break;
540 
541         case effSetSampleRate:
542             fPlugin.setSampleRate(opt, true);
543 
544 #if DISTRHO_PLUGIN_HAS_UI
545             if (fVstUI != nullptr)
546                 fVstUI->setSampleRate(opt);
547 #endif
548             break;
549 
550         case effSetBlockSize:
551             fPlugin.setBufferSize(value, true);
552             break;
553 
554         case effMainsChanged:
555             if (value != 0)
556             {
557 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
558                 fMidiEventCount = 0;
559 
560                 // tell host we want MIDI events
561                 hostCallback(audioMasterWantMidi);
562 #endif
563 
564                 // deactivate for possible changes
565                 fPlugin.deactivateIfNeeded();
566 
567                 // check if something changed
568                 const uint32_t bufferSize = static_cast<uint32_t>(hostCallback(audioMasterGetBlockSize));
569                 const double   sampleRate = static_cast<double>(hostCallback(audioMasterGetSampleRate));
570 
571                 if (bufferSize != 0)
572                     fPlugin.setBufferSize(bufferSize, true);
573 
574                 if (sampleRate != 0.0)
575                     fPlugin.setSampleRate(sampleRate, true);
576 
577                 fPlugin.activate();
578             }
579             else
580             {
581                 fPlugin.deactivate();
582             }
583             break;
584 
585 #if DISTRHO_PLUGIN_HAS_UI
586         case effEditGetRect:
587             if (fVstUI != nullptr)
588             {
589                 fVstRect.right  = fVstUI->getWidth();
590                 fVstRect.bottom = fVstUI->getHeight();
591             }
592             else
593             {
594                 d_lastUiSampleRate = fPlugin.getSampleRate();
595 
596                 UIExporter tmpUI(nullptr, 0,
597                                  nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
598                                  fPlugin.getInstancePointer(), fLastScaleFactor);
599                 fVstRect.right  = tmpUI.getWidth();
600                 fVstRect.bottom = tmpUI.getHeight();
601                 tmpUI.quit();
602             }
603             *(ERect**)ptr = &fVstRect;
604             return 1;
605 
606         case effEditOpen:
607             delete fVstUI; // hosts which don't pair effEditOpen/effEditClose calls (Minihost Modular)
608             fVstUI = nullptr;
609             {
610 # if DISTRHO_OS_MAC
611                 if (! fUsingNsView)
612                 {
613                     d_stderr("Host doesn't support hasCockosViewAsConfig, cannot use UI");
614                     return 0;
615                 }
616 # endif
617                 d_lastUiSampleRate = fPlugin.getSampleRate();
618 
619                 fVstUI = new UIVst(fAudioMaster, fEffect, this, &fPlugin, (intptr_t)ptr, fLastScaleFactor);
620 
621 # if DISTRHO_PLUGIN_WANT_FULL_STATE
622                 // Update current state from plugin side
623                 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
624                 {
625                     const String& key = cit->first;
626                     fStateMap[key] = fPlugin.getState(key);
627                 }
628 # endif
629 
630 # if DISTRHO_PLUGIN_WANT_STATE
631                 // Set state
632                 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
633                 {
634                     const String& key   = cit->first;
635                     const String& value = cit->second;
636 
637                     fVstUI->setStateFromPlugin(key, value);
638                 }
639 # endif
640                 for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
641                     setParameterValueFromPlugin(i, fPlugin.getParameterValue(i));
642 
643                 fVstUI->idle();
644                 return 1;
645             }
646             break;
647 
648         case effEditClose:
649             if (fVstUI != nullptr)
650             {
651                 delete fVstUI;
652                 fVstUI = nullptr;
653                 return 1;
654             }
655             break;
656 
657         //case effIdle:
658         case effEditIdle:
659             if (fVstUI != nullptr)
660                 fVstUI->idle();
661             break;
662 
663         case effEditKeyDown:
664             if (fVstUI != nullptr)
665                 return fVstUI->handlePluginKeyEvent(true, index, value);
666             break;
667 
668         case effEditKeyUp:
669             if (fVstUI != nullptr)
670                 return fVstUI->handlePluginKeyEvent(false, index, value);
671             break;
672 #endif // DISTRHO_PLUGIN_HAS_UI
673 
674 #if DISTRHO_PLUGIN_WANT_STATE
675         case effGetChunk:
676         {
677             if (ptr == nullptr)
678                 return 0;
679 
680             if (fStateChunk != nullptr)
681             {
682                 delete[] fStateChunk;
683                 fStateChunk = nullptr;
684             }
685 
686             const uint32_t paramCount = fPlugin.getParameterCount();
687 
688             if (fPlugin.getStateCount() == 0 && paramCount == 0)
689             {
690                 fStateChunk    = new char[1];
691                 fStateChunk[0] = '\0';
692                 ret = 1;
693             }
694             else
695             {
696 # if DISTRHO_PLUGIN_WANT_FULL_STATE
697                 // Update current state
698                 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
699                 {
700                     const String& key = cit->first;
701                     fStateMap[key] = fPlugin.getState(key);
702                 }
703 # endif
704 
705                 String chunkStr;
706 
707                 for (StringMap::const_iterator cit=fStateMap.begin(), cite=fStateMap.end(); cit != cite; ++cit)
708                 {
709                     const String& key   = cit->first;
710                     const String& value = cit->second;
711 
712                     // join key and value
713                     String tmpStr;
714                     tmpStr  = key;
715                     tmpStr += "\xff";
716                     tmpStr += value;
717                     tmpStr += "\xff";
718 
719                     chunkStr += tmpStr;
720                 }
721 
722                 if (paramCount != 0)
723                 {
724                     // add another separator
725                     chunkStr += "\xff";
726 
727                     // temporarily set locale to "C" while converting floats
728                     const ScopedSafeLocale ssl;
729 
730                     for (uint32_t i=0; i<paramCount; ++i)
731                     {
732                         if (fPlugin.isParameterOutputOrTrigger(i))
733                             continue;
734 
735                         // join key and value
736                         String tmpStr;
737                         tmpStr  = fPlugin.getParameterSymbol(i);
738                         tmpStr += "\xff";
739                         tmpStr += String(fPlugin.getParameterValue(i));
740                         tmpStr += "\xff";
741 
742                         chunkStr += tmpStr;
743                     }
744                 }
745 
746                 const std::size_t chunkSize(chunkStr.length()+1);
747 
748                 fStateChunk = new char[chunkSize];
749                 std::memcpy(fStateChunk, chunkStr.buffer(), chunkStr.length());
750                 fStateChunk[chunkSize-1] = '\0';
751 
752                 for (std::size_t i=0; i<chunkSize; ++i)
753                 {
754                     if (fStateChunk[i] == '\xff')
755                         fStateChunk[i] = '\0';
756                 }
757 
758                 ret = chunkSize;
759             }
760 
761             *(void**)ptr = fStateChunk;
762             return ret;
763         }
764 
765         case effSetChunk:
766         {
767             if (value <= 1 || ptr == nullptr)
768                 return 0;
769 
770             const size_t chunkSize = static_cast<size_t>(value);
771 
772             const char* key   = (const char*)ptr;
773             const char* value = nullptr;
774             size_t size, bytesRead = 0;
775 
776             while (bytesRead < chunkSize)
777             {
778                 if (key[0] == '\0')
779                     break;
780 
781                 size  = std::strlen(key)+1;
782                 value = key + size;
783                 bytesRead += size;
784 
785                 setStateFromUI(key, value);
786 
787 # if DISTRHO_PLUGIN_HAS_UI
788                 if (fVstUI != nullptr)
789                     fVstUI->setStateFromPlugin(key, value);
790 # endif
791 
792                 // get next key
793                 size = std::strlen(value)+1;
794                 key  = value + size;
795                 bytesRead += size;
796             }
797 
798             const uint32_t paramCount = fPlugin.getParameterCount();
799 
800             if (bytesRead+4 < chunkSize && paramCount != 0)
801             {
802                 ++key;
803                 float fvalue;
804 
805                 // temporarily set locale to "C" while converting floats
806                 const ScopedSafeLocale ssl;
807 
808                 while (bytesRead < chunkSize)
809                 {
810                     if (key[0] == '\0')
811                         break;
812 
813                     size  = std::strlen(key)+1;
814                     value = key + size;
815                     bytesRead += size;
816 
817                     // find parameter with this symbol, and set its value
818                     for (uint32_t i=0; i<paramCount; ++i)
819                     {
820                         if (fPlugin.isParameterOutputOrTrigger(i))
821                             continue;
822                         if (fPlugin.getParameterSymbol(i) != key)
823                             continue;
824 
825                         fvalue = std::atof(value);
826                         fPlugin.setParameterValue(i, fvalue);
827 # if DISTRHO_PLUGIN_HAS_UI
828                         if (fVstUI != nullptr)
829                             setParameterValueFromPlugin(i, fvalue);
830 # endif
831                         break;
832                     }
833 
834                     // get next key
835                     size = std::strlen(value)+1;
836                     key  = value + size;
837                     bytesRead += size;
838                 }
839             }
840 
841             return 1;
842         }
843 #endif // DISTRHO_PLUGIN_WANT_STATE
844 
845 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
846         case effProcessEvents:
847             if (! fPlugin.isActive())
848             {
849                 // host has not activated the plugin yet, nasty!
850                 vst_dispatcher(effMainsChanged, 0, 1, nullptr, 0.0f);
851             }
852 
853             if (const VstEvents* const events = (const VstEvents*)ptr)
854             {
855                 if (events->numEvents == 0)
856                     break;
857 
858                 for (int i=0, count=events->numEvents; i < count; ++i)
859                 {
860                     const VstMidiEvent* const vstMidiEvent((const VstMidiEvent*)events->events[i]);
861 
862                     if (vstMidiEvent == nullptr)
863                         break;
864                     if (vstMidiEvent->type != kVstMidiType)
865                         continue;
866                     if (fMidiEventCount >= kMaxMidiEvents)
867                         break;
868 
869                     MidiEvent& midiEvent(fMidiEvents[fMidiEventCount++]);
870                     midiEvent.frame  = vstMidiEvent->deltaFrames;
871                     midiEvent.size   = 3;
872                     std::memcpy(midiEvent.data, vstMidiEvent->midiData, sizeof(uint8_t)*3);
873                 }
874             }
875             break;
876 #endif
877 
878         case effCanBeAutomated:
879             if (index < static_cast<int32_t>(fPlugin.getParameterCount()))
880             {
881                 const uint32_t hints(fPlugin.getParameterHints(index));
882 
883                 // must be automable, and not output
884                 if ((hints & kParameterIsAutomable) != 0 && (hints & kParameterIsOutput) == 0)
885                     return 1;
886             }
887             break;
888 
889         case effCanDo:
890             if (const char* const canDo = (const char*)ptr)
891             {
892 #if DISTRHO_OS_MAC && DISTRHO_PLUGIN_HAS_UI
893                 if (std::strcmp(canDo, "hasCockosViewAsConfig") == 0)
894                 {
895                     fUsingNsView = true;
896                     return 0xbeef0000;
897                 }
898 #endif
899                 if (std::strcmp(canDo, "receiveVstEvents") == 0 ||
900                     std::strcmp(canDo, "receiveVstMidiEvent") == 0)
901 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
902                     return 1;
903 #else
904                     return -1;
905 #endif
906                 if (std::strcmp(canDo, "sendVstEvents") == 0 ||
907                     std::strcmp(canDo, "sendVstMidiEvent") == 0)
908 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
909                     return 1;
910 #else
911                     return -1;
912 #endif
913                 if (std::strcmp(canDo, "receiveVstTimeInfo") == 0)
914 #if DISTRHO_PLUGIN_WANT_TIMEPOS
915                     return 1;
916 #else
917                     return -1;
918 #endif
919             }
920             break;
921 
922         case effVendorSpecific:
923 #if DISTRHO_PLUGIN_HAS_UI
924             if (index == CCONST('P', 'r', 'e', 'S') && value == CCONST('A', 'e', 'C', 's'))
925                 fLastScaleFactor = opt;
926 #endif
927             break;
928 
929         //case effStartProcess:
930         //case effStopProcess:
931         // unused
932         //    break;
933         }
934 
935         return 0;
936     }
937 
vst_getParameter(const int32_t index)938     float vst_getParameter(const int32_t index)
939     {
940         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
941         return ranges.getNormalizedValue(fPlugin.getParameterValue(index));
942     }
943 
vst_setParameter(const int32_t index,const float value)944     void vst_setParameter(const int32_t index, const float value)
945     {
946         const uint32_t hints(fPlugin.getParameterHints(index));
947         const ParameterRanges& ranges(fPlugin.getParameterRanges(index));
948 
949         // TODO figure out how to detect kVstParameterUsesIntegerMinMax host support, and skip normalization
950         float realValue = ranges.getUnnormalizedValue(value);
951 
952         if (hints & kParameterIsBoolean)
953         {
954             const float midRange = ranges.min + (ranges.max - ranges.min) / 2.0f;
955             realValue = realValue > midRange ? ranges.max : ranges.min;
956         }
957 
958         if (hints & kParameterIsInteger)
959         {
960             realValue = std::round(realValue);
961         }
962 
963         fPlugin.setParameterValue(index, realValue);
964 
965 #if DISTRHO_PLUGIN_HAS_UI
966         if (fVstUI != nullptr)
967             setParameterValueFromPlugin(index, realValue);
968 #endif
969     }
970 
vst_processReplacing(const float ** const inputs,float ** const outputs,const int32_t sampleFrames)971     void vst_processReplacing(const float** const inputs, float** const outputs, const int32_t sampleFrames)
972     {
973         if (! fPlugin.isActive())
974         {
975             // host has not activated the plugin yet, nasty!
976             vst_dispatcher(effMainsChanged, 0, 1, nullptr, 0.0f);
977         }
978 
979         if (sampleFrames <= 0)
980         {
981             updateParameterOutputsAndTriggers();
982             return;
983         }
984 
985 #if DISTRHO_PLUGIN_WANT_TIMEPOS
986         static const int kWantVstTimeFlags(kVstTransportPlaying|kVstPpqPosValid|kVstTempoValid|kVstTimeSigValid);
987 
988         if (const VstTimeInfo* const vstTimeInfo = (const VstTimeInfo*)hostCallback(audioMasterGetTime, 0, kWantVstTimeFlags))
989         {
990             fTimePosition.frame     =   vstTimeInfo->samplePos;
991             fTimePosition.playing   =  (vstTimeInfo->flags & kVstTransportPlaying);
992             fTimePosition.bbt.valid = ((vstTimeInfo->flags & kVstTempoValid) != 0 || (vstTimeInfo->flags & kVstTimeSigValid) != 0);
993 
994             // ticksPerBeat is not possible with VST
995             fTimePosition.bbt.ticksPerBeat = 960.0;
996 
997             if (vstTimeInfo->flags & kVstTempoValid)
998                 fTimePosition.bbt.beatsPerMinute = vstTimeInfo->tempo;
999             else
1000                 fTimePosition.bbt.beatsPerMinute = 120.0;
1001 
1002             if (vstTimeInfo->flags & (kVstPpqPosValid|kVstTimeSigValid))
1003             {
1004                 const double ppqPos    = std::abs(vstTimeInfo->ppqPos);
1005                 const int    ppqPerBar = vstTimeInfo->timeSigNumerator * 4 / vstTimeInfo->timeSigDenominator;
1006                 const double barBeats  = (std::fmod(ppqPos, ppqPerBar) / ppqPerBar) * vstTimeInfo->timeSigNumerator;
1007                 const double rest      =  std::fmod(barBeats, 1.0);
1008 
1009                 fTimePosition.bbt.bar         = static_cast<int32_t>(ppqPos) / ppqPerBar + 1;
1010                 fTimePosition.bbt.beat        = static_cast<int32_t>(barBeats - rest + 0.5) + 1;
1011                 fTimePosition.bbt.tick        = static_cast<int32_t>(rest * fTimePosition.bbt.ticksPerBeat + 0.5);
1012                 fTimePosition.bbt.beatsPerBar = vstTimeInfo->timeSigNumerator;
1013                 fTimePosition.bbt.beatType    = vstTimeInfo->timeSigDenominator;
1014 
1015                 if (vstTimeInfo->ppqPos < 0.0)
1016                 {
1017                     --fTimePosition.bbt.bar;
1018                     fTimePosition.bbt.beat = vstTimeInfo->timeSigNumerator - fTimePosition.bbt.beat + 1;
1019                     fTimePosition.bbt.tick = int(fTimePosition.bbt.ticksPerBeat) - fTimePosition.bbt.tick - 1;
1020                 }
1021             }
1022             else
1023             {
1024                 fTimePosition.bbt.bar         = 1;
1025                 fTimePosition.bbt.beat        = 1;
1026                 fTimePosition.bbt.tick        = 0;
1027                 fTimePosition.bbt.beatsPerBar = 4.0f;
1028                 fTimePosition.bbt.beatType    = 4.0f;
1029             }
1030 
1031             fTimePosition.bbt.barStartTick = fTimePosition.bbt.ticksPerBeat*fTimePosition.bbt.beatsPerBar*(fTimePosition.bbt.bar-1);
1032 
1033             fPlugin.setTimePosition(fTimePosition);
1034         }
1035 #endif
1036 
1037 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
1038         fPlugin.run(inputs, outputs, sampleFrames, fMidiEvents, fMidiEventCount);
1039         fMidiEventCount = 0;
1040 #else
1041         fPlugin.run(inputs, outputs, sampleFrames);
1042 #endif
1043 
1044         updateParameterOutputsAndTriggers();
1045     }
1046 
1047     // -------------------------------------------------------------------
1048 
1049     friend class UIVst;
1050 
1051 private:
1052     // Plugin
1053     PluginExporter fPlugin;
1054 
1055     // VST stuff
1056     const audioMasterCallback fAudioMaster;
1057     AEffect* const fEffect;
1058 
1059     // Temporary data
1060     char fProgramName[32+1];
1061 
1062 #if DISTRHO_PLUGIN_WANT_MIDI_INPUT
1063     uint32_t  fMidiEventCount;
1064     MidiEvent fMidiEvents[kMaxMidiEvents];
1065 #endif
1066 
1067 #if DISTRHO_PLUGIN_WANT_TIMEPOS
1068     TimePosition fTimePosition;
1069 #endif
1070 
1071     // UI stuff
1072 #if DISTRHO_PLUGIN_HAS_UI
1073     UIVst* fVstUI;
1074     ERect  fVstRect;
1075     float  fLastScaleFactor;
1076 # if DISTRHO_OS_MAC
1077     bool fUsingNsView;
1078 # endif
1079 #endif
1080 
1081 #if DISTRHO_PLUGIN_WANT_STATE
1082     char*     fStateChunk;
1083     StringMap fStateMap;
1084 #endif
1085 
1086     // -------------------------------------------------------------------
1087     // host callback
1088 
hostCallback(const int32_t opcode,const int32_t index=0,const intptr_t value=0,void * const ptr=nullptr,const float opt=0.0f)1089     intptr_t hostCallback(const int32_t opcode,
1090                           const int32_t index = 0,
1091                           const intptr_t value = 0,
1092                           void* const ptr = nullptr,
1093                           const float opt = 0.0f)
1094     {
1095         return fAudioMaster(fEffect, opcode, index, value, ptr, opt);
1096     }
1097 
1098     // -------------------------------------------------------------------
1099     // functions called from the plugin side, RT no block
1100 
updateParameterOutputsAndTriggers()1101     void updateParameterOutputsAndTriggers()
1102     {
1103         float curValue;
1104 
1105         for (uint32_t i=0, count=fPlugin.getParameterCount(); i < count; ++i)
1106         {
1107             if (fPlugin.isParameterOutput(i))
1108             {
1109                 // NOTE: no output parameter support in VST, simulate it here
1110                 curValue = fPlugin.getParameterValue(i);
1111 
1112                 if (d_isEqual(curValue, parameterValues[i]))
1113                     continue;
1114 
1115 #if DISTRHO_PLUGIN_HAS_UI
1116                 if (fVstUI != nullptr)
1117                     setParameterValueFromPlugin(i, curValue);
1118                 else
1119 #endif
1120                 parameterValues[i] = curValue;
1121 
1122 #ifndef DPF_VST_SHOW_PARAMETER_OUTPUTS
1123                 // skip automating parameter outputs from plugin if we disable them on VST
1124                 continue;
1125 #endif
1126             }
1127             else if ((fPlugin.getParameterHints(i) & kParameterIsTrigger) == kParameterIsTrigger)
1128             {
1129                 // NOTE: no trigger support in VST parameters, simulate it here
1130                 curValue = fPlugin.getParameterValue(i);
1131 
1132                 if (d_isEqual(curValue, fPlugin.getParameterRanges(i).def))
1133                     continue;
1134 
1135 #if DISTRHO_PLUGIN_HAS_UI
1136                 if (fVstUI != nullptr)
1137                     setParameterValueFromPlugin(i, curValue);
1138 #endif
1139                 fPlugin.setParameterValue(i, curValue);
1140             }
1141             else
1142             {
1143                 continue;
1144             }
1145 
1146             const ParameterRanges& ranges(fPlugin.getParameterRanges(i));
1147             hostCallback(audioMasterAutomate, i, 0, nullptr, ranges.getNormalizedValue(curValue));
1148         }
1149     }
1150 
1151 #if DISTRHO_PLUGIN_HAS_UI
setParameterValueFromPlugin(const uint32_t index,const float realValue)1152     void setParameterValueFromPlugin(const uint32_t index, const float realValue)
1153     {
1154         parameterValues[index] = realValue;
1155         parameterChecks[index] = true;
1156     }
1157 #endif
1158 
1159 #if DISTRHO_PLUGIN_WANT_MIDI_OUTPUT
writeMidi(const MidiEvent & midiEvent)1160     bool writeMidi(const MidiEvent& midiEvent)
1161     {
1162         if (midiEvent.size > 4)
1163             return true;
1164 
1165         VstEvents vstEvents;
1166         std::memset(&vstEvents, 0, sizeof(VstEvents));
1167 
1168         VstMidiEvent vstMidiEvent;
1169         std::memset(&vstMidiEvent, 0, sizeof(VstMidiEvent));
1170 
1171         vstEvents.numEvents = 1;
1172         vstEvents.events[0] = (VstEvent*)&vstMidiEvent;
1173 
1174         vstMidiEvent.type = kVstMidiType;
1175         vstMidiEvent.byteSize    = kVstMidiEventSize;
1176         vstMidiEvent.deltaFrames = midiEvent.frame;
1177 
1178         for (uint8_t i=0; i<midiEvent.size; ++i)
1179             vstMidiEvent.midiData[i] = midiEvent.data[i];
1180 
1181         return hostCallback(audioMasterProcessEvents, 0, 0, &vstEvents) == 1;
1182     }
1183 
writeMidiCallback(void * ptr,const MidiEvent & midiEvent)1184     static bool writeMidiCallback(void* ptr, const MidiEvent& midiEvent)
1185     {
1186         return ((PluginVst*)ptr)->writeMidi(midiEvent);
1187     }
1188 #endif
1189 
1190 #if DISTRHO_PLUGIN_WANT_STATE
1191     // -------------------------------------------------------------------
1192     // functions called from the UI side, may block
1193 
1194 # if DISTRHO_PLUGIN_HAS_UI
setStateFromUI(const char * const key,const char * const newValue)1195     void setStateFromUI(const char* const key, const char* const newValue) override
1196 # else
1197     void setStateFromUI(const char* const key, const char* const newValue)
1198 # endif
1199     {
1200         fPlugin.setState(key, newValue);
1201 
1202         // check if we want to save this key
1203         if (! fPlugin.wantStateKey(key))
1204             return;
1205 
1206         // check if key already exists
1207         for (StringMap::iterator it=fStateMap.begin(), ite=fStateMap.end(); it != ite; ++it)
1208         {
1209             const String& dkey(it->first);
1210 
1211             if (dkey == key)
1212             {
1213                 it->second = newValue;
1214                 return;
1215             }
1216         }
1217 
1218         d_stderr("Failed to find plugin state with key \"%s\"", key);
1219     }
1220 #endif
1221 };
1222 
1223 // -----------------------------------------------------------------------
1224 
1225 struct VstObject {
1226     audioMasterCallback audioMaster;
1227     PluginVst* plugin;
1228 };
1229 
1230 #define validObject  effect != nullptr && effect->object != nullptr
1231 #define validPlugin  effect != nullptr && effect->object != nullptr && ((VstObject*)effect->object)->plugin != nullptr
1232 #define vstObjectPtr (VstObject*)effect->object
1233 #define pluginPtr    (vstObjectPtr)->plugin
1234 
vst_dispatcherCallback(AEffect * effect,int32_t opcode,int32_t index,intptr_t value,void * ptr,float opt)1235 static intptr_t vst_dispatcherCallback(AEffect* effect, int32_t opcode, int32_t index, intptr_t value, void* ptr, float opt)
1236 {
1237     // first internal init
1238     const bool doInternalInit = (opcode == -1729 && index == 0xdead && value == 0xf00d);
1239 
1240     if (doInternalInit)
1241     {
1242         // set valid but dummy values
1243         d_lastBufferSize = 512;
1244         d_lastSampleRate = 44100.0;
1245     }
1246 
1247     // Create dummy plugin to get data from
1248     static PluginExporter plugin(nullptr, nullptr);
1249 
1250     if (doInternalInit)
1251     {
1252         // unset
1253         d_lastBufferSize = 0;
1254         d_lastSampleRate = 0.0;
1255 
1256         *(PluginExporter**)ptr = &plugin;
1257         return 0;
1258     }
1259 
1260     // handle base opcodes
1261     switch (opcode)
1262     {
1263     case effOpen:
1264         if (VstObject* const obj = vstObjectPtr)
1265         {
1266             // this must always be valid
1267             DISTRHO_SAFE_ASSERT_RETURN(obj->audioMaster != nullptr, 0);
1268 
1269             // some hosts call effOpen twice
1270             DISTRHO_SAFE_ASSERT_RETURN(obj->plugin == nullptr, 1);
1271 
1272             audioMasterCallback audioMaster = (audioMasterCallback)obj->audioMaster;
1273 
1274             d_lastBufferSize = audioMaster(effect, audioMasterGetBlockSize, 0, 0, nullptr, 0.0f);
1275             d_lastSampleRate = audioMaster(effect, audioMasterGetSampleRate, 0, 0, nullptr, 0.0f);
1276 
1277             // some hosts are not ready at this point or return 0 buffersize/samplerate
1278             if (d_lastBufferSize == 0)
1279                 d_lastBufferSize = 2048;
1280             if (d_lastSampleRate <= 0.0)
1281                 d_lastSampleRate = 44100.0;
1282 
1283             obj->plugin = new PluginVst(audioMaster, effect);
1284             return 1;
1285         }
1286         return 0;
1287 
1288     case effClose:
1289         if (VstObject* const obj = vstObjectPtr)
1290         {
1291             if (obj->plugin != nullptr)
1292             {
1293                 delete obj->plugin;
1294                 obj->plugin = nullptr;
1295             }
1296 
1297 #if 0
1298             /* This code invalidates the object created in VSTPluginMain
1299              * Probably not safe against all hosts */
1300             obj->audioMaster = nullptr;
1301             effect->object = nullptr;
1302             delete obj;
1303 #endif
1304 
1305             return 1;
1306         }
1307         //delete effect;
1308         return 0;
1309 
1310     case effGetParamLabel:
1311         if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount()))
1312         {
1313             DISTRHO_NAMESPACE::strncpy((char*)ptr, plugin.getParameterUnit(index), 8);
1314             return 1;
1315         }
1316         return 0;
1317 
1318     case effGetParamName:
1319         if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount()))
1320         {
1321             const String& shortName(plugin.getParameterShortName(index));
1322             if (shortName.isNotEmpty())
1323                 DISTRHO_NAMESPACE::strncpy((char*)ptr, shortName, 16);
1324             else
1325                 DISTRHO_NAMESPACE::strncpy((char*)ptr, plugin.getParameterName(index), 16);
1326             return 1;
1327         }
1328         return 0;
1329 
1330     case effGetParameterProperties:
1331         if (ptr != nullptr && index < static_cast<int32_t>(plugin.getParameterCount()))
1332         {
1333             if (VstParameterProperties* const properties = (VstParameterProperties*)ptr)
1334             {
1335                 memset(properties, 0, sizeof(VstParameterProperties));
1336 
1337                 const uint32_t hints = plugin.getParameterHints(index);
1338 
1339                 if (hints & kParameterIsOutput)
1340                     return 1;
1341 
1342                 if (hints & kParameterIsBoolean)
1343                 {
1344                     properties->flags |= kVstParameterIsSwitch;
1345                 }
1346 
1347                 if (hints & kParameterIsInteger)
1348                 {
1349                     properties->flags |= kVstParameterUsesIntegerMinMax;
1350                     const ParameterRanges& ranges(plugin.getParameterRanges(index));
1351 
1352                     properties->minInteger = static_cast<int32_t>(ranges.min);
1353                     properties->maxInteger = static_cast<int32_t>(ranges.max);
1354                 }
1355 
1356                 if (hints & kParameterIsLogarithmic)
1357                 {
1358                     properties->flags |= kVstParameterCanRamp;
1359                 }
1360 
1361                 return 1;
1362             }
1363         }
1364         return 0;
1365 
1366     case effGetPlugCategory:
1367 #if DISTRHO_PLUGIN_IS_SYNTH
1368         return kPlugCategSynth;
1369 #else
1370         return kPlugCategEffect;
1371 #endif
1372 
1373     case effGetEffectName:
1374         if (char* const cptr = (char*)ptr)
1375         {
1376             DISTRHO_NAMESPACE::strncpy(cptr, plugin.getName(), 32);
1377             return 1;
1378         }
1379         return 0;
1380 
1381     case effGetVendorString:
1382         if (char* const cptr = (char*)ptr)
1383         {
1384             DISTRHO_NAMESPACE::strncpy(cptr, plugin.getMaker(), 32);
1385             return 1;
1386         }
1387         return 0;
1388 
1389     case effGetProductString:
1390         if (char* const cptr = (char*)ptr)
1391         {
1392             DISTRHO_NAMESPACE::strncpy(cptr, plugin.getLabel(), 32);
1393             return 1;
1394         }
1395         return 0;
1396 
1397     case effGetVendorVersion:
1398         return plugin.getVersion();
1399 
1400     case effGetVstVersion:
1401         return kVstVersion;
1402     };
1403 
1404     // handle advanced opcodes
1405     if (validPlugin)
1406         return pluginPtr->vst_dispatcher(opcode, index, value, ptr, opt);
1407 
1408     return 0;
1409 }
1410 
vst_getParameterCallback(AEffect * effect,int32_t index)1411 static float vst_getParameterCallback(AEffect* effect, int32_t index)
1412 {
1413     if (validPlugin)
1414         return pluginPtr->vst_getParameter(index);
1415     return 0.0f;
1416 }
1417 
vst_setParameterCallback(AEffect * effect,int32_t index,float value)1418 static void vst_setParameterCallback(AEffect* effect, int32_t index, float value)
1419 {
1420     if (validPlugin)
1421         pluginPtr->vst_setParameter(index, value);
1422 }
1423 
vst_processCallback(AEffect * effect,float ** inputs,float ** outputs,int32_t sampleFrames)1424 static void vst_processCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames)
1425 {
1426     if (validPlugin)
1427         pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames);
1428 }
1429 
vst_processReplacingCallback(AEffect * effect,float ** inputs,float ** outputs,int32_t sampleFrames)1430 static void vst_processReplacingCallback(AEffect* effect, float** inputs, float** outputs, int32_t sampleFrames)
1431 {
1432     if (validPlugin)
1433         pluginPtr->vst_processReplacing(const_cast<const float**>(inputs), outputs, sampleFrames);
1434 }
1435 
1436 #undef pluginPtr
1437 #undef validObject
1438 #undef validPlugin
1439 #undef vstObjectPtr
1440 
1441 // -----------------------------------------------------------------------
1442 
1443 END_NAMESPACE_DISTRHO
1444 
1445 DISTRHO_PLUGIN_EXPORT
1446 #if DISTRHO_OS_WINDOWS || DISTRHO_OS_MAC
1447 const AEffect* VSTPluginMain(audioMasterCallback audioMaster);
1448 #else
1449 const AEffect* VSTPluginMain(audioMasterCallback audioMaster) asm ("main");
1450 #endif
1451 
1452 DISTRHO_PLUGIN_EXPORT
VSTPluginMain(audioMasterCallback audioMaster)1453 const AEffect* VSTPluginMain(audioMasterCallback audioMaster)
1454 {
1455     USE_NAMESPACE_DISTRHO
1456 
1457     // old version
1458     if (audioMaster(nullptr, audioMasterVersion, 0, 0, nullptr, 0.0f) == 0)
1459         return nullptr;
1460 
1461     // first internal init
1462     PluginExporter* plugin = nullptr;
1463     vst_dispatcherCallback(nullptr, -1729, 0xdead, 0xf00d, &plugin, 0.0f);
1464     DISTRHO_SAFE_ASSERT_RETURN(plugin != nullptr, nullptr);
1465 
1466     AEffect* const effect(new AEffect);
1467     std::memset(effect, 0, sizeof(AEffect));
1468 
1469     // vst fields
1470     effect->magic    = kEffectMagic;
1471     effect->uniqueID = plugin->getUniqueId();
1472     effect->version  = plugin->getVersion();
1473 
1474     // VST doesn't support parameter outputs. we can fake them, but it is a hack. Disabled by default.
1475 #ifdef DPF_VST_SHOW_PARAMETER_OUTPUTS
1476     const int numParams = plugin->getParameterCount();
1477 #else
1478     int numParams = 0;
1479     bool outputsReached = false;
1480 
1481     for (uint32_t i=0, count=plugin->getParameterCount(); i < count; ++i)
1482     {
1483         if (plugin->isParameterInput(i))
1484         {
1485             // parameter outputs must be all at the end
1486             DISTRHO_SAFE_ASSERT_BREAK(! outputsReached);
1487             ++numParams;
1488             continue;
1489         }
1490         outputsReached = true;
1491     }
1492 #endif
1493 
1494     // plugin fields
1495     effect->numParams   = numParams;
1496     effect->numPrograms = 1;
1497     effect->numInputs   = DISTRHO_PLUGIN_NUM_INPUTS;
1498     effect->numOutputs  = DISTRHO_PLUGIN_NUM_OUTPUTS;
1499 
1500     // plugin flags
1501     effect->flags |= effFlagsCanReplacing;
1502 #if DISTRHO_PLUGIN_IS_SYNTH
1503     effect->flags |= effFlagsIsSynth;
1504 #endif
1505 #if DISTRHO_PLUGIN_HAS_UI
1506     effect->flags |= effFlagsHasEditor;
1507 #endif
1508 #if DISTRHO_PLUGIN_WANT_STATE
1509     effect->flags |= effFlagsProgramChunks;
1510 #endif
1511 
1512     // static calls
1513     effect->dispatcher   = vst_dispatcherCallback;
1514     effect->process      = vst_processCallback;
1515     effect->getParameter = vst_getParameterCallback;
1516     effect->setParameter = vst_setParameterCallback;
1517     effect->processReplacing = vst_processReplacingCallback;
1518 
1519     // pointers
1520     VstObject* const obj(new VstObject());
1521     obj->audioMaster = audioMaster;
1522     obj->plugin      = nullptr;
1523 
1524     // done
1525     effect->object = obj;
1526 
1527     return effect;
1528 }
1529 
1530 // -----------------------------------------------------------------------
1531