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 return _this->GetTailSize();
767 }
768 case effVendorSpecific:
769 {
770 switch (idx)
771 {
772 // Mouse wheel
773// case 0x73744341:
774// {
775// if (value == 0x57686565)
776// {
777// IGraphics* pGraphics = _this->GetUI();
778// if (pGraphics) {
779// return pGraphics->ProcessMouseWheel(opt);
780// }
781// }
782// break;
783// }
784 // Support Reaper VST extensions: http://www.reaper.fm/sdk/vst/
785 case effGetParamDisplay:
786 {
787 if (ptr)
788 {
789 if (value >= 0 && value < _this->NParams())
790 {
791 _this->GetParam((int) value)->GetDisplay((double) opt, true, _this->mParamDisplayStr);
792 strcpy((char*) ptr, _this->mParamDisplayStr.Get());
793 }
794 return 0xbeef;
795 }
796 break;
797 }
798 case effString2Parameter:
799 {
800 if (ptr && value >= 0 && value < _this->NParams())
801 {
802 if (*(char*) ptr != '\0')
803 {
804 IParam* pParam = _this->GetParam((int) value);
805 sprintf((char*) ptr, "%.17f", pParam->ToNormalized(pParam->StringToValue((const char*) ptr)));
806 }
807 return 0xbeef;
808 }
809 break;
810 }
811 case kVstParameterUsesIntStep:
812 {
813 if (value >= 0 && value < _this->NParams())
814 {
815 IParam* pParam = _this->GetParam((int) value);
816 switch (pParam->Type())
817 {
818 case IParam::kTypeBool:
819 return 0xbeef;
820 case IParam::kTypeInt:
821 case IParam::kTypeEnum:
822 {
823 double min, max;
824 pParam->GetBounds(min, max);
825 if (std::fabs(max - min) < 1.5)
826 return 0xbeef;
827
828 break;
829 }
830 default:
831 break;
832 }
833 }
834 break;
835 }
836 }
837 return _this->VSTVendorSpecific(idx, value, ptr, opt);
838 }
839 case effGetProgram:
840 {
841 return _this->GetCurrentPresetIdx();
842 }
843 case effSetProgram:
844 {
845 if (_this->DoesStateChunks() == false)
846 {
847 _this->ModifyCurrentPreset(); // TODO: test, something is funny about this http://forum.cockos.com/showpost.php?p=485113&postcount=22
848 }
849 _this->RestorePreset((int) value);
850 return 0;
851 }
852 case effGetProgramNameIndexed:
853 {
854 strcpy((char*) ptr, _this->GetPresetName(idx));
855 return (CStringHasContents((char*) ptr) ? 1 : 0);
856 }
857 case effSetProgramName:
858 {
859 if (ptr)
860 {
861 _this->ModifyCurrentPreset((char*) ptr);
862 _this->OnPresetsModified();
863 }
864 return 0;
865 }
866 case effGetProgramName:
867 {
868 if (ptr)
869 {
870 int idx = _this->GetCurrentPresetIdx();
871 strcpy((char*) ptr, _this->GetPresetName(idx));
872 }
873 return 0;
874 }
875 case effGetMidiKeyName:
876 {
877 if (ptr)
878 {
879 MidiKeyName* pMKN = (MidiKeyName*) ptr;
880 pMKN->keyName[0] = '\0';
881 if (_this->GetMidiNoteText(pMKN->thisKeyNumber, pMKN->keyName))
882 {
883 return 1;
884 }
885 }
886 return 0;
887 }
888 case effGetVstVersion:
889 {
890 return VST_VERSION;
891 }
892 case effEditKeyDown:
893 case effEditKeyUp:
894 {
895 char str[2];
896 str[0] = static_cast<char>(idx);
897 str[1] = '\0';
898
899 // Workaround for Reaper's funky behaviour
900 if (_this->GetHost() == iplug::EHost::kHostReaper && value != VKEY_SPACE)
901 {
902 return 0;
903 }
904
905 int vk = VSTKeyCodeToVK(static_cast<int>(value), idx);
906 int modifiers = (int)opt;
907
908 IKeyPress keyPress{ str, static_cast<int>(vk),
909 static_cast<bool>(modifiers & MODIFIER_SHIFT),
910 static_cast<bool>(modifiers & MODIFIER_CONTROL),
911 static_cast<bool>(modifiers & MODIFIER_ALTERNATE) };
912
913 bool handled;
914 if (opCode == effEditKeyDown)
915 handled = _this->OnKeyDown(keyPress);
916 else
917 handled = _this->OnKeyUp(keyPress);
918
919 return handled ? 1 : 0;
920 }
921 case effEndSetProgram:
922 case effBeginSetProgram:
923 case effGetMidiProgramName:
924 case effHasMidiProgramsChanged:
925 case effGetMidiProgramCategory:
926 case effGetCurrentMidiProgram:
927 case effSetBypass:
928 default:
929 {
930 return 0;
931 }
932 }
933}
934
935template <class SAMPLETYPE>
936void IPlugVST2::VSTPreProcess(SAMPLETYPE** inputs, SAMPLETYPE** outputs, VstInt32 nFrames)
937{
938 if (DoesMIDIIn())
939 mHostCallback(&mAEffect, __audioMasterWantMidiDeprecated, 0, 0, 0, 0.0f);
940
941 AttachBuffers(ERoute::kInput, 0, MaxNChannels(ERoute::kInput), inputs, nFrames);
942 AttachBuffers(ERoute::kOutput, 0, MaxNChannels(ERoute::kOutput), outputs, nFrames);
943
944 VstTimeInfo* pTI = (VstTimeInfo*) mHostCallback(&mAEffect, audioMasterGetTime, 0, kVstPpqPosValid | kVstTempoValid | kVstBarsValid | kVstCyclePosValid | kVstTimeSigValid, 0, 0);
945
946 ITimeInfo timeInfo;
947
948 if (pTI)
949 {
950 timeInfo.mSamplePos = pTI->samplePos;
951
952 if ((pTI->flags & kVstPpqPosValid) && pTI->ppqPos >= 0.0) timeInfo.mPPQPos = pTI->ppqPos;
953 if ((pTI->flags & kVstTempoValid) && pTI->tempo > 0.0) timeInfo.mTempo = pTI->tempo;
954 if ((pTI->flags & kVstBarsValid) && pTI->barStartPos >= 0.0) timeInfo.mLastBar = pTI->barStartPos;
955 if ((pTI->flags & kVstCyclePosValid) && pTI->cycleStartPos >= 0.0 && pTI->cycleEndPos >= 0.0)
956 {
957 timeInfo.mCycleStart = pTI->cycleStartPos;
958 timeInfo.mCycleEnd = pTI->cycleEndPos;
959 }
960 if ((pTI->flags & kVstTimeSigValid) && pTI->timeSigNumerator > 0.0 && pTI->timeSigDenominator > 0.0)
961 {
962 timeInfo.mNumerator = pTI->timeSigNumerator;
963 timeInfo.mDenominator = pTI->timeSigDenominator;
964 }
965 timeInfo.mTransportIsRunning = pTI->flags & kVstTransportPlaying;
966 timeInfo.mTransportLoopEnabled = pTI->flags & kVstTransportCycleActive;
967 }
968
969 const bool renderingOffline = mHostCallback(&mAEffect, audioMasterGetCurrentProcessLevel, 0, 0, 0, 0.0f) == kVstProcessLevelOffline;
970
971 SetTimeInfo(timeInfo);
972 SetRenderingOffline(renderingOffline);
973
974 IMidiMsg msg;
975
976 while (mMidiMsgsFromEditor.Pop(msg))
977 {
978 ProcessMidiMsg(msg);
979 }
980}
981
982// Deprecated.
983void VSTCALLBACK IPlugVST2::VSTProcess(AEffect* pEffect, float** inputs, float** outputs, VstInt32 nFrames)
984{
985 TRACE
986 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
987 _this->VSTPreProcess(inputs, outputs, nFrames);
988 ENTER_PARAMS_MUTEX_STATIC
989 _this->ProcessBuffersAccumulating(nFrames);
990 LEAVE_PARAMS_MUTEX_STATIC
991 _this->OutputSysexFromEditor();
992}
993
994void VSTCALLBACK IPlugVST2::VSTProcessReplacing(AEffect* pEffect, float** inputs, float** outputs, VstInt32 nFrames)
995{
996 TRACE
997 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
998 _this->VSTPreProcess(inputs, outputs, nFrames);
999 ENTER_PARAMS_MUTEX_STATIC
1000 _this->ProcessBuffers((float) 0.0f, nFrames);
1001 LEAVE_PARAMS_MUTEX_STATIC
1002 _this->OutputSysexFromEditor();
1003}
1004
1005void VSTCALLBACK IPlugVST2::VSTProcessDoubleReplacing(AEffect* pEffect, double** inputs, double** outputs, VstInt32 nFrames)
1006{
1007 TRACE
1008 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
1009 _this->VSTPreProcess(inputs, outputs, nFrames);
1010 ENTER_PARAMS_MUTEX_STATIC
1011 _this->ProcessBuffers((double) 0.0, nFrames);
1012 LEAVE_PARAMS_MUTEX_STATIC
1013 _this->OutputSysexFromEditor();
1014}
1015
1016float VSTCALLBACK IPlugVST2::VSTGetParameter(AEffect *pEffect, VstInt32 idx)
1017{
1018 Trace(TRACELOC, "%d", idx);
1019 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
1020 if (idx >= 0 && idx < _this->NParams())
1021 {
1022 ENTER_PARAMS_MUTEX_STATIC
1023 const float val = (float) _this->GetParam(idx)->GetNormalized();
1024 LEAVE_PARAMS_MUTEX_STATIC
1025
1026 return val;
1027 }
1028 return 0.0f;
1029}
1030
1031void VSTCALLBACK IPlugVST2::VSTSetParameter(AEffect *pEffect, VstInt32 idx, float value)
1032{
1033 Trace(TRACELOC, "%d:%f", idx, value);
1034 IPlugVST2* _this = (IPlugVST2*) pEffect->object;
1035 if (idx >= 0 && idx < _this->NParams())
1036 {
1037 ENTER_PARAMS_MUTEX_STATIC
1038 _this->GetParam(idx)->SetNormalized(value);
1039 _this->SendParameterValueFromAPI(idx, value, true);
1040 _this->OnParamChange(idx, kHost);
1041 LEAVE_PARAMS_MUTEX_STATIC
1042 }
1043}
1044
1045void IPlugVST2::OutputSysexFromEditor()
1046{
1047 //Output SYSEX from the editor, which has bypassed ProcessSysEx()
1048 if(mSysExDataFromEditor.ElementsAvailable())
1049 {
1050 while (mSysExDataFromEditor.Pop(mSysexBuf))
1051 {
1052 ISysEx smsg {mSysexBuf.mOffset, mSysexBuf.mData, mSysexBuf.mSize};
1053 SendSysEx(smsg);
1054 }
1055 }
1056}
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.
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
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 ProcessSysEx(ISysEx &msg)
Override this method to handle incoming MIDI System Exclusive (SysEx) messages.
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