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