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