iPlug2 - C++ Audio Plug-in Framework
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 
15 using namespace iplug;
16 
17 static const int VST_VERSION = 2400;
18 
19 static 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 
27 static 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 
49 static 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 
122 IPlugVST2::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 
184 void 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 
199 bool 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 
217 void IPlugVST2::UpdateEditRect()
218 {
219  mEditRect.left = mEditRect.top = 0;
220  mEditRect.right = GetEditorWidth();
221  mEditRect.bottom = GetEditorHeight();
222 }
223 
224 void IPlugVST2::SetLatency(int samples)
225 {
226  mAEffect.initialDelay = samples;
228  mHostCallback(&mAEffect, audioMasterIOChanged, 0, 0, 0, 0.0f);
229 }
230 
231 bool 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 
258 bool IPlugVST2::SendSysEx(const ISysEx& msg)
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 
292 VstIntPtr 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  int vk = VSTKeyCodeToVK(value, idx);
900  int modifiers = (int)opt;
901 
902  IKeyPress keyPress{ str, static_cast<int>(vk),
903  static_cast<bool>(modifiers & MODIFIER_SHIFT),
904  static_cast<bool>(modifiers & MODIFIER_CONTROL),
905  static_cast<bool>(modifiers & MODIFIER_ALTERNATE) };
906 
907  bool handled;
908  if (opCode == effEditKeyDown)
909  handled = _this->OnKeyDown(keyPress);
910  else
911  handled = _this->OnKeyUp(keyPress);
912 
913  return handled ? 1 : 0;
914  }
915  case effEndSetProgram:
916  case effBeginSetProgram:
917  case effGetMidiProgramName:
918  case effHasMidiProgramsChanged:
919  case effGetMidiProgramCategory:
920  case effGetCurrentMidiProgram:
921  case effSetBypass:
922  default:
923  {
924  return 0;
925  }
926  }
927 }
928 
929 template <class SAMPLETYPE>
930 void IPlugVST2::VSTPreProcess(SAMPLETYPE** inputs, SAMPLETYPE** outputs, VstInt32 nFrames)
931 {
932  if (DoesMIDIIn())
933  mHostCallback(&mAEffect, __audioMasterWantMidiDeprecated, 0, 0, 0, 0.0f);
934 
935  AttachBuffers(ERoute::kInput, 0, MaxNChannels(ERoute::kInput), inputs, nFrames);
936  AttachBuffers(ERoute::kOutput, 0, MaxNChannels(ERoute::kOutput), outputs, nFrames);
937 
938  VstTimeInfo* pTI = (VstTimeInfo*) mHostCallback(&mAEffect, audioMasterGetTime, 0, kVstPpqPosValid | kVstTempoValid | kVstBarsValid | kVstCyclePosValid | kVstTimeSigValid, 0, 0);
939 
940  ITimeInfo timeInfo;
941 
942  if (pTI)
943  {
944  timeInfo.mSamplePos = pTI->samplePos;
945 
946  if ((pTI->flags & kVstPpqPosValid) && pTI->ppqPos >= 0.0) timeInfo.mPPQPos = pTI->ppqPos;
947  if ((pTI->flags & kVstTempoValid) && pTI->tempo > 0.0) timeInfo.mTempo = pTI->tempo;
948  if ((pTI->flags & kVstBarsValid) && pTI->barStartPos >= 0.0) timeInfo.mLastBar = pTI->barStartPos;
949  if ((pTI->flags & kVstCyclePosValid) && pTI->cycleStartPos >= 0.0 && pTI->cycleEndPos >= 0.0)
950  {
951  timeInfo.mCycleStart = pTI->cycleStartPos;
952  timeInfo.mCycleEnd = pTI->cycleEndPos;
953  }
954  if ((pTI->flags & kVstTimeSigValid) && pTI->timeSigNumerator > 0.0 && pTI->timeSigDenominator > 0.0)
955  {
956  timeInfo.mNumerator = pTI->timeSigNumerator;
957  timeInfo.mDenominator = pTI->timeSigDenominator;
958  }
959  timeInfo.mTransportIsRunning = pTI->flags & kVstTransportPlaying;
960  timeInfo.mTransportLoopEnabled = pTI->flags & kVstTransportCycleActive;
961  }
962 
963  const bool renderingOffline = mHostCallback(&mAEffect, audioMasterGetCurrentProcessLevel, 0, 0, 0, 0.0f) == kVstProcessLevelOffline;
964 
965  SetTimeInfo(timeInfo);
966  SetRenderingOffline(renderingOffline);
967 
968  IMidiMsg msg;
969 
970  while (mMidiMsgsFromEditor.Pop(msg))
971  {
972  ProcessMidiMsg(msg);
973  }
974 }
975 
976 // Deprecated.
977 void VSTCALLBACK IPlugVST2::VSTProcess(AEffect* pEffect, float** inputs, float** outputs, VstInt32 nFrames)
978 {
979  TRACE
980  IPlugVST2* _this = (IPlugVST2*) pEffect->object;
981  _this->VSTPreProcess(inputs, outputs, nFrames);
982  ENTER_PARAMS_MUTEX_STATIC
983  _this->ProcessBuffersAccumulating(nFrames);
984  LEAVE_PARAMS_MUTEX_STATIC
985  _this->OutputSysexFromEditor();
986 }
987 
988 void VSTCALLBACK IPlugVST2::VSTProcessReplacing(AEffect* pEffect, float** inputs, float** outputs, VstInt32 nFrames)
989 {
990  TRACE
991  IPlugVST2* _this = (IPlugVST2*) pEffect->object;
992  _this->VSTPreProcess(inputs, outputs, nFrames);
993  ENTER_PARAMS_MUTEX_STATIC
994  _this->ProcessBuffers((float) 0.0f, nFrames);
995  LEAVE_PARAMS_MUTEX_STATIC
996  _this->OutputSysexFromEditor();
997 }
998 
999 void VSTCALLBACK IPlugVST2::VSTProcessDoubleReplacing(AEffect* pEffect, double** inputs, double** outputs, VstInt32 nFrames)
1000 {
1001  TRACE
1002  IPlugVST2* _this = (IPlugVST2*) pEffect->object;
1003  _this->VSTPreProcess(inputs, outputs, nFrames);
1004  ENTER_PARAMS_MUTEX_STATIC
1005  _this->ProcessBuffers((double) 0.0, nFrames);
1006  LEAVE_PARAMS_MUTEX_STATIC
1007  _this->OutputSysexFromEditor();
1008 }
1009 
1010 float VSTCALLBACK IPlugVST2::VSTGetParameter(AEffect *pEffect, VstInt32 idx)
1011 {
1012  Trace(TRACELOC, "%d", idx);
1013  IPlugVST2* _this = (IPlugVST2*) pEffect->object;
1014  if (idx >= 0 && idx < _this->NParams())
1015  {
1016  ENTER_PARAMS_MUTEX_STATIC
1017  const float val = (float) _this->GetParam(idx)->GetNormalized();
1018  LEAVE_PARAMS_MUTEX_STATIC
1019 
1020  return val;
1021  }
1022  return 0.0f;
1023 }
1024 
1025 void VSTCALLBACK IPlugVST2::VSTSetParameter(AEffect *pEffect, VstInt32 idx, float value)
1026 {
1027  Trace(TRACELOC, "%d:%f", idx, value);
1028  IPlugVST2* _this = (IPlugVST2*) pEffect->object;
1029  if (idx >= 0 && idx < _this->NParams())
1030  {
1031  ENTER_PARAMS_MUTEX_STATIC
1032  _this->GetParam(idx)->SetNormalized(value);
1033  _this->SendParameterValueFromAPI(idx, value, true);
1034  _this->OnParamChange(idx, kHost);
1035  LEAVE_PARAMS_MUTEX_STATIC
1036  }
1037 }
1038 
1039 void IPlugVST2::OutputSysexFromEditor()
1040 {
1041  //Output SYSEX from the editor, which has bypassed ProcessSysEx()
1042  if(mSysExDataFromEditor.ElementsAvailable())
1043  {
1044  while (mSysExDataFromEditor.Pop(mSysexBuf))
1045  {
1046  ISysEx smsg {mSysexBuf.mOffset, mSysexBuf.mData, mSysexBuf.mSize};
1047  SendSysEx(smsg);
1048  }
1049  }
1050 }
int MaxNChannels(ERoute direction) const
const char * GetPresetName(int idx) const
Get the name a preset.
The base class of an IPlug plug-in, which interacts with the different plug-in APIs.
Definition: IPlugAPIBase.h:42
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
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
const char * GetProductName() const
Get the product name as a CString.
bool DoesMPE() const
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:30
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...
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
IPlug&#39;s parameter class.
virtual void SetLatency(int latency)
Call this if the latency of your plug-in changes after initialization (perhaps from OnReset() ) This ...
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:92
bool DoesStateChunks() const
virtual bool SerializeState(IByteChunk &chunk) const
Override this method to serialize custom state data, if your plugin does state chunks.
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
bool DoesMIDIOut() const
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
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 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
bool IsInstrument() const
Manages a block of memory, for plug-in settings store/recall.
Definition: IPlugStructs.h:111
int UnserializePresets(const IByteChunk &chunk, int startPos)
[VST2 only] Called when the VST2 host calls effSetChunk for a bank *
VST2.4 API base class for an IPlug plug-in.
Definition: IPlugVST2.h:34
double StringToValue(const char *str) const
Convert a textual representation of the parameter value to a double (real value)
const char * GetPluginName() const
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 * GetMfrName() const
Get the manufacturer name as a CString.
int Size() const
Returns the current size of the chunk.
Definition: IPlugStructs.h:221
virtual void ProcessSysEx(ISysEx &msg)
Override this method to handle incoming MIDI System Exclusive (SysEx) messages.
int Resize(int newSize)
Resizes the chunk.
Definition: IPlugStructs.h:229
int GetCurrentPresetIdx() const
Get the index of the current, active preset.
void ModifyCurrentPreset(const char *name=0)
This method should update the current preset with current values NOTE: This is only relevant for VST2...
EHost GetHost() const
bool DoesMIDIIn() const
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
virtual void ProcessMidiMsg(const IMidiMsg &msg)
Override this method to handle incoming MIDI messages.
The base class for IPlug Audio Processing.
bool RestorePreset(int idx)
Restore a preset by index.
void HostSpecificInit() override
This method is implemented in some API classes, in order to do specific initialisation for particular...
Definition: IPlugVST2.cpp:272
double ToNormalized(double nonNormalizedValue) const
Convert a real value to normalized value for this parameter.
EParamType Type() const
Get the parameter&#39;s type.
virtual void OnReset()
Override this method in your plug-in class to do something prior to playback etc. ...
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
uint8_t * GetData()
Gets a ptr to the chunk data.
Definition: IPlugStructs.h:242
bool HasUI() const
A struct for dealing with SysEx messages.
Definition: IPlugMidi.h:538
void LimitToStereoIO()
This is called by IPlugVST in order to limit a plug-in to stereo I/O for certain picky hosts...
int GetPluginVersion(bool decimal) const
Get the plug-in version number.
bool SerializePresets(IByteChunk &chunk) const
[VST2 only] Called when the VST2 host calls effGetChunk for a bank *
Base class that contains plug-in info and state manipulation methods.
Encapsulates information about the host transport state.
Definition: IPlugStructs.h:581
void SetHost(const char *host, int version)
Called to set the name of the current host, if known (calls on to HostSpecificInit() and OnHostIdenti...
virtual void OnActivate(bool active)
Override OnActivate() which should be called by the API class when a plug-in is "switched on" by the ...
virtual void OnPresetsModified()
[VST2 only] Called when the preset name is changed by the host
Used for key press info, such as ASCII representation, virtual key (mapped to win32 codes) and modifi...
Definition: IPlugStructs.h:612
void GetBounds(double &lo, double &hi) const
Get the minimum and maximum real value of the parameter&#39;s range in one method call.
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