iPlug2 - C++ Audio Plug-in Framework
Loading...
Searching...
No Matches
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
19using namespace iplug;
20
21#ifndef MAX_PATH_LEN
22#define MAX_PATH_LEN 2048
23#endif
24
25#define STRBUFSZ 100
26
27std::unique_ptr<IPlugAPPHost> IPlugAPPHost::sInstance;
28UINT gSCROLLMSG;
29
30IPlugAPPHost::IPlugAPPHost()
31: mIPlug(MakePlug(InstanceInfo{this}))
32{
33}
34
35IPlugAPPHost::~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
49IPlugAPPHost* IPlugAPPHost::Create()
50{
51 sInstance = std::make_unique<IPlugAPPHost>();
52 return sInstance.get();
53}
54
55bool 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
75bool IPlugAPPHost::OpenWindow(HWND pParent)
76{
77 return mIPlug->OpenWindow(pParent) != nullptr;
78}
79
80void IPlugAPPHost::CloseWindow()
81{
82 mIPlug->CloseWindow();
83}
84
85bool 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 overwrite
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
164void 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
202std::string IPlugAPPHost::GetAudioDeviceName(uint32_t deviceID) const
203{
204 auto str = mDAC->getDeviceInfo(deviceID).name;
205 std::size_t pos = str.find(':');
206
207 if (pos != std::string::npos)
208 {
209 std::string subStr = str.substr(pos + 1);
210 return subStr;
211 }
212 else
213 {
214 return str;
215 }
216}
217
218std::optional<uint32_t> IPlugAPPHost::GetAudioDeviceID(const char* deviceNameToTest) const
219{
220 auto deviceIDs = mDAC->getDeviceIds();
221
222 for (auto deviceID : deviceIDs)
223 {
224 auto name = GetAudioDeviceName(deviceID);
225
226 if (std::string_view(deviceNameToTest) == name)
227 {
228 return deviceID;
229 }
230 }
231
232 return std::nullopt;
233}
234
235int IPlugAPPHost::GetMIDIPortNumber(ERoute direction, const char* nameToTest) const
236{
237 int start = 1;
238
239 auto nameStrView = std::string_view(nameToTest);
240
241 if (direction == ERoute::kInput)
242 {
243 if (nameStrView == OFF_TEXT) return 0;
244
245 #ifdef OS_MAC
246 start = 2;
247 if (nameStrView == "virtual input") return 1;
248 #endif
249
250 for (int i = 0; i < mMidiIn->getPortCount(); i++)
251 {
252 if (nameStrView == mMidiIn->getPortName(i).c_str())
253 return (i + start);
254 }
255 }
256 else
257 {
258 if (nameStrView == OFF_TEXT) return 0;
259
260 #ifdef OS_MAC
261 start = 2;
262 if (nameStrView == "virtual output") return 1;
263 #endif
264
265 for (int i = 0; i < mMidiOut->getPortCount(); i++)
266 {
267 if (nameStrView == mMidiOut->getPortName(i).c_str())
268 return (i + start);
269 }
270 }
271
272 return -1;
273}
274
275void IPlugAPPHost::ProbeAudioIO()
276{
277 DBGMSG("\nRtAudio Version %s", RtAudio::getVersion().c_str());
278
279 RtAudio::DeviceInfo info;
280
281 mAudioInputDevIDs.clear();
282 mAudioOutputDevIDs.clear();
283
284 auto deviceIDs = mDAC->getDeviceIds();
285
286 for (auto deviceID : deviceIDs)
287 {
288 info = mDAC->getDeviceInfo(deviceID);
289
290 if (info.inputChannels > 0)
291 {
292 mAudioInputDevIDs.push_back(deviceID);
293 }
294
295 if (info.outputChannels > 0)
296 {
297 mAudioOutputDevIDs.push_back(deviceID);
298 }
299
300 if (info.isDefaultInput)
301 {
302 mDefaultInputDev = deviceID;
303 }
304
305 if (info.isDefaultOutput)
306 {
307 mDefaultOutputDev = deviceID;
308 }
309 }
310}
311
312void IPlugAPPHost::ProbeMidiIO()
313{
314 if (!mMidiIn || !mMidiOut)
315 return;
316 else
317 {
318 int nInputPorts = mMidiIn->getPortCount();
319
320 mMidiInputDevNames.push_back(OFF_TEXT);
321
322#ifdef OS_MAC
323 mMidiInputDevNames.push_back("virtual input");
324#endif
325
326 for (int i=0; i<nInputPorts; i++)
327 {
328 mMidiInputDevNames.push_back(mMidiIn->getPortName(i));
329 }
330
331 int nOutputPorts = mMidiOut->getPortCount();
332
333 mMidiOutputDevNames.push_back(OFF_TEXT);
334
335#ifdef OS_MAC
336 mMidiOutputDevNames.push_back("virtual output");
337#endif
338
339 for (int i=0; i<nOutputPorts; i++)
340 {
341 mMidiOutputDevNames.push_back(mMidiOut->getPortName(i));
342 //This means the virtual output port wont be added as an input
343 }
344 }
345}
346
347bool IPlugAPPHost::AudioSettingsInStateAreEqual(AppState& os, AppState& ns)
348{
349 if (os.mAudioDriverType != ns.mAudioDriverType) return false;
350 if (std::string_view(os.mAudioInDev.Get()) != ns.mAudioInDev.Get()) return false;
351 if (std::string_view(os.mAudioOutDev.Get()) != ns.mAudioOutDev.Get()) return false;
352 if (os.mAudioSR != ns.mAudioSR) return false;
353 if (os.mBufferSize != ns.mBufferSize) return false;
354 if (os.mAudioInChanL != ns.mAudioInChanL) return false;
355 if (os.mAudioInChanR != ns.mAudioInChanR) return false;
356 if (os.mAudioOutChanL != ns.mAudioOutChanL) return false;
357 if (os.mAudioOutChanR != ns.mAudioOutChanR) return false;
358// if (os.mAudioInIsMono != ns.mAudioInIsMono) return false;
359
360 return true;
361}
362
363bool IPlugAPPHost::MIDISettingsInStateAreEqual(AppState& os, AppState& ns)
364{
365 if (std::string_view(os.mMidiInDev.Get()) != ns.mMidiInDev.Get()) return false;
366 if (std::string_view(os.mMidiOutDev.Get()) != ns.mMidiOutDev.Get()) return false;
367 if (os.mMidiInChan != ns.mMidiInChan) return false;
368 if (os.mMidiOutChan != ns.mMidiOutChan) return false;
369
370 return true;
371}
372
373bool IPlugAPPHost::TryToChangeAudioDriverType()
374{
375 CloseAudio();
376
377 if (mDAC)
378 {
379 mDAC = nullptr;
380 }
381
382#if defined OS_WIN
383 if (mState.mAudioDriverType == kDeviceASIO)
384 mDAC = std::make_unique<RtAudio>(RtAudio::WINDOWS_ASIO);
385 else if (mState.mAudioDriverType == kDeviceDS)
386 mDAC = std::make_unique<RtAudio>(RtAudio::WINDOWS_DS);
387#elif defined OS_MAC
388 if (mState.mAudioDriverType == kDeviceCoreAudio)
389 mDAC = std::make_unique<RtAudio>(RtAudio::MACOSX_CORE);
390 //else
391 //mDAC = std::make_unique<RtAudio>(RtAudio::UNIX_JACK);
392#else
393 #error NOT IMPLEMENTED
394#endif
395
396 if (mDAC)
397 {
398 mDAC->setErrorCallback(ErrorCallback);
399 return true;
400 }
401
402 return false;
403}
404
405bool IPlugAPPHost::TryToChangeAudio()
406{
407#if defined OS_WIN
408 // ASIO has one device, use the output for the input ID
409 auto inputID = GetAudioDeviceID(mState.mAudioDriverType == kDeviceASIO ? mState.mAudioOutDev.Get() : mState.mAudioInDev.Get());
410#elif defined OS_MAC
411 auto inputID = GetAudioDeviceID(mState.mAudioInDev.Get());
412#else
413 #error NOT IMPLEMENTED
414#endif
415 auto outputID = GetAudioDeviceID(mState.mAudioOutDev.Get());
416
417 bool failedToFindDevice = false;
418 bool resetToDefault = false;
419
420 if (!inputID)
421 {
422 if (mDefaultInputDev)
423 {
424 resetToDefault = true;
425 inputID = mDefaultInputDev;
426
427 if (mAudioInputDevIDs.size())
428 mState.mAudioInDev.Set(GetAudioDeviceName(inputID.value()).c_str());
429 }
430 else
431 failedToFindDevice = true;
432 }
433
434 if (!outputID)
435 {
436 if (mDefaultOutputDev)
437 {
438 resetToDefault = true;
439 outputID = mDefaultOutputDev;
440
441 if (mAudioOutputDevIDs.size())
442 mState.mAudioOutDev.Set(GetAudioDeviceName(outputID.value()).c_str());
443 }
444 else
445 failedToFindDevice = true;
446 }
447
448 if (resetToDefault)
449 {
450 DBGMSG("Couldn't find previous audio device, reseting to default\n");
451 UpdateINI();
452 }
453
454 if (failedToFindDevice)
455 MessageBox(gHWND, "Please check the audio settings", "Error", MB_OK);
456
457 if (inputID && outputID)
458 {
459 return InitAudio(inputID.value(), outputID.value(), mState.mAudioSR, mState.mBufferSize);
460 }
461
462 return false;
463}
464
465bool IPlugAPPHost::SelectMIDIDevice(ERoute direction, const char* pPortName)
466{
467 int port = GetMIDIPortNumber(direction, pPortName);
468
469 if (direction == ERoute::kInput)
470 {
471 if (port == -1)
472 {
473 mState.mMidiInDev.Set(OFF_TEXT);
474 UpdateINI();
475 port = 0;
476 }
477
478 //TODO: send all notes off?
479 if (mMidiIn)
480 {
481 mMidiIn->closePort();
482
483 if (port == 0)
484 {
485 return true;
486 }
487 #if defined OS_WIN
488 else
489 {
490 mMidiIn->openPort(port-1);
491 return true;
492 }
493 #elif defined OS_MAC
494 else if (port == 1)
495 {
496 std::string virtualMidiInputName = "To ";
497 virtualMidiInputName += BUNDLE_NAME;
498 mMidiIn->openVirtualPort(virtualMidiInputName);
499 return true;
500 }
501 else
502 {
503 mMidiIn->openPort(port-2);
504 return true;
505 }
506 #else
507 #error NOT IMPLEMENTED
508 #endif
509 }
510 }
511 else
512 {
513 if (port == -1)
514 {
515 mState.mMidiOutDev.Set(OFF_TEXT);
516 UpdateINI();
517 port = 0;
518 }
519
520 if (mMidiOut)
521 {
522 //TODO: send all notes off?
523 mMidiOut->closePort();
524
525 if (port == 0)
526 return true;
527#if defined OS_WIN
528 else
529 {
530 mMidiOut->openPort(port-1);
531 return true;
532 }
533#elif defined OS_MAC
534 else if (port == 1)
535 {
536 std::string virtualMidiOutputName = "From ";
537 virtualMidiOutputName += BUNDLE_NAME;
538 mMidiOut->openVirtualPort(virtualMidiOutputName);
539 return true;
540 }
541 else
542 {
543 mMidiOut->openPort(port-2);
544 return true;
545 }
546#else
547 #error NOT IMPLEMENTED
548#endif
549 }
550 }
551
552 return false;
553}
554
555void IPlugAPPHost::CloseAudio()
556{
557 if (mDAC && mDAC->isStreamOpen())
558 {
559 if (mDAC->isStreamRunning())
560 {
561 mAudioEnding = true;
562
563 while (!mAudioDone)
564 Sleep(10);
565
566 mDAC->abortStream();
567 }
568
569 mDAC->closeStream();
570 }
571}
572
573bool IPlugAPPHost::InitAudio(uint32_t inID, uint32_t outID, uint32_t sr, uint32_t iovs)
574{
575 CloseAudio();
576
577 RtAudio::StreamParameters iParams, oParams;
578 iParams.deviceId = inID;
579 iParams.nChannels = GetPlug()->MaxNChannels(ERoute::kInput); // TODO: flexible channel count
580 iParams.firstChannel = 0; // TODO: flexible channel count
581
582 oParams.deviceId = outID;
583 oParams.nChannels = GetPlug()->MaxNChannels(ERoute::kOutput); // TODO: flexible channel count
584 oParams.firstChannel = 0; // TODO: flexible channel count
585
586 mBufferSize = iovs; // mBufferSize may get changed by stream
587
588 DBGMSG("trying to start audio stream @ %i sr, buffer size %i\nindev = %s\noutdev = %s\ninputs = %i\noutputs = %i\n",
589 sr, mBufferSize, GetAudioDeviceName(inID).c_str(), GetAudioDeviceName(outID).c_str(), iParams.nChannels, oParams.nChannels);
590
591 RtAudio::StreamOptions options;
592 options.flags = RTAUDIO_NONINTERLEAVED;
593 // options.streamName = BUNDLE_NAME; // JACK stream name, not used on other streams
594
595 mBufIndex = 0;
596 mSamplesElapsed = 0;
597 mSampleRate = static_cast<double>(sr);
598 mVecWait = 0;
599 mAudioEnding = false;
600 mAudioDone = false;
601
602 mIPlug->SetBlockSize(APP_SIGNAL_VECTOR_SIZE);
603 mIPlug->SetSampleRate(mSampleRate);
604 mIPlug->OnReset();
605
606 auto status = mDAC->openStream(&oParams, iParams.nChannels > 0 ? &iParams : nullptr, RTAUDIO_FLOAT64, sr, &mBufferSize, &AudioCallback, this, &options);
607
608 if (status != RtAudioErrorType::RTAUDIO_NO_ERROR)
609 {
610 DBGMSG("%s", mDAC->getErrorText().c_str());
611 return false;
612 }
613
614 for (int i = 0; i < iParams.nChannels; i++)
615 {
616 mInputBufPtrs.Add(nullptr); //will be set in callback
617 }
618
619 for (int i = 0; i < oParams.nChannels; i++)
620 {
621 mOutputBufPtrs.Add(nullptr); //will be set in callback
622 }
623
624 if (mDAC->startStream() != RTAUDIO_NO_ERROR)
625 {
626 DBGMSG("Error starting stream: %s\n", mDAC->getErrorText().c_str());
627 return false;
628 }
629
630 mActiveState = mState;
631
632 return true;
633}
634
635bool IPlugAPPHost::InitMidi()
636{
637 try
638 {
639 mMidiIn = std::make_unique<RtMidiIn>();
640 }
641 catch (RtMidiError &error)
642 {
643 mMidiIn = nullptr;
644 error.printMessage();
645 return false;
646 }
647
648 try
649 {
650 mMidiOut = std::make_unique<RtMidiOut>();
651 }
652 catch (RtMidiError &error)
653 {
654 mMidiOut = nullptr;
655 error.printMessage();
656 return false;
657 }
658
659 mMidiIn->setCallback(&MIDICallback, this);
660 mMidiIn->ignoreTypes(false, true, false );
661
662 return true;
663}
664
665void ApplyFades(double *pBuffer, int nChans, int nFrames, bool down)
666{
667 for (int i = 0; i < nChans; i++)
668 {
669 double *pIO = pBuffer + (i * nFrames);
670
671 if (down)
672 {
673 for (int j = 0; j < nFrames; j++)
674 pIO[j] *= ((double) (nFrames - (j + 1)) / (double) nFrames);
675 }
676 else
677 {
678 for (int j = 0; j < nFrames; j++)
679 pIO[j] *= ((double) j / (double) nFrames);
680 }
681 }
682}
683
684// static
685int IPlugAPPHost::AudioCallback(void* pOutputBuffer, void* pInputBuffer, uint32_t nFrames, double streamTime, RtAudioStreamStatus status, void* pUserData)
686{
687 IPlugAPPHost* _this = (IPlugAPPHost*) pUserData;
688
689 int nins = _this->GetPlug()->MaxNChannels(ERoute::kInput);
690 int nouts = _this->GetPlug()->MaxNChannels(ERoute::kOutput);
691
692 double* pInputBufferD = static_cast<double*>(pInputBuffer);
693 double* pOutputBufferD = static_cast<double*>(pOutputBuffer);
694
695 bool startWait = _this->mVecWait >= APP_N_VECTOR_WAIT; // wait APP_N_VECTOR_WAIT * iovs before processing audio, to avoid clicks
696 bool doFade = _this->mVecWait == APP_N_VECTOR_WAIT || _this->mAudioEnding;
697
698 if (startWait && !_this->mAudioDone)
699 {
700 if (doFade)
701 ApplyFades(pInputBufferD, nins, nFrames, _this->mAudioEnding);
702
703 for (int i = 0; i < nFrames; i++)
704 {
705 _this->mBufIndex %= APP_SIGNAL_VECTOR_SIZE;
706
707 if (_this->mBufIndex == 0)
708 {
709 for (int c = 0; c < nins; c++)
710 {
711 _this->mInputBufPtrs.Set(c, (pInputBufferD + (c * nFrames)) + i);
712 }
713
714 for (int c = 0; c < nouts; c++)
715 {
716 _this->mOutputBufPtrs.Set(c, (pOutputBufferD + (c * nFrames)) + i);
717 }
718
719 _this->mIPlug->AppProcess(_this->mInputBufPtrs.GetList(), _this->mOutputBufPtrs.GetList(), APP_SIGNAL_VECTOR_SIZE);
720
721 _this->mSamplesElapsed += APP_SIGNAL_VECTOR_SIZE;
722 }
723
724 for (int c = 0; c < nouts; c++)
725 {
726 pOutputBufferD[c * nFrames + i] *= APP_MULT;
727 }
728
729 _this->mBufIndex++;
730 }
731
732 if (doFade)
733 ApplyFades(pOutputBufferD, nouts, nFrames, _this->mAudioEnding);
734
735 if (_this->mAudioEnding)
736 _this->mAudioDone = true;
737 }
738 else
739 {
740 memset(pOutputBufferD, 0, nFrames * nouts * sizeof(double));
741 }
742
743 _this->mVecWait = std::min(_this->mVecWait + 1, uint32_t(APP_N_VECTOR_WAIT + 1));
744
745 return 0;
746}
747
748// static
749void IPlugAPPHost::MIDICallback(double deltatime, std::vector<uint8_t>* pMsg, void* pUserData)
750{
751 IPlugAPPHost* _this = (IPlugAPPHost*) pUserData;
752
753 if (pMsg->size() == 0 || _this->mExiting)
754 return;
755
756 if (pMsg->size() > 3)
757 {
758 if (pMsg->size() > MAX_SYSEX_SIZE)
759 {
760 DBGMSG("SysEx message exceeds MAX_SYSEX_SIZE\n");
761 return;
762 }
763
764 SysExData data { 0, static_cast<int>(pMsg->size()), pMsg->data() };
765
766 _this->mIPlug->mSysExMsgsFromCallback.Push(data);
767 return;
768 }
769 else if (pMsg->size())
770 {
771 IMidiMsg msg;
772 msg.mStatus = pMsg->at(0);
773 pMsg->size() > 1 ? msg.mData1 = pMsg->at(1) : msg.mData1 = 0;
774 pMsg->size() > 2 ? msg.mData2 = pMsg->at(2) : msg.mData2 = 0;
775
776 _this->mIPlug->mMidiMsgsFromCallback.Push(msg);
777 }
778}
779
780// static
781void IPlugAPPHost::ErrorCallback(RtAudioErrorType type, const std::string &errorText)
782{
783 std::cerr << "\nerrorCallback: " << errorText << "\n\n";
784}
785
IPlug logging a.k.a tracing functionality.
A class that hosts an IPlug as a standalone app and provides Audio/Midi I/O.
Definition: IPlugAPP_host.h:83
int GetMIDIPortNumber(ERoute direction, const char *name) const
std::optional< uint32_t > GetAudioDeviceID(const char *name) const
Returns the a validated audio device ID linked to a particular name.
std::string GetAudioDeviceName(uint32_t deviceID) const
Returns the name of the audio device with a given RTAudio device ID.
int MaxNChannels(ERoute direction) const
ERoute
Used to identify whether a bus/channel connection is an input or an output.
Encapsulates a MIDI message and provides helper functions.
Definition: IPlugMidi.h:31
This structure is used when queueing Sysex messages.
Definition: IPlugStructs.h:45