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