iPlug2 - C++ Audio Plug-in Framework
IPlugAPP_host.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 "IPlugAPP_host.h"
12 
13 #ifdef OS_WIN
14 #include <sys/stat.h>
15 #endif
16 
17 #include "IPlugLogger.h"
18 
19 using namespace iplug;
20 
21 #ifndef MAX_PATH_LEN
22 #define MAX_PATH_LEN 2048
23 #endif
24 
25 #define STRBUFSZ 100
26 
27 std::unique_ptr<IPlugAPPHost> IPlugAPPHost::sInstance;
28 UINT gSCROLLMSG;
29 
30 IPlugAPPHost::IPlugAPPHost()
31 : mIPlug(MakePlug(InstanceInfo{this}))
32 {
33 }
34 
35 IPlugAPPHost::~IPlugAPPHost()
36 {
37  mExiting = true;
38 
39  CloseAudio();
40 
41  if(mMidiIn)
42  mMidiIn->cancelCallback();
43 
44  if(mMidiOut)
45  mMidiOut->closePort();
46 }
47 
48 //static
49 IPlugAPPHost* IPlugAPPHost::Create()
50 {
51  sInstance = std::make_unique<IPlugAPPHost>();
52  return sInstance.get();
53 }
54 
55 bool IPlugAPPHost::Init()
56 {
57  mIPlug->SetHost("standalone", mIPlug->GetPluginVersion(false));
58 
59  if (!InitState())
60  return false;
61 
62  TryToChangeAudioDriverType(); // will init RTAudio with an API type based on gState->mAudioDriverType
63  ProbeAudioIO(); // find out what audio IO devs are available and put their IDs in the global variables gAudioInputDevs / gAudioOutputDevs
64  InitMidi(); // creates RTMidiIn and RTMidiOut objects
65  ProbeMidiIO(); // find out what midi IO devs are available and put their names in the global variables gMidiInputDevs / gMidiOutputDevs
66  SelectMIDIDevice(ERoute::kInput, mState.mMidiInDev.Get());
67  SelectMIDIDevice(ERoute::kOutput, mState.mMidiOutDev.Get());
68 
69  mIPlug->OnParamReset(kReset);
70  mIPlug->OnActivate(true);
71 
72  return true;
73 }
74 
75 bool IPlugAPPHost::OpenWindow(HWND pParent)
76 {
77  return mIPlug->OpenWindow(pParent) != nullptr;
78 }
79 
80 void IPlugAPPHost::CloseWindow()
81 {
82  mIPlug->CloseWindow();
83 }
84 
85 bool IPlugAPPHost::InitState()
86 {
87 #if defined OS_WIN
88  TCHAR strPath[MAX_PATH_LEN];
89  SHGetFolderPathA( NULL, CSIDL_LOCAL_APPDATA, NULL, 0, strPath );
90  mINIPath.SetFormatted(MAX_PATH_LEN, "%s\\%s\\", strPath, BUNDLE_NAME);
91 #elif defined OS_MAC
92  mINIPath.SetFormatted(MAX_PATH_LEN, "%s/Library/Application Support/%s/", getenv("HOME"), BUNDLE_NAME);
93 #else
94  #error NOT IMPLEMENTED
95 #endif
96 
97  struct stat st;
98 
99  if(stat(mINIPath.Get(), &st) == 0) // if directory exists
100  {
101  mINIPath.Append("settings.ini"); // add file name to path
102 
103  char buf[STRBUFSZ];
104 
105  if(stat(mINIPath.Get(), &st) == 0) // if settings file exists read values into state
106  {
107  DBGMSG("Reading ini file from %s\n", mINIPath.Get());
108 
109  mState.mAudioDriverType = GetPrivateProfileInt("audio", "driver", 0, mINIPath.Get());
110 
111  GetPrivateProfileString("audio", "indev", "Built-in Input", buf, STRBUFSZ, mINIPath.Get()); mState.mAudioInDev.Set(buf);
112  GetPrivateProfileString("audio", "outdev", "Built-in Output", buf, STRBUFSZ, mINIPath.Get()); mState.mAudioOutDev.Set(buf);
113 
114  //audio
115  mState.mAudioInChanL = GetPrivateProfileInt("audio", "in1", 1, mINIPath.Get()); // 1 is first audio input
116  mState.mAudioInChanR = GetPrivateProfileInt("audio", "in2", 2, mINIPath.Get());
117  mState.mAudioOutChanL = GetPrivateProfileInt("audio", "out1", 1, mINIPath.Get()); // 1 is first audio output
118  mState.mAudioOutChanR = GetPrivateProfileInt("audio", "out2", 2, mINIPath.Get());
119  //mState.mAudioInIsMono = GetPrivateProfileInt("audio", "monoinput", 0, mINIPath.Get());
120 
121  mState.mBufferSize = GetPrivateProfileInt("audio", "buffer", 512, mINIPath.Get());
122  mState.mAudioSR = GetPrivateProfileInt("audio", "sr", 44100, mINIPath.Get());
123 
124  //midi
125  GetPrivateProfileString("midi", "indev", "no input", buf, STRBUFSZ, mINIPath.Get()); mState.mMidiInDev.Set(buf);
126  GetPrivateProfileString("midi", "outdev", "no output", buf, STRBUFSZ, mINIPath.Get()); mState.mMidiOutDev.Set(buf);
127 
128  mState.mMidiInChan = GetPrivateProfileInt("midi", "inchan", 0, mINIPath.Get()); // 0 is any
129  mState.mMidiOutChan = GetPrivateProfileInt("midi", "outchan", 0, mINIPath.Get()); // 1 is first chan
130  }
131 
132  // if settings file doesn't exist, populate with default values, otherwise overrwrite
133  UpdateINI();
134  }
135  else // folder doesn't exist - make folder and make file
136  {
137 #if defined OS_WIN
138  // folder doesn't exist - make folder and make file
139  CreateDirectory(mINIPath.Get(), NULL);
140  mINIPath.Append("settings.ini");
141  UpdateINI(); // will write file if doesn't exist
142 #elif defined OS_MAC
143  mode_t process_mask = umask(0);
144  int result_code = mkdir(mINIPath.Get(), S_IRWXU | S_IRWXG | S_IRWXO);
145  umask(process_mask);
146 
147  if(!result_code)
148  {
149  mINIPath.Append("\\settings.ini");
150  UpdateINI(); // will write file if doesn't exist
151  }
152  else
153  {
154  return false;
155  }
156 #else
157  #error NOT IMPLEMENTED
158 #endif
159  }
160 
161  return true;
162 }
163 
164 void IPlugAPPHost::UpdateINI()
165 {
166  char buf[STRBUFSZ]; // temp buffer for writing integers to profile strings
167  const char* ini = mINIPath.Get();
168 
169  sprintf(buf, "%u", mState.mAudioDriverType);
170  WritePrivateProfileString("audio", "driver", buf, ini);
171 
172  WritePrivateProfileString("audio", "indev", mState.mAudioInDev.Get(), ini);
173  WritePrivateProfileString("audio", "outdev", mState.mAudioOutDev.Get(), ini);
174 
175  sprintf(buf, "%u", mState.mAudioInChanL);
176  WritePrivateProfileString("audio", "in1", buf, ini);
177  sprintf(buf, "%u", mState.mAudioInChanR);
178  WritePrivateProfileString("audio", "in2", buf, ini);
179  sprintf(buf, "%u", mState.mAudioOutChanL);
180  WritePrivateProfileString("audio", "out1", buf, ini);
181  sprintf(buf, "%u", mState.mAudioOutChanR);
182  WritePrivateProfileString("audio", "out2", buf, ini);
183  //sprintf(buf, "%u", mState.mAudioInIsMono);
184  //WritePrivateProfileString("audio", "monoinput", buf, ini);
185 
186  WDL_String str;
187  str.SetFormatted(32, "%i", mState.mBufferSize);
188  WritePrivateProfileString("audio", "buffer", str.Get(), ini);
189 
190  str.SetFormatted(32, "%i", mState.mAudioSR);
191  WritePrivateProfileString("audio", "sr", str.Get(), ini);
192 
193  WritePrivateProfileString("midi", "indev", mState.mMidiInDev.Get(), ini);
194  WritePrivateProfileString("midi", "outdev", mState.mMidiOutDev.Get(), ini);
195 
196  sprintf(buf, "%u", mState.mMidiInChan);
197  WritePrivateProfileString("midi", "inchan", buf, ini);
198  sprintf(buf, "%u", mState.mMidiOutChan);
199  WritePrivateProfileString("midi", "outchan", buf, ini);
200 }
201 
202 std::string IPlugAPPHost::GetAudioDeviceName(int idx) const
203 {
204  return mAudioIDDevNames.at(idx);
205 }
206 
207 int IPlugAPPHost::GetAudioDeviceIdx(const char* deviceNameToTest) const
208 {
209  for(int i = 0; i < mAudioIDDevNames.size(); i++)
210  {
211  if(!strcmp(deviceNameToTest, mAudioIDDevNames.at(i).c_str() ))
212  return i;
213  }
214 
215  return -1;
216 }
217 
218 int IPlugAPPHost::GetMIDIPortNumber(ERoute direction, const char* nameToTest) const
219 {
220  int start = 1;
221 
222  if(direction == ERoute::kInput)
223  {
224  if(!strcmp(nameToTest, OFF_TEXT)) return 0;
225 
226  #ifdef OS_MAC
227  start = 2;
228  if(!strcmp(nameToTest, "virtual input")) return 1;
229  #endif
230 
231  for (int i = 0; i < mMidiIn->getPortCount(); i++)
232  {
233  if(!strcmp(nameToTest, mMidiIn->getPortName(i).c_str()))
234  return (i + start);
235  }
236  }
237  else
238  {
239  if(!strcmp(nameToTest, OFF_TEXT)) return 0;
240 
241  #ifdef OS_MAC
242  start = 2;
243  if(!strcmp(nameToTest, "virtual output")) return 1;
244  #endif
245 
246  for (int i = 0; i < mMidiOut->getPortCount(); i++)
247  {
248  if(!strcmp(nameToTest, mMidiOut->getPortName(i).c_str()))
249  return (i + start);
250  }
251  }
252 
253  return -1;
254 }
255 
257 {
258  std::cout << "\nRtAudio Version " << RtAudio::getVersion() << std::endl;
259 
260  RtAudio::DeviceInfo info;
261 
262  mAudioInputDevs.clear();
263  mAudioOutputDevs.clear();
264  mAudioIDDevNames.clear();
265 
266  uint32_t nDevices = mDAC->getDeviceCount();
267 
268  for (int i=0; i<nDevices; i++)
269  {
270  info = mDAC->getDeviceInfo(i);
271  std::string deviceName = info.name;
272 
273 #ifdef OS_MAC
274  size_t colonIdx = deviceName.rfind(": ");
275 
276  if(colonIdx != std::string::npos && deviceName.length() >= 2)
277  deviceName = deviceName.substr(colonIdx + 2, deviceName.length() - colonIdx - 2);
278 
279 #endif
280 
281  mAudioIDDevNames.push_back(deviceName);
282 
283  if ( info.probed == false )
284  std::cout << deviceName << ": Probe Status = Unsuccessful\n";
285  else if ( !strcmp("Generic Low Latency ASIO Driver", deviceName.c_str() ))
286  std::cout << deviceName << ": Probe Status = Unsuccessful\n";
287  else
288  {
289  if(info.inputChannels > 0)
290  mAudioInputDevs.push_back(i);
291 
292  if(info.outputChannels > 0)
293  mAudioOutputDevs.push_back(i);
294 
295  if (info.isDefaultInput)
296  mDefaultInputDev = i;
297 
298  if (info.isDefaultOutput)
299  mDefaultOutputDev = i;
300  }
301  }
302 }
303 
304 void IPlugAPPHost::ProbeMidiIO()
305 {
306  if ( !mMidiIn || !mMidiOut )
307  return;
308  else
309  {
310  int nInputPorts = mMidiIn->getPortCount();
311 
312  mMidiInputDevNames.push_back(OFF_TEXT);
313 
314 #ifdef OS_MAC
315  mMidiInputDevNames.push_back("virtual input");
316 #endif
317 
318  for (int i=0; i<nInputPorts; i++ )
319  {
320  mMidiInputDevNames.push_back(mMidiIn->getPortName(i));
321  }
322 
323  int nOutputPorts = mMidiOut->getPortCount();
324 
325  mMidiOutputDevNames.push_back(OFF_TEXT);
326 
327 #ifdef OS_MAC
328  mMidiOutputDevNames.push_back("virtual output");
329 #endif
330 
331  for (int i=0; i<nOutputPorts; i++ )
332  {
333  mMidiOutputDevNames.push_back(mMidiOut->getPortName(i));
334  //This means the virtual output port wont be added as an input
335  }
336  }
337 }
338 
339 bool IPlugAPPHost::AudioSettingsInStateAreEqual(AppState& os, AppState& ns)
340 {
341  if (os.mAudioDriverType != ns.mAudioDriverType) return false;
342  if (strcmp(os.mAudioInDev.Get(), ns.mAudioInDev.Get())) return false;
343  if (strcmp(os.mAudioOutDev.Get(), ns.mAudioOutDev.Get())) return false;
344  if (os.mAudioSR != ns.mAudioSR) return false;
345  if (os.mBufferSize != ns.mBufferSize) return false;
346  if (os.mAudioInChanL != ns.mAudioInChanL) return false;
347  if (os.mAudioInChanR != ns.mAudioInChanR) return false;
348  if (os.mAudioOutChanL != ns.mAudioOutChanL) return false;
349  if (os.mAudioOutChanR != ns.mAudioOutChanR) return false;
350 // if (os.mAudioInIsMono != ns.mAudioInIsMono) return false;
351 
352  return true;
353 }
354 
355 bool IPlugAPPHost::MIDISettingsInStateAreEqual(AppState& os, AppState& ns)
356 {
357  if (strcmp(os.mMidiInDev.Get(), ns.mMidiInDev.Get())) return false;
358  if (strcmp(os.mMidiOutDev.Get(), ns.mMidiOutDev.Get())) return false;
359  if (os.mMidiInChan != ns.mMidiInChan) return false;
360  if (os.mMidiOutChan != ns.mMidiOutChan) return false;
361 
362  return true;
363 }
364 
365 bool IPlugAPPHost::TryToChangeAudioDriverType()
366 {
367  CloseAudio();
368 
369  if (mDAC)
370  {
371  mDAC = nullptr;
372  }
373 
374 #if defined OS_WIN
375  if(mState.mAudioDriverType == kDeviceASIO)
376  mDAC = std::make_unique<RtAudio>(RtAudio::WINDOWS_ASIO);
377  else
378  mDAC = std::make_unique<RtAudio>(RtAudio::WINDOWS_DS);
379 #elif defined OS_MAC
380  if(mState.mAudioDriverType == kDeviceCoreAudio)
381  mDAC = std::make_unique<RtAudio>(RtAudio::MACOSX_CORE);
382  //else
383  //mDAC = std::make_unique<RtAudio>(RtAudio::UNIX_JACK);
384 #else
385  #error NOT IMPLEMENTED
386 #endif
387 
388  if(mDAC)
389  return true;
390  else
391  return false;
392 }
393 
394 bool IPlugAPPHost::TryToChangeAudio()
395 {
396  int inputID = -1;
397  int outputID = -1;
398 
399 #if defined OS_WIN
400  if(mState.mAudioDriverType == kDeviceASIO)
401  inputID = GetAudioDeviceIdx(mState.mAudioOutDev.Get());
402  else
403  inputID = GetAudioDeviceIdx(mState.mAudioInDev.Get());
404 #elif defined OS_MAC
405  inputID = GetAudioDeviceIdx(mState.mAudioInDev.Get());
406 #else
407  #error NOT IMPLEMENTED
408 #endif
409  outputID = GetAudioDeviceIdx(mState.mAudioOutDev.Get());
410 
411  bool failedToFindDevice = false;
412  bool resetToDefault = false;
413 
414  if (inputID == -1)
415  {
416  if (mDefaultInputDev > -1)
417  {
418  resetToDefault = true;
419  inputID = mDefaultInputDev;
420 
421  if (mAudioInputDevs.size())
422  mState.mAudioInDev.Set(GetAudioDeviceName(inputID).c_str());
423  }
424  else
425  failedToFindDevice = true;
426  }
427 
428  if (outputID == -1)
429  {
430  if (mDefaultOutputDev > -1)
431  {
432  resetToDefault = true;
433 
434  outputID = mDefaultOutputDev;
435 
436  if (mAudioOutputDevs.size())
437  mState.mAudioOutDev.Set(GetAudioDeviceName(outputID).c_str());
438  }
439  else
440  failedToFindDevice = true;
441  }
442 
443  if (resetToDefault)
444  {
445  DBGMSG("couldn't find previous audio device, reseting to default\n");
446 
447  UpdateINI();
448  }
449 
450  if (failedToFindDevice)
451  MessageBox(gHWND, "Please check your soundcard settings in Preferences", "Error", MB_OK);
452 
453  if (inputID != -1 && outputID != -1)
454  {
455  return InitAudio(inputID, outputID, mState.mAudioSR, mState.mBufferSize);
456  }
457 
458  return false;
459 }
460 
461 bool IPlugAPPHost::SelectMIDIDevice(ERoute direction, const char* pPortName)
462 {
463  int port = GetMIDIPortNumber(direction, pPortName);
464 
465  if(direction == ERoute::kInput)
466  {
467  if(port == -1)
468  {
469  mState.mMidiInDev.Set(OFF_TEXT);
470  UpdateINI();
471  port = 0;
472  }
473 
474  //TODO: send all notes off?
475  if (mMidiIn)
476  {
477  mMidiIn->closePort();
478 
479  if (port == 0)
480  {
481  return true;
482  }
483  #if defined OS_WIN
484  else
485  {
486  mMidiIn->openPort(port-1);
487  return true;
488  }
489  #elif defined OS_MAC
490  else if(port == 1)
491  {
492  std::string virtualMidiInputName = "To ";
493  virtualMidiInputName += BUNDLE_NAME;
494  mMidiIn->openVirtualPort(virtualMidiInputName);
495  return true;
496  }
497  else
498  {
499  mMidiIn->openPort(port-2);
500  return true;
501  }
502  #else
503  #error NOT IMPLEMENTED
504  #endif
505  }
506  }
507  else
508  {
509  if(port == -1)
510  {
511  mState.mMidiOutDev.Set(OFF_TEXT);
512  UpdateINI();
513  port = 0;
514  }
515 
516  if (mMidiOut)
517  {
518  //TODO: send all notes off?
519  mMidiOut->closePort();
520 
521  if (port == 0)
522  return true;
523 #if defined OS_WIN
524  else
525  {
526  mMidiOut->openPort(port-1);
527  return true;
528  }
529 #elif defined OS_MAC
530  else if(port == 1)
531  {
532  std::string virtualMidiOutputName = "From ";
533  virtualMidiOutputName += BUNDLE_NAME;
534  mMidiOut->openVirtualPort(virtualMidiOutputName);
535  return true;
536  }
537  else
538  {
539  mMidiOut->openPort(port-2);
540  return true;
541  }
542 #else
543  #error NOT IMPLEMENTED
544 #endif
545  }
546  }
547 
548  return false;
549 }
550 
551 void IPlugAPPHost::CloseAudio()
552 {
553  if (mDAC && mDAC->isStreamOpen())
554  {
555  if (mDAC->isStreamRunning())
556  {
557  mAudioEnding = true;
558 
559  while (!mAudioDone)
560  Sleep(10);
561 
562  try
563  {
564  mDAC->abortStream();
565  }
566  catch (RtAudioError& e)
567  {
568  e.printMessage();
569  }
570  }
571 
572  mDAC->closeStream();
573  }
574 }
575 
576 bool IPlugAPPHost::InitAudio(uint32_t inId, uint32_t outId, uint32_t sr, uint32_t iovs)
577 {
578  CloseAudio();
579 
580  RtAudio::StreamParameters iParams, oParams;
581  iParams.deviceId = inId;
582  iParams.nChannels = GetPlug()->MaxNChannels(ERoute::kInput); // TODO: flexible channel count
583  iParams.firstChannel = 0; // TODO: flexible channel count
584 
585  oParams.deviceId = outId;
586  oParams.nChannels = GetPlug()->MaxNChannels(ERoute::kOutput); // TODO: flexible channel count
587  oParams.firstChannel = 0; // TODO: flexible channel count
588 
589  mBufferSize = iovs; // mBufferSize may get changed by stream
590 
591  DBGMSG("\ntrying to start audio stream @ %i sr, %i buffer size\nindev = %i:%s\noutdev = %i:%s\ninputs = %i\noutputs = %i\n",
592  sr, mBufferSize, inId, GetAudioDeviceName(inId).c_str(), outId, GetAudioDeviceName(outId).c_str(), iParams.nChannels, oParams.nChannels);
593 
594  RtAudio::StreamOptions options;
595  options.flags = RTAUDIO_NONINTERLEAVED;
596  // options.streamName = BUNDLE_NAME; // JACK stream name, not used on other streams
597 
598  mBufIndex = 0;
599  mSamplesElapsed = 0;
600  mSampleRate = (double) sr;
601  mVecWait = 0;
602  mAudioEnding = false;
603  mAudioDone = false;
604 
605  mIPlug->SetBlockSize(APP_SIGNAL_VECTOR_SIZE);
606  mIPlug->SetSampleRate(mSampleRate);
607  mIPlug->OnReset();
608 
609  try
610  {
611  mDAC->openStream(&oParams, iParams.nChannels > 0 ? &iParams : nullptr, RTAUDIO_FLOAT64, sr, &mBufferSize, &AudioCallback, this, &options /*, &ErrorCallback */);
612 
613  for (int i = 0; i < iParams.nChannels; i++)
614  {
615  mInputBufPtrs.Add(nullptr); //will be set in callback
616  }
617 
618  for (int i = 0; i < oParams.nChannels; i++)
619  {
620  mOutputBufPtrs.Add(nullptr); //will be set in callback
621  }
622 
623  mDAC->startStream();
624 
625  mActiveState = mState;
626  }
627  catch (RtAudioError& e)
628  {
629  e.printMessage();
630  return false;
631  }
632 
633  return true;
634 }
635 
636 bool IPlugAPPHost::InitMidi()
637 {
638  try
639  {
640  mMidiIn = std::make_unique<RtMidiIn>();
641  }
642  catch (RtMidiError &error)
643  {
644  mMidiIn = nullptr;
645  error.printMessage();
646  return false;
647  }
648 
649  try
650  {
651  mMidiOut = std::make_unique<RtMidiOut>();
652  }
653  catch (RtMidiError &error)
654  {
655  mMidiOut = nullptr;
656  error.printMessage();
657  return false;
658  }
659 
660  mMidiIn->setCallback(&MIDICallback, this);
661  mMidiIn->ignoreTypes(false, true, false );
662 
663  return true;
664 }
665 
666 void ApplyFades(double *pBuffer, int nChans, int nFrames, bool down)
667 {
668  for (int i = 0; i < nChans; i++)
669  {
670  double *pIO = pBuffer + (i * nFrames);
671 
672  if (down)
673  {
674  for (int j = 0; j < nFrames; j++)
675  pIO[j] *= ((double) (nFrames - (j + 1)) / (double) nFrames);
676  }
677  else
678  {
679  for (int j = 0; j < nFrames; j++)
680  pIO[j] *= ((double) j / (double) nFrames);
681  }
682  }
683 }
684 
685 // static
686 int IPlugAPPHost::AudioCallback(void* pOutputBuffer, void* pInputBuffer, uint32_t nFrames, double streamTime, RtAudioStreamStatus status, void* pUserData)
687 {
688  IPlugAPPHost* _this = (IPlugAPPHost*) pUserData;
689 
690  int nins = _this->GetPlug()->MaxNChannels(ERoute::kInput);
691  int nouts = _this->GetPlug()->MaxNChannels(ERoute::kOutput);
692 
693  double* pInputBufferD = static_cast<double*>(pInputBuffer);
694  double* pOutputBufferD = static_cast<double*>(pOutputBuffer);
695 
696  bool startWait = _this->mVecWait >= APP_N_VECTOR_WAIT; // wait APP_N_VECTOR_WAIT * iovs before processing audio, to avoid clicks
697  bool doFade = _this->mVecWait == APP_N_VECTOR_WAIT || _this->mAudioEnding;
698 
699  if (startWait && !_this->mAudioDone)
700  {
701  if (doFade)
702  ApplyFades(pInputBufferD, nins, nFrames, _this->mAudioEnding);
703 
704  for (int i = 0; i < nFrames; i++)
705  {
706  _this->mBufIndex %= APP_SIGNAL_VECTOR_SIZE;
707 
708  if (_this->mBufIndex == 0)
709  {
710  for (int c = 0; c < nins; c++)
711  {
712  _this->mInputBufPtrs.Set(c, (pInputBufferD + (c * nFrames)) + i);
713  }
714 
715  for (int c = 0; c < nouts; c++)
716  {
717  _this->mOutputBufPtrs.Set(c, (pOutputBufferD + (c * nFrames)) + i);
718  }
719 
720  _this->mIPlug->AppProcess(_this->mInputBufPtrs.GetList(), _this->mOutputBufPtrs.GetList(), APP_SIGNAL_VECTOR_SIZE);
721 
722  _this->mSamplesElapsed += APP_SIGNAL_VECTOR_SIZE;
723  }
724 
725  for (int c = 0; c < nouts; c++)
726  {
727  pOutputBufferD[c * nFrames + i] *= APP_MULT;
728  }
729 
730  _this->mBufIndex++;
731  }
732 
733  if (doFade)
734  ApplyFades(pOutputBufferD, nouts, nFrames, _this->mAudioEnding);
735 
736  if (_this->mAudioEnding)
737  _this->mAudioDone = true;
738  }
739  else
740  {
741  memset(pOutputBufferD, 0, nFrames * nouts * sizeof(double));
742  }
743 
744  _this->mVecWait = std::min(_this->mVecWait + 1, uint32_t(APP_N_VECTOR_WAIT + 1));
745 
746  return 0;
747 }
748 
749 // static
750 void IPlugAPPHost::MIDICallback(double deltatime, std::vector<uint8_t>* pMsg, void* pUserData)
751 {
752  IPlugAPPHost* _this = (IPlugAPPHost*) pUserData;
753 
754  if (pMsg->size() == 0 || _this->mExiting)
755  return;
756 
757  if (pMsg->size() > 3)
758  {
759  if(pMsg->size() > MAX_SYSEX_SIZE)
760  {
761  DBGMSG("SysEx message exceeds MAX_SYSEX_SIZE\n");
762  return;
763  }
764 
765  SysExData data { 0, static_cast<int>(pMsg->size()), pMsg->data() };
766 
767  _this->mIPlug->mSysExMsgsFromCallback.Push(data);
768  return;
769  }
770  else if (pMsg->size())
771  {
772  IMidiMsg msg;
773  msg.mStatus = pMsg->at(0);
774  pMsg->size() > 1 ? msg.mData1 = pMsg->at(1) : msg.mData1 = 0;
775  pMsg->size() > 2 ? msg.mData2 = pMsg->at(2) : msg.mData2 = 0;
776 
777  _this->mIPlug->mMidiMsgsFromCallback.Push(msg);
778  }
779 }
780 
781 // static
782 void IPlugAPPHost::ErrorCallback(RtAudioError::Type type, const std::string &errorText )
783 {
784  //TODO:
785 }
786 
int MaxNChannels(ERoute direction) const
std::string GetAudioDeviceName(int idx) const
Returns the name of the audio device at idx.
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:30
int GetMIDIPortNumber(ERoute direction, const char *name) const
void ProbeAudioIO()
find out which devices have input channels & which have output channels, add their ids to the lists ...
IPlug logging a.k.a tracing functionality.
ERoute
Used to identify whether a bus/channel connection is an input or an output.
int GetAudioDeviceIdx(const char *name) const
Returns the audio device index linked to a particular name.
This structure is used when queueing Sysex messages.
Definition: IPlugStructs.h:44
Used to manage changes to app i/o.
Definition: IPlugAPP_host.h:86
A class that hosts an IPlug as a standalone app and provides Audio/Midi I/O.
Definition: IPlugAPP_host.h:81