iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
IPlugVST2.cpp
1/*
2 ==============================================================================
3
4 This file is part of the iPlug 2 library. Copyright (C) the iPlug 2 developers.
5
6 See LICENSE.txt for more info.
7
8 ==============================================================================
9*/
10
11#include <cstdio>
12#include "IPlugVST2.h"
13#include "IPlugPluginBase.h"
14
15using namespace iplug;
16
17static const int VST_VERSION = 2400;
18
19static int VSTSpkrArrType(int nchan)
20{
21 if (!nchan) return kSpeakerArrEmpty;
22 if (nchan == 1) return kSpeakerArrMono;
23 if (nchan == 2) return kSpeakerArrStereo;
24 return kSpeakerArrUserDefined;
25}
26
27static int AsciiToVK(int ascii)
28{
29#ifdef OS_WIN
30 HKL layout = GetKeyboardLayout(0);
31 return VkKeyScanExA((CHAR)ascii, layout);
32#else
33 // Numbers and uppercase alpha chars map directly to VK
34 if ((ascii >= 0x30 && ascii <= 0x39) || (ascii >= 0x41 && ascii <= 0x5A))
35 {
36 return ascii;
37 }
38
39 // Lowercase alpha chars map to VK but need shifting
40 if (ascii >= 0x61 && ascii <= 0x7A)
41 {
42 return ascii - 0x20;
43 }
44
45 return kVK_NONE;
46#endif
47}
48
49static int VSTKeyCodeToVK(int code, int ascii)
50{
51 // If the keycode provided by the host is 0, we can still calculate the VK from the ascii value
52 // NOTE: VKEY_EQUALS Doesn't seem to map to a Windows VK, so get the VK from the ascii char instead
53 if (code == 0 || code == VKEY_EQUALS)
54 {
55 return AsciiToVK(ascii);
56 }
57
58 switch (code)
59 {
60 case VKEY_BACK: return kVK_BACK;
61 case VKEY_TAB: return kVK_TAB;
62 case VKEY_CLEAR: return kVK_CLEAR;
63 case VKEY_RETURN: return kVK_RETURN;
64 case VKEY_PAUSE: return kVK_PAUSE;
65 case VKEY_ESCAPE: return kVK_ESCAPE;
66 case VKEY_SPACE: return kVK_SPACE;
67 case VKEY_NEXT: return kVK_NEXT;
68 case VKEY_END: return kVK_END;
69 case VKEY_HOME: return kVK_HOME;
70 case VKEY_LEFT: return kVK_LEFT;
71 case VKEY_UP: return kVK_UP;
72 case VKEY_RIGHT: return kVK_RIGHT;
73 case VKEY_DOWN: return kVK_DOWN;
74 case VKEY_PAGEUP: return kVK_PRIOR;
75 case VKEY_PAGEDOWN: return kVK_NEXT;
76 case VKEY_SELECT: return kVK_SELECT;
77 case VKEY_PRINT: return kVK_PRINT;
78 case VKEY_ENTER: return kVK_RETURN;
79 case VKEY_SNAPSHOT: return kVK_SNAPSHOT;
80 case VKEY_INSERT: return kVK_INSERT;
81 case VKEY_DELETE: return kVK_DELETE;
82 case VKEY_HELP: return kVK_HELP;
83 case VKEY_NUMPAD0: return kVK_NUMPAD0;
84 case VKEY_NUMPAD1: return kVK_NUMPAD1;
85 case VKEY_NUMPAD2: return kVK_NUMPAD2;
86 case VKEY_NUMPAD3: return kVK_NUMPAD3;
87 case VKEY_NUMPAD4: return kVK_NUMPAD4;
88 case VKEY_NUMPAD5: return kVK_NUMPAD5;
89 case VKEY_NUMPAD6: return kVK_NUMPAD6;
90 case VKEY_NUMPAD7: return kVK_NUMPAD7;
91 case VKEY_NUMPAD8: return kVK_NUMPAD8;
92 case VKEY_NUMPAD9: return kVK_NUMPAD9;
93 case VKEY_MULTIPLY: return kVK_MULTIPLY;
94 case VKEY_ADD: return kVK_ADD;
95 case VKEY_SEPARATOR: return kVK_SEPARATOR;
96 case VKEY_SUBTRACT: return kVK_SUBTRACT;
97 case VKEY_DECIMAL: return kVK_DECIMAL;
98 case VKEY_DIVIDE: return kVK_DIVIDE;
99 case VKEY_F1: return kVK_F1;
100 case VKEY_F2: return kVK_F2;
101 case VKEY_F3: return kVK_F3;
102 case VKEY_F4: return kVK_F4;
103 case VKEY_F5: return kVK_F5;
104 case VKEY_F6: return kVK_F6;
105 case VKEY_F7: return kVK_F7;
106 case VKEY_F8: return kVK_F8;
107 case VKEY_F9: return kVK_F9;
108 case VKEY_F10: return kVK_F10;
109 case VKEY_F11: return kVK_F11;
110 case VKEY_F12: return kVK_F12;
111 case VKEY_NUMLOCK: return kVK_NUMLOCK;
112 case VKEY_SCROLL: return kVK_SCROLL;
113 case VKEY_SHIFT: return kVK_SHIFT;
114 case VKEY_CONTROL: return kVK_CONTROL;
115 case VKEY_ALT: return kVK_MENU;
116 case VKEY_EQUALS: return kVK_NONE;
117 }
118
119 return kVK_NONE;
120}
121
122IPlugVST2::IPlugVST2(const InstanceInfo& info, const Config& config)
123 : IPlugAPIBase(config, kAPIVST2)
124 , IPlugProcessor(config, kAPIVST2)
125 , mHostCallback(info.mVSTHostCallback)
126{
127 Trace(TRACELOC, "%s", config.pluginName);
128
129 mHasVSTExtensions = VSTEXT_NONE;
130
131 int nInputs = MaxNChannels(ERoute::kInput), nOutputs = MaxNChannels(ERoute::kOutput);
132
133 memset(&mAEffect, 0, sizeof(AEffect));
134 mAEffect.object = this;
135 mAEffect.magic = kEffectMagic;
136 mAEffect.dispatcher = VSTDispatcher;
137 mAEffect.getParameter = VSTGetParameter;
138 mAEffect.setParameter = VSTSetParameter;
139 mAEffect.numPrograms = config.nPresets;
140 mAEffect.numParams = config.nParams;
141 mAEffect.numInputs = nInputs;
142 mAEffect.numOutputs = nOutputs;
143 mAEffect.uniqueID = config.uniqueID;
144 mAEffect.version = GetPluginVersion(true);
145 mAEffect.__ioRatioDeprecated = 1.0f;
146 mAEffect.__processDeprecated = VSTProcess;
147 mAEffect.processReplacing = VSTProcessReplacing;
148 mAEffect.processDoubleReplacing = VSTProcessDoubleReplacing;
149 mAEffect.initialDelay = config.latency;
150 mAEffect.flags = effFlagsCanReplacing | effFlagsCanDoubleReplacing;
151
152 if (config.plugDoesChunks) { mAEffect.flags |= effFlagsProgramChunks; }
153 if (LegalIO(1, -1)) { mAEffect.flags |= __effFlagsCanMonoDeprecated; }
154 if (config.plugType == EIPlugPluginType::kInstrument) { mAEffect.flags |= effFlagsIsSynth; }
155
156 memset(&mEditRect, 0, sizeof(ERect));
157 memset(&mInputSpkrArr, 0, sizeof(VstSpeakerArrangement));
158 memset(&mOutputSpkrArr, 0, sizeof(VstSpeakerArrangement));
159 mInputSpkrArr.numChannels = nInputs;
160 mOutputSpkrArr.numChannels = nOutputs;
161 mInputSpkrArr.type = VSTSpkrArrType(nInputs);
162 mOutputSpkrArr.type = VSTSpkrArrType(nOutputs);
163
164 // Default everything to connected, then disconnect pins if the host says to.
165 SetChannelConnections(ERoute::kInput, 0, nInputs, true);
166 SetChannelConnections(ERoute::kOutput, 0, nOutputs, true);
167
168 SetBlockSize(DEFAULT_BLOCK_SIZE);
169
170 if (config.plugHasUI)
171 {
172 mAEffect.flags |= effFlagsHasEditor;
173 UpdateEditRect();
174 }
175
176 CreateTimer();
177}
178
180{
181 mHostCallback(&mAEffect, audioMasterBeginEdit, idx, 0, 0, 0.0f);
182}
183
184void IPlugVST2::InformHostOfParamChange(int idx, double normalizedValue)
185{
186 mHostCallback(&mAEffect, audioMasterAutomate, idx, 0, 0, (float) normalizedValue);
187}
188
190{
191 mHostCallback(&mAEffect, audioMasterEndEdit, idx, 0, 0, 0.0f);
192}
193
195{
196 mHostCallback(&mAEffect, audioMasterUpdateDisplay, 0, 0, 0, 0.0f);
197}
198
199bool IPlugVST2::EditorResize(int viewWidth, int viewHeight)
200{
201 bool resized = false;
202
203 if (HasUI())
204 {
205 if (viewWidth != GetEditorWidth() || viewHeight != GetEditorHeight())
206 {
207 SetEditorSize(viewWidth, viewHeight);
208 UpdateEditRect();
209
210 resized = mHostCallback(&mAEffect, audioMasterSizeWindow, viewWidth, viewHeight, 0, 0.f);
211 }
212 }
213
214 return resized;
215}
216
217void IPlugVST2::UpdateEditRect()
218{
219 mEditRect.left = mEditRect.top = 0;
220 mEditRect.right = GetEditorWidth();
221 mEditRect.bottom = GetEditorHeight();
222}
223
224void IPlugVST2::SetLatency(int samples)
225{
226 mAEffect.initialDelay = samples;
228 mHostCallback(&mAEffect, audioMasterIOChanged, 0, 0, 0, 0.0f);
229}
230
231bool IPlugVST2::SendVSTEvent(VstEvent& event)
232{
233 // It would be more efficient to bundle these and send at the end of a processed block,
234 // but that would require writing OnBlockEnd and making sure it always gets called,
235 // and who cares anyway, midi events aren't that dense.
236 VstEvents events;
237 memset(&events, 0, sizeof(VstEvents));
238 events.numEvents = 1;
239 events.events[0] = &event;
240 return (mHostCallback(&mAEffect, audioMasterProcessEvents, 0, 0, &events, 0.0f) == 1);
241}
242
244{
245 VstMidiEvent midiEvent;
246 memset(&midiEvent, 0, sizeof(VstMidiEvent));
247
248 midiEvent.type = kVstMidiType;
249 midiEvent.byteSize = sizeof(VstMidiEvent); // Should this be smaller?
250 midiEvent.deltaFrames = msg.mOffset;
251 midiEvent.midiData[0] = msg.mStatus;
252 midiEvent.midiData[1] = msg.mData1;
253 midiEvent.midiData[2] = msg.mData2;
254
255 return SendVSTEvent((VstEvent&) midiEvent);
256}
257
259{
260 VstMidiSysexEvent sysexEvent;
261 memset(&sysexEvent, 0, sizeof(VstMidiSysexEvent));
262
263 sysexEvent.type = kVstSysExType;
264 sysexEvent.byteSize = sizeof(VstMidiSysexEvent);
265 sysexEvent.deltaFrames = msg.mOffset;
266 sysexEvent.dumpBytes = msg.mSize;
267 sysexEvent.sysexDump = (char*) msg.mData;
268
269 return SendVSTEvent((VstEvent&) sysexEvent);
270}
271
273{
274 switch (GetHost())
275 {
276 case kHostAudition:
277 case kHostOrion:
278 case kHostForte:
279 case kHostSAWStudio:
280 LimitToStereoIO(); //TODO: is this still necessary?
281 break;
282 default:
283 break;
284 }
285
286 // This won't always solve a picky host problem -- for example Forte
287 // looks at mAEffect IO count before identifying itself.
288 mAEffect.numInputs = mInputSpkrArr.numChannels = MaxNChannels(ERoute::kInput);
289 mAEffect.numOutputs = mOutputSpkrArr.numChannels = MaxNChannels(ERoute::kOutput);
290}
291
292VstIntPtr VSTCALLBACK IPlugVST2::VSTDispatcher(AEffect *pEffect, VstInt32 opCode, VstInt32 idx, VstIntPtr value, void *ptr, float opt)
293{
294 // VSTDispatcher is an IPlugVST class member, we can access anything in IPlugVST from here.
295 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
296 if (!_this)
297 {
298 return 0;
299 }
300
301 // Handle a couple of opcodes here to make debugging easier.
302// switch (opCode)
303// {
304// case effEditIdle:
305// case __effIdleDeprecated:
306// #ifdef USE_IDLE_CALLS
307// _this->OnIdle();
308// #endif
309// return 0;
310// }
311
312 Trace(TRACELOC, "%d(%s):%d:%d", opCode, VSTOpcodeStr(opCode), idx, (int) value);
313
314 switch (opCode)
315 {
316 case effOpen:
317 {
318 if (_this->GetHost() == kHostUninit)
319 {
320 char productStr[256];
321 productStr[0] = '\0';
322 int version = 0;
323 _this->mHostCallback(&_this->mAEffect, audioMasterGetProductString, 0, 0, productStr, 0.0f);
324
325 if (CStringHasContents(productStr))
326 {
327 int decVer = (int) _this->mHostCallback(&_this->mAEffect, audioMasterGetVendorVersion, 0, 0, 0, 0.0f);
328 int ver = decVer / 10000;
329 int rmaj = (decVer - 10000 * ver) / 100;
330 int rmin = (decVer - 10000 * ver - 100 * rmaj);
331 version = (ver << 16) + (rmaj << 8) + rmin;
332 }
333
334 _this->SetHost(productStr, version);
335 }
336 _this->OnParamReset(kReset);
337 return 0;
338 }
339 case effClose:
340 {
341 delete _this;
342 return 0;
343 }
344 case effGetParamLabel:
345 {
346 if (idx >= 0 && idx < _this->NParams())
347 {
348 ENTER_PARAMS_MUTEX_STATIC
349 strcpy((char*) ptr, _this->GetParam(idx)->GetLabel());
350 LEAVE_PARAMS_MUTEX_STATIC
351 }
352 return 0;
353 }
354 case effGetParamDisplay:
355 {
356 if (idx >= 0 && idx < _this->NParams())
357 {
358 ENTER_PARAMS_MUTEX_STATIC
359 _this->GetParam(idx)->GetDisplay(_this->mParamDisplayStr);
360 LEAVE_PARAMS_MUTEX_STATIC
361 strcpy((char*) ptr, _this->mParamDisplayStr.Get());
362 }
363 return 0;
364 }
365 case effGetParamName:
366 {
367 if (idx >= 0 && idx < _this->NParams())
368 {
369 ENTER_PARAMS_MUTEX_STATIC
370 strcpy((char*) ptr, _this->GetParam(idx)->GetName());
371 LEAVE_PARAMS_MUTEX_STATIC
372 }
373 return 0;
374 }
375 case effGetParameterProperties:
376 {
377 if (idx >= 0 && idx < _this->NParams())
378 {
379 VstParameterProperties* props = (VstParameterProperties*) ptr;
380
381 ENTER_PARAMS_MUTEX_STATIC
382 IParam* pParam = _this->GetParam(idx);
383 switch (pParam->Type())
384 {
385 case IParam::kTypeInt:
386 case IParam::kTypeEnum:
387 props->flags = kVstParameterUsesIntStep | kVstParameterUsesIntegerMinMax;
388 props->minInteger = (int) pParam->GetMin();
389 props->maxInteger = (int) pParam->GetMax();
390 props->stepInteger = props->largeStepInteger = 1;
391 break;
392 case IParam::kTypeBool:
393 props->flags = kVstParameterIsSwitch;
394 break;
395 case IParam::kTypeDouble:
396 default:
397 props->flags = kVstParameterUsesFloatStep;
398 props->largeStepFloat = props->smallStepFloat = props->stepFloat = (float) pParam->GetStep();
399 break;
400 }
401
402 strcpy(props->label, pParam->GetLabel());
403 LEAVE_PARAMS_MUTEX_STATIC
404
405 return 1;
406 }
407 return 0;
408 }
409 case effString2Parameter:
410 {
411 if (idx >= 0 && idx < _this->NParams())
412 {
413 if (ptr)
414 {
415 ENTER_PARAMS_MUTEX_STATIC
416 IParam* pParam = _this->GetParam(idx);
417 const double v = pParam->StringToValue((const char *)ptr);
418 pParam->Set(v);
419 _this->SendParameterValueFromAPI(idx, v, false);
420 _this->OnParamChange(idx, kHost);
421 LEAVE_PARAMS_MUTEX_STATIC
422 }
423 return 1;
424 }
425 return 0;
426 }
427 case effSetSampleRate:
428 {
429 _this->SetSampleRate(opt);
430 _this->OnReset();
431 return 0;
432 }
433 case effSetBlockSize:
434 {
435 _this->SetBlockSize((int) value);
436 _this->OnReset();
437 return 0;
438 }
439 case effMainsChanged:
440 {
441 if (!value)
442 {
443 _this->OnActivate(false);
444 _this->OnReset();
445 }
446 else
447 {
448 _this->OnActivate(true);
449 }
450 return 0;
451 }
452 case effEditGetRect:
453 {
454 if (ptr && _this->HasUI())
455 {
456 _this->UpdateEditRect();
457 *(ERect**) ptr = &(_this->mEditRect);
458 return 1;
459 }
460 ptr = 0;
461 return 0;
462 }
463 case effEditOpen:
464 {
465#if defined OS_WIN || defined ARCH_64BIT
466 if (_this->OpenWindow(ptr))
467 {
468 return 1;
469 }
470#else // OSX 32 bit, check if we are in a Cocoa VST host, otherwise tough luck
471 bool iscocoa = (_this->mHasVSTExtensions&VSTEXT_COCOA);
472 if (iscocoa && _this->OpenWindow(ptr))
473 {
474 return 1; // cocoa supported open cocoa
475 }
476#endif
477 return 0;
478 }
479 case effEditClose:
480 {
481 if (_this->HasUI())
482 {
483 _this->CloseWindow();
484 return 1;
485 }
486 return 0;
487 }
488 case __effIdentifyDeprecated:
489 {
490 return 'NvEf'; // Random deprecated magic.
491 }
492 case effGetChunk:
493 {
494 uint8_t** ppData = (uint8_t**) ptr;
495 if (ppData)
496 {
497 bool isBank = (!idx);
498 IByteChunk& chunk = (isBank ? _this->mBankState : _this->mState);
500 bool savedOK = true;
501
502 if (isBank)
503 {
504 _this->ModifyCurrentPreset();
505 savedOK = static_cast<IPluginBase*>(_this)->SerializePresets(chunk);
506 }
507 else
508 {
509 savedOK = _this->SerializeState(chunk);
510 }
511
512 if (savedOK && chunk.Size())
513 {
514 *ppData = chunk.GetData();
515 return chunk.Size();
516 }
517 }
518 return 0;
519 }
520 case effSetChunk:
521 {
522 if (ptr)
523 {
524 bool isBank = (!idx);
525 IByteChunk& chunk = (isBank ? _this->mBankState : _this->mState);
526 chunk.Resize((int) value);
527 memcpy(chunk.GetData(), ptr, value);
528 int pos = 0;
529 int iplugVer = IByteChunk::GetIPlugVerFromChunk(chunk, pos);
530 isBank &= (iplugVer >= 0x010000);
531
532 if (isBank)
533 {
534 pos = static_cast<IPluginBase*>(_this)->UnserializePresets(chunk, pos);
535 }
536 else
537 {
538 pos = _this->UnserializeState(chunk, pos);
539 _this->ModifyCurrentPreset();
540 }
541
542 if (pos >= 0)
543 {
544 _this->OnRestoreState();
545 return 1;
546 }
547 }
548 return 0;
549 }
550 case effProcessEvents:
551 {
552 VstEvents* pEvents = (VstEvents*) ptr;
553 if (pEvents)
554 {
555 for (int i = 0; i < pEvents->numEvents; ++i)
556 {
557 VstEvent* pEvent = pEvents->events[i];
558 if (pEvent)
559 {
560 if (pEvent->type == kVstMidiType)
561 {
562 VstMidiEvent* pME = (VstMidiEvent*) pEvent;
563 IMidiMsg msg(pME->deltaFrames, pME->midiData[0], pME->midiData[1], pME->midiData[2]);
564 _this->ProcessMidiMsg(msg);
565 _this->mMidiMsgsFromProcessor.Push(msg);
566
567 //#ifdef TRACER_BUILD
568 // msg.LogMsg();
569 //#endif
570 }
571 else if (pEvent->type == kVstSysExType)
572 {
573 VstMidiSysexEvent* pSE = (VstMidiSysexEvent*) pEvent;
574 ISysEx sysex(pSE->deltaFrames, (const uint8_t*)pSE->sysexDump, pSE->dumpBytes);
575 _this->ProcessSysEx(sysex);
576 }
577 }
578 }
579 return 1;
580 }
581 return 0;
582 }
583 case effCanBeAutomated:
584 {
585 if (idx >= 0 && idx < _this->NParams())
586 {
587 return _this->GetParam(idx)->GetCanAutomate();
588 }
589 }
590 case effGetInputProperties:
591 {
592 if (ptr && idx >= 0 && idx < _this->MaxNChannels(ERoute::kInput))
593 {
594 VstPinProperties* pp = (VstPinProperties*) ptr;
595 pp->flags = kVstPinIsActive;
596
597 if (!(idx%2) && idx < _this->MaxNChannels(ERoute::kInput)-1)
598 pp->flags |= kVstPinIsStereo;
599
600 if (_this->GetChannelLabel(ERoute::kInput, idx).GetLength())
601 sprintf(pp->label, "%s", _this->GetChannelLabel(ERoute::kInput, idx).Get());
602 else
603 sprintf(pp->label, "Input %d", idx + 1);
604
605 return 1;
606 }
607 return 0;
608 }
609 case effGetOutputProperties:
610 {
611 if (ptr && idx >= 0 && idx < _this->MaxNChannels(ERoute::kOutput))
612 {
613 VstPinProperties* pp = (VstPinProperties*) ptr;
614 pp->flags = kVstPinIsActive;
615
616 if (!(idx%2) && idx < _this->MaxNChannels(ERoute::kOutput)-1)
617 pp->flags |= kVstPinIsStereo;
618
619 if (_this->GetChannelLabel(ERoute::kOutput, idx).GetLength())
620 sprintf(pp->label, "%s", _this->GetChannelLabel(ERoute::kOutput, idx).Get());
621 else
622 sprintf(pp->label, "Output %d", idx + 1);
623
624 return 1;
625 }
626 return 0;
627 }
628 case effGetPlugCategory:
629 {
630 if (_this->IsInstrument()) return kPlugCategSynth;
631 return kPlugCategEffect;
632 }
633 case effProcessVarIo:
634 {
635 // VstVariableIo* pIO = (VstVariableIo*) ptr; // For offline processing (of audio files?)
636 return 0;
637 }
638 case effSetSpeakerArrangement:
639 {
640 VstSpeakerArrangement* pInputArr = (VstSpeakerArrangement*) value;
641 VstSpeakerArrangement* pOutputArr = (VstSpeakerArrangement*) ptr;
642
643 if (pInputArr)
644 {
645 int n = pInputArr->numChannels;
646
647 // For a mono-in plug-in in Reaper reject effSetSpeakerArrangement passed due to wantsChannelCountNotifications
648 if (n > _this->mAEffect.numInputs)
649 return 0;
650
651 _this->SetChannelConnections(ERoute::kInput, 0, n, true);
652 _this->SetChannelConnections(ERoute::kInput, n, _this->MaxNChannels(ERoute::kInput) - n, false);
653 }
654 if (pOutputArr)
655 {
656 int n = pOutputArr->numChannels;
657
658 // For a mono-out plug-in in Reaper reject effSetSpeakerArrangement passed due to wantsChannelCountNotifications
659 if (n > _this->mAEffect.numOutputs)
660 return 0;
661
662 _this->SetChannelConnections(ERoute::kOutput, 0, n, true);
663 _this->SetChannelConnections(ERoute::kOutput, n, _this->MaxNChannels(ERoute::kOutput) - n, false);
664 }
665 return 1;
666 }
667 case effGetSpeakerArrangement:
668 {
669 VstSpeakerArrangement** ppInputArr = (VstSpeakerArrangement**) value;
670 VstSpeakerArrangement** ppOutputArr = (VstSpeakerArrangement**) ptr;
671 if (ppInputArr)
672 {
673 *ppInputArr = &(_this->mInputSpkrArr);
674 }
675 if (ppOutputArr)
676 {
677 *ppOutputArr = &(_this->mOutputSpkrArr);
678 }
679 return 1;
680 }
681 case effGetEffectName:
682 {
683 if (ptr)
684 {
685 strcpy((char*) ptr, _this->GetPluginName());
686 return 1;
687 }
688 return 0;
689 }
690 case effGetProductString:
691 {
692 if (ptr)
693 {
694 strcpy((char*) ptr, _this->GetProductName());
695 return 1;
696 }
697 return 0;
698 }
699 case effGetVendorString:
700 {
701 if (ptr)
702 {
703 strcpy((char*) ptr, _this->GetMfrName());
704 return 1;
705 }
706 return 0;
707 }
708 case effGetVendorVersion:
709 {
710 return _this->GetPluginVersion(true);
711 }
712 case effCanDo:
713 {
714 if (ptr)
715 {
716 Trace(TRACELOC, "VSTCanDo(%s)", (char*) ptr);
717 if (!strcmp((char*) ptr, "receiveVstTimeInfo"))
718 {
719 return 1;
720 }
721 if (_this->DoesMIDIIn())
722 {
723 if (!strcmp((char*) ptr, "receiveVstEvents") ||
724 !strcmp((char*) ptr, "receiveVstMidiEvent"))
725 {
726 return 1;
727 }
728 }
729 if (_this->DoesMIDIOut())
730 {
731 if (!strcmp((char*) ptr, "sendVstEvents") ||
732 !strcmp((char*) ptr, "sendVstMidiEvent"))
733 {
734 return 1;
735 }
736 }
737 // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/
738 if (!strcmp((char*) ptr, "hasCockosExtensions"))
739 {
740 _this->mHasVSTExtensions |= VSTEXT_COCKOS;
741 return 0xbeef0000;
742 }
743
744 if (!strcmp((char*) ptr, "hasCockosViewAsConfig"))
745 {
746 _this->mHasVSTExtensions |= VSTEXT_COCOA;
747 return 0xbeef0000;
748 }
749
750 if (!strcmp((char*) ptr, "wantsChannelCountNotifications"))
751 {
752 return 1;
753 }
754
755 if (!strcmp((char*)ptr, "MPE"))
756 {
757 return _this->DoesMPE() ? 1 : 0;
758 }
759
760 return _this->VSTCanDo((char *) ptr);
761 }
762 return 0;
763 }
764 case effGetTailSize:
765 {
766 int tailSize = _this->GetTailSize();
767 return _this->GetTailIsInfinite() ? std::numeric_limits<int>::max() : (tailSize ? std::min(2, tailSize) : 1);
768 }
769 case effVendorSpecific:
770 {
771 switch (idx)
772 {
773 // Mouse wheel
774// case 0x73744341:
775// {
776// if (value == 0x57686565)
777// {
778// IGraphics* pGraphics = _this->GetUI();
779// if (pGraphics) {
780// return pGraphics->ProcessMouseWheel(opt);
781// }
782// }
783// break;
784// }
785 // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/
786 case effGetParamDisplay:
787 {
788 if (ptr)
789 {
790 if (value >= 0 && value < _this->NParams())
791 {
792 _this->GetParam((int) value)->GetDisplay((double) opt, true, _this->mParamDisplayStr);
793 strcpy((char*) ptr, _this->mParamDisplayStr.Get());
794 }
795 return 0xbeef;
796 }
797 break;
798 }
799 case effString2Parameter:
800 {
801 if (ptr && value >= 0 && value < _this->NParams())
802 {
803 if (*(char*) ptr != '\0')
804 {
805 IParam* pParam = _this->GetParam((int) value);
806 sprintf((char*) ptr, "%.17f", pParam->ToNormalized(pParam->StringToValue((const char*) ptr)));
807 }
808 return 0xbeef;
809 }
810 break;
811 }
812 case kVstParameterUsesIntStep:
813 {
814 if (value >= 0 && value < _this->NParams())
815 {
816 IParam* pParam = _this->GetParam((int) value);
817 switch (pParam->Type())
818 {
819 case IParam::kTypeBool:
820 return 0xbeef;
821 case IParam::kTypeInt:
822 case IParam::kTypeEnum:
823 {
824 double min, max;
825 pParam->GetBounds(min, max);
826 if (std::fabs(max - min) < 1.5)
827 return 0xbeef;
828
829 break;
830 }
831 default:
832 break;
833 }
834 }
835 break;
836 }
837 }
838 return _this->VSTVendorSpecific(idx, value, ptr, opt);
839 }
840 case effGetProgram:
841 {
842 return _this->GetCurrentPresetIdx();
843 }
844 case effSetProgram:
845 {
846 if (_this->DoesStateChunks() == false)
847 {
848 _this->ModifyCurrentPreset(); // TODO: test, something is funny about this http://forum.cockos.com/showpost.php?p=485113&postcount=22
849 }
850 _this->RestorePreset((int) value);
851 return 0;
852 }
853 case effGetProgramNameIndexed:
854 {
855 strcpy((char*) ptr, _this->GetPresetName(idx));
856 return (CStringHasContents((char*) ptr) ? 1 : 0);
857 }
858 case effSetProgramName:
859 {
860 if (ptr)
861 {
862 _this->ModifyCurrentPreset((char*) ptr);
863 _this->OnPresetsModified();
864 }
865 return 0;
866 }
867 case effGetProgramName:
868 {
869 if (ptr)
870 {
871 int idx = _this->GetCurrentPresetIdx();
872 strcpy((char*) ptr, _this->GetPresetName(idx));
873 }
874 return 0;
875 }
876 case effGetMidiKeyName:
877 {
878 if (ptr)
879 {
880 MidiKeyName* pMKN = (MidiKeyName*) ptr;
881 pMKN->keyName[0] = '\0';
882 if (_this->GetMidiNoteText(pMKN->thisKeyNumber, pMKN->keyName))
883 {
884 return 1;
885 }
886 }
887 return 0;
888 }
889 case effGetVstVersion:
890 {
891 return VST_VERSION;
892 }
893 case effEditKeyDown:
894 case effEditKeyUp:
895 {
896 char str[2];
897 str[0] = static_cast<char>(idx);
898 str[1] = '\0';
899
900 // Workaround for Reaper's funky behaviour
901 if (_this->GetHost() == iplug::EHost::kHostReaper && value != VKEY_SPACE)
902 {
903 return 0;
904 }
905
906 int vk = VSTKeyCodeToVK(static_cast<int>(value), idx);
907 int modifiers = (int)opt;
908
909 IKeyPress keyPress{ str, static_cast<int>(vk),
910 static_cast<bool>(modifiers & MODIFIER_SHIFT),
911 static_cast<bool>(modifiers & MODIFIER_CONTROL),
912 static_cast<bool>(modifiers & MODIFIER_ALTERNATE) };
913
914 bool handled;
915 if (opCode == effEditKeyDown)
916 handled = _this->OnKeyDown(keyPress);
917 else
918 handled = _this->OnKeyUp(keyPress);
919
920 return handled ? 1 : 0;
921 }
922 case effEndSetProgram:
923 case effBeginSetProgram:
924 case effGetMidiProgramName:
925 case effHasMidiProgramsChanged:
926 case effGetMidiProgramCategory:
927 case effGetCurrentMidiProgram:
928 case effSetBypass:
929 default:
930 {
931 return 0;
932 }
933 }
934}
935
936template <class SAMPLETYPE>
937void IPlugVST2::VSTPreProcess(SAMPLETYPE** inputs, SAMPLETYPE** outputs, VstInt32 nFrames)
938{
939 if (DoesMIDIIn())
940 mHostCallback(&mAEffect, __audioMasterWantMidiDeprecated, 0, 0, 0, 0.0f);
941
942 AttachBuffers(ERoute::kInput, 0, MaxNChannels(ERoute::kInput), inputs, nFrames);
943 AttachBuffers(ERoute::kOutput, 0, MaxNChannels(ERoute::kOutput), outputs, nFrames);
944
945 VstTimeInfo* pTI = (VstTimeInfo*) mHostCallback(&mAEffect, audioMasterGetTime, 0, kVstPpqPosValid | kVstTempoValid | kVstBarsValid | kVstCyclePosValid | kVstTimeSigValid, 0, 0);
946
947 ITimeInfo timeInfo;
948
949 if (pTI)
950 {
951 timeInfo.mSamplePos = pTI->samplePos;
952
953 if ((pTI->flags & kVstPpqPosValid) && pTI->ppqPos >= 0.0) timeInfo.mPPQPos = pTI->ppqPos;
954 if ((pTI->flags & kVstTempoValid) && pTI->tempo > 0.0) timeInfo.mTempo = pTI->tempo;
955 if ((pTI->flags & kVstBarsValid) && pTI->barStartPos >= 0.0) timeInfo.mLastBar = pTI->barStartPos;
956 if ((pTI->flags & kVstCyclePosValid) && pTI->cycleStartPos >= 0.0 && pTI->cycleEndPos >= 0.0)
957 {
958 timeInfo.mCycleStart = pTI->cycleStartPos;
959 timeInfo.mCycleEnd = pTI->cycleEndPos;
960 }
961 if ((pTI->flags & kVstTimeSigValid) && pTI->timeSigNumerator > 0.0 && pTI->timeSigDenominator > 0.0)
962 {
963 timeInfo.mNumerator = pTI->timeSigNumerator;
964 timeInfo.mDenominator = pTI->timeSigDenominator;
965 }
966 timeInfo.mTransportIsRunning = pTI->flags & kVstTransportPlaying;
967 timeInfo.mTransportLoopEnabled = pTI->flags & kVstTransportCycleActive;
968 }
969
970 const bool renderingOffline = mHostCallback(&mAEffect, audioMasterGetCurrentProcessLevel, 0, 0, 0, 0.0f) == kVstProcessLevelOffline;
971
972 SetTimeInfo(timeInfo);
973 SetRenderingOffline(renderingOffline);
974
975 IMidiMsg msg;
976
977 while (mMidiMsgsFromEditor.Pop(msg))
978 {
979 ProcessMidiMsg(msg);
980 }
981}
982
983// Deprecated.
984void VSTCALLBACK IPlugVST2::VSTProcess(AEffect* pEffect, float** inputs, float** outputs, VstInt32 nFrames)
985{
986 TRACE
987 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
988 _this->VSTPreProcess(inputs, outputs, nFrames);
989 ENTER_PARAMS_MUTEX_STATIC
990 _this->ProcessBuffersAccumulating(nFrames);
991 LEAVE_PARAMS_MUTEX_STATIC
992 _this->OutputSysexFromEditor();
993}
994
995void VSTCALLBACK IPlugVST2::VSTProcessReplacing(AEffect* pEffect, float** inputs, float** outputs, VstInt32 nFrames)
996{
997 TRACE
998 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
999 _this->VSTPreProcess(inputs, outputs, nFrames);
1000 ENTER_PARAMS_MUTEX_STATIC
1001 _this->ProcessBuffers((float) 0.0f, nFrames);
1002 LEAVE_PARAMS_MUTEX_STATIC
1003 _this->OutputSysexFromEditor();
1004}
1005
1006void VSTCALLBACK IPlugVST2::VSTProcessDoubleReplacing(AEffect* pEffect, double** inputs, double** outputs, VstInt32 nFrames)
1007{
1008 TRACE
1009 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
1010 _this->VSTPreProcess(inputs, outputs, nFrames);
1011 ENTER_PARAMS_MUTEX_STATIC
1012 _this->ProcessBuffers((double) 0.0, nFrames);
1013 LEAVE_PARAMS_MUTEX_STATIC
1014 _this->OutputSysexFromEditor();
1015}
1016
1017float VSTCALLBACK IPlugVST2::VSTGetParameter(AEffect *pEffect, VstInt32 idx)
1018{
1019 Trace(TRACELOC, "%d", idx);
1020 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
1021 if (idx >= 0 && idx < _this->NParams())
1022 {
1023 ENTER_PARAMS_MUTEX_STATIC
1024 const float val = (float) _this->GetParam(idx)->GetNormalized();
1025 LEAVE_PARAMS_MUTEX_STATIC
1026
1027 return val;
1028 }
1029 return 0.0f;
1030}
1031
1032void VSTCALLBACK IPlugVST2::VSTSetParameter(AEffect *pEffect, VstInt32 idx, float value)
1033{
1034 Trace(TRACELOC, "%d:%f", idx, value);
1035 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
1036 if (idx >= 0 && idx < _this->NParams())
1037 {
1038 ENTER_PARAMS_MUTEX_STATIC
1039 _this->GetParam(idx)->SetNormalized(value);
1040 _this->SendParameterValueFromAPI(idx, value, true);
1041 _this->OnParamChange(idx, kHost);
1042 LEAVE_PARAMS_MUTEX_STATIC
1043 }
1044}
1045
1046void IPlugVST2::OutputSysexFromEditor()
1047{
1048 //Output SYSEX from the editor, which has bypassed ProcessSysEx()
1049 if(mSysExDataFromEditor.ElementsAvailable())
1050 {
1051 while (mSysExDataFromEditor.Pop(mSysexBuf))
1052 {
1053 ISysEx smsg {mSysexBuf.mOffset, mSysexBuf.mData, mSysexBuf.mSize};
1054 SendSysEx(smsg);
1055 }
1056 }
1057}
Manages a block of memory, for plug-in settings store/recall.
Definition: IPlugStructs.h:112
static int GetIPlugVerFromChunk(const IByteChunk &chunk, int &position)
Helper method to retrieve the IPlug version number from the beginning of the byte chunk.
Definition: IPlugStructs.h:132
uint8_t * GetData()
Gets a ptr to the chunk data.
Definition: IPlugStructs.h:242
static void InitChunkWithIPlugVer(IByteChunk &chunk)
This method is used in order to place the IPlug version number in the chunk when serialising data.
Definition: IPlugStructs.h:119
int Size() const
Returns the current size of the chunk.
Definition: IPlugStructs.h:221
int Resize(int newSize)
Resizes the chunk.
Definition: IPlugStructs.h:229
IPlug's parameter class.
EParamType Type() const
Get the parameter's type.
double ToNormalized(double nonNormalizedValue) const
Convert a real value to normalized value for this parameter.
void GetBounds(double &lo, double &hi) const
Get the minimum and maximum real value of the parameter's range in one method call.
double StringToValue(const char *str) const
Convert a textual representation of the parameter value to a double (real value)
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
Definition: IPlugAPIBase.h:43
virtual bool GetMidiNoteText(int noteNumber, char *str) const
Override this method to provide custom text linked to MIDI note numbers in API classes that support t...
Definition: IPlugAPIBase.h:96
virtual void SendParameterValueFromAPI(int paramIdx, double value, bool normalized)
This is called from the plug-in API class in order to update UI controls linked to plug-in parameters...
void SetHost(const char *host, int version)
Called to set the name of the current host, if known (calls on to HostSpecificInit() and OnHostIdenti...
The base class for IPlug Audio Processing.
bool GetTailIsInfinite() const
int GetTailSize() const
virtual void ProcessMidiMsg(const IMidiMsg &msg)
Override this method to handle incoming MIDI messages.
virtual void SetLatency(int latency)
Call this if the latency of your plug-in changes after initialization (perhaps from OnReset() ) This ...
bool IsInstrument() const
bool DoesMPE() const
bool DoesMIDIIn() const
virtual void ProcessSysEx(const ISysEx &msg)
Override this method to handle incoming MIDI System Exclusive (SysEx) messages.
int MaxNChannels(ERoute direction) const
void LimitToStereoIO()
This is called by IPlugVST in order to limit a plug-in to stereo I/O for certain picky hosts.
virtual void OnActivate(bool active)
Override OnActivate() which should be called by the API class when a plug-in is "switched on" by the ...
bool DoesMIDIOut() const
virtual void OnReset()
Override this method in your plug-in class to do something prior to playback etc.
VST2.4 API base class for an IPlug plug-in.
Definition: IPlugVST2.h:36
void InformHostOfParamChange(int idx, double normalizedValue) override
Implemented by the API class, called by the UI via SetParameterValue() with the value of a parameter ...
Definition: IPlugVST2.cpp:184
bool SendMidiMsg(const IMidiMsg &msg) override
Send a single MIDI message // TODO: info about what thread should this be called on or not called on!
Definition: IPlugVST2.cpp:243
void EndInformHostOfParamChange(int idx) override
Implemented by the API class, called by the UI (or by a delegate) at the end of a parameter change ge...
Definition: IPlugVST2.cpp:189
void SetLatency(int samples) override
Call this if the latency of your plug-in changes after initialization (perhaps from OnReset() ) This ...
Definition: IPlugVST2.cpp:224
bool EditorResize(int viewWidth, int viewHeight) override
Implementations call into the APIs resize hooks returns a bool to indicate whether the DAW or plugin ...
Definition: IPlugVST2.cpp:199
void HostSpecificInit() override
This method is implemented in some API classes, in order to do specific initialisation for particular...
Definition: IPlugVST2.cpp:272
bool SendSysEx(const ISysEx &msg) override
Send a single MIDI System Exclusive (SysEx) message // TODO: info about what thread should this be ca...
Definition: IPlugVST2.cpp:258
void BeginInformHostOfParamChange(int idx) override
Implemented by the API class, called by the UI (or by a delegate) at the beginning of a parameter cha...
Definition: IPlugVST2.cpp:179
void InformHostOfPresetChange() override
Implemented by the API class, called by the UI (etc) when the plug-in initiates a program/preset chan...
Definition: IPlugVST2.cpp:194
Base class that contains plug-in info and state manipulation methods.
virtual int UnserializeState(const IByteChunk &chunk, int startPos)
Override this method to unserialize custom state data, if your plugin does state chunks.
const char * GetPluginName() const
int GetPluginVersion(bool decimal) const
Get the plug-in version number.
bool HasUI() const
int UnserializePresets(const IByteChunk &chunk, int startPos)
[VST2 only] Called when the VST2 host calls effSetChunk for a bank *
virtual bool SerializeState(IByteChunk &chunk) const
Override this method to serialize custom state data, if your plugin does state chunks.
bool DoesStateChunks() const
void ModifyCurrentPreset(const char *name=0)
This method should update the current preset with current values NOTE: This is only relevant for VST2...
int GetCurrentPresetIdx() const
Get the index of the current, active preset.
const char * GetMfrName() const
Get the manufacturer name as a CString.
bool RestorePreset(int idx)
Restore a preset by index.
bool SerializePresets(IByteChunk &chunk) const
[VST2 only] Called when the VST2 host calls effGetChunk for a bank *
const char * GetPresetName(int idx) const
Get the name a preset.
EHost GetHost() const
virtual void OnPresetsModified()
[VST2 only] Called when the preset name is changed by the host
const char * GetProductName() const
Get the product name as a CString.
Encapsulates information about the host transport state.
Definition: IPlugStructs.h:585
Used for key press info, such as ASCII representation, virtual key (mapped to win32 codes) and modifi...
Definition: IPlugStructs.h:616
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:31
A struct for dealing with SysEx messages.
Definition: IPlugMidi.h:539