11#include "IPlugAPP_host.h"
22#define MAX_PATH_LEN 2048
27std::unique_ptr<IPlugAPPHost> IPlugAPPHost::sInstance;
30IPlugAPPHost::IPlugAPPHost()
31: mIPlug(MakePlug(InstanceInfo{this}))
35IPlugAPPHost::~IPlugAPPHost()
42 mMidiIn->cancelCallback();
45 mMidiOut->closePort();
51 sInstance = std::make_unique<IPlugAPPHost>();
52 return sInstance.get();
55bool IPlugAPPHost::Init()
57 mIPlug->SetHost(
"standalone", mIPlug->GetPluginVersion(
false));
62 TryToChangeAudioDriverType();
66 SelectMIDIDevice(ERoute::kInput, mState.mMidiInDev.Get());
67 SelectMIDIDevice(ERoute::kOutput, mState.mMidiOutDev.Get());
69 mIPlug->OnParamReset(kReset);
70 mIPlug->OnActivate(
true);
75bool IPlugAPPHost::OpenWindow(HWND pParent)
77 return mIPlug->OpenWindow(pParent) !=
nullptr;
80void IPlugAPPHost::CloseWindow()
82 mIPlug->CloseWindow();
85bool IPlugAPPHost::InitState()
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);
92 mINIPath.SetFormatted(MAX_PATH_LEN,
"%s/Library/Application Support/%s/", getenv(
"HOME"), BUNDLE_NAME);
94 #error NOT IMPLEMENTED
99 if(stat(mINIPath.Get(), &st) == 0)
101 mINIPath.Append(
"settings.ini");
105 if(stat(mINIPath.Get(), &st) == 0)
107 DBGMSG(
"Reading ini file from %s\n", mINIPath.Get());
109 mState.mAudioDriverType = GetPrivateProfileInt(
"audio",
"driver", 0, mINIPath.Get());
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);
115 mState.mAudioInChanL = GetPrivateProfileInt(
"audio",
"in1", 1, mINIPath.Get());
116 mState.mAudioInChanR = GetPrivateProfileInt(
"audio",
"in2", 2, mINIPath.Get());
117 mState.mAudioOutChanL = GetPrivateProfileInt(
"audio",
"out1", 1, mINIPath.Get());
118 mState.mAudioOutChanR = GetPrivateProfileInt(
"audio",
"out2", 2, mINIPath.Get());
121 mState.mBufferSize = GetPrivateProfileInt(
"audio",
"buffer", 512, mINIPath.Get());
122 mState.mAudioSR = GetPrivateProfileInt(
"audio",
"sr", 44100, mINIPath.Get());
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);
128 mState.mMidiInChan = GetPrivateProfileInt(
"midi",
"inchan", 0, mINIPath.Get());
129 mState.mMidiOutChan = GetPrivateProfileInt(
"midi",
"outchan", 0, mINIPath.Get());
139 CreateDirectory(mINIPath.Get(), NULL);
140 mINIPath.Append(
"settings.ini");
143 mode_t process_mask = umask(0);
144 int result_code = mkdir(mINIPath.Get(), S_IRWXU | S_IRWXG | S_IRWXO);
149 mINIPath.Append(
"\\settings.ini");
157 #error NOT IMPLEMENTED
164void IPlugAPPHost::UpdateINI()
167 const char* ini = mINIPath.Get();
169 sprintf(buf,
"%u", mState.mAudioDriverType);
170 WritePrivateProfileString(
"audio",
"driver", buf, ini);
172 WritePrivateProfileString(
"audio",
"indev", mState.mAudioInDev.Get(), ini);
173 WritePrivateProfileString(
"audio",
"outdev", mState.mAudioOutDev.Get(), ini);
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);
187 str.SetFormatted(32,
"%i", mState.mBufferSize);
188 WritePrivateProfileString(
"audio",
"buffer", str.Get(), ini);
190 str.SetFormatted(32,
"%i", mState.mAudioSR);
191 WritePrivateProfileString(
"audio",
"sr", str.Get(), ini);
193 WritePrivateProfileString(
"midi",
"indev", mState.mMidiInDev.Get(), ini);
194 WritePrivateProfileString(
"midi",
"outdev", mState.mMidiOutDev.Get(), ini);
196 sprintf(buf,
"%u", mState.mMidiInChan);
197 WritePrivateProfileString(
"midi",
"inchan", buf, ini);
198 sprintf(buf,
"%u", mState.mMidiOutChan);
199 WritePrivateProfileString(
"midi",
"outchan", buf, ini);
204 return mAudioIDDevNames.at(idx);
209 for(
int i = 0; i < mAudioIDDevNames.size(); i++)
211 if(!strcmp(deviceNameToTest, mAudioIDDevNames.at(i).c_str() ))
222 if(direction == ERoute::kInput)
224 if(!strcmp(nameToTest, OFF_TEXT))
return 0;
228 if(!strcmp(nameToTest,
"virtual input"))
return 1;
231 for (
int i = 0; i < mMidiIn->getPortCount(); i++)
233 if(!strcmp(nameToTest, mMidiIn->getPortName(i).c_str()))
239 if(!strcmp(nameToTest, OFF_TEXT))
return 0;
243 if(!strcmp(nameToTest,
"virtual output"))
return 1;
246 for (
int i = 0; i < mMidiOut->getPortCount(); i++)
248 if(!strcmp(nameToTest, mMidiOut->getPortName(i).c_str()))
258 std::cout <<
"\nRtAudio Version " << RtAudio::getVersion() << std::endl;
260 RtAudio::DeviceInfo info;
262 mAudioInputDevs.clear();
263 mAudioOutputDevs.clear();
264 mAudioIDDevNames.clear();
266 uint32_t nDevices = mDAC->getDeviceCount();
268 for (
int i=0; i<nDevices; i++)
270 info = mDAC->getDeviceInfo(i);
271 std::string deviceName = info.name;
274 size_t colonIdx = deviceName.rfind(
": ");
276 if(colonIdx != std::string::npos && deviceName.length() >= 2)
277 deviceName = deviceName.substr(colonIdx + 2, deviceName.length() - colonIdx - 2);
281 mAudioIDDevNames.push_back(deviceName);
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";
289 if(info.inputChannels > 0)
290 mAudioInputDevs.push_back(i);
292 if(info.outputChannels > 0)
293 mAudioOutputDevs.push_back(i);
295 if (info.isDefaultInput)
296 mDefaultInputDev = i;
298 if (info.isDefaultOutput)
299 mDefaultOutputDev = i;
304void IPlugAPPHost::ProbeMidiIO()
306 if ( !mMidiIn || !mMidiOut )
310 int nInputPorts = mMidiIn->getPortCount();
312 mMidiInputDevNames.push_back(OFF_TEXT);
315 mMidiInputDevNames.push_back(
"virtual input");
318 for (
int i=0; i<nInputPorts; i++ )
320 mMidiInputDevNames.push_back(mMidiIn->getPortName(i));
323 int nOutputPorts = mMidiOut->getPortCount();
325 mMidiOutputDevNames.push_back(OFF_TEXT);
328 mMidiOutputDevNames.push_back(
"virtual output");
331 for (
int i=0; i<nOutputPorts; i++ )
333 mMidiOutputDevNames.push_back(mMidiOut->getPortName(i));
339bool IPlugAPPHost::AudioSettingsInStateAreEqual(AppState& os, AppState& ns)
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;
355bool IPlugAPPHost::MIDISettingsInStateAreEqual(AppState& os, AppState& ns)
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;
365bool IPlugAPPHost::TryToChangeAudioDriverType()
375 if(mState.mAudioDriverType == kDeviceASIO)
376 mDAC = std::make_unique<RtAudio>(RtAudio::WINDOWS_ASIO);
378 mDAC = std::make_unique<RtAudio>(RtAudio::WINDOWS_DS);
380 if(mState.mAudioDriverType == kDeviceCoreAudio)
381 mDAC = std::make_unique<RtAudio>(RtAudio::MACOSX_CORE);
385 #error NOT IMPLEMENTED
394bool IPlugAPPHost::TryToChangeAudio()
400 if(mState.mAudioDriverType == kDeviceASIO)
407 #error NOT IMPLEMENTED
411 bool failedToFindDevice =
false;
412 bool resetToDefault =
false;
416 if (mDefaultInputDev > -1)
418 resetToDefault =
true;
419 inputID = mDefaultInputDev;
421 if (mAudioInputDevs.size())
425 failedToFindDevice =
true;
430 if (mDefaultOutputDev > -1)
432 resetToDefault =
true;
434 outputID = mDefaultOutputDev;
436 if (mAudioOutputDevs.size())
440 failedToFindDevice =
true;
445 DBGMSG(
"couldn't find previous audio device, reseting to default\n");
450 if (failedToFindDevice)
451 MessageBox(gHWND,
"Please check your soundcard settings in Preferences",
"Error", MB_OK);
453 if (inputID != -1 && outputID != -1)
455 return InitAudio(inputID, outputID, mState.mAudioSR, mState.mBufferSize);
461bool IPlugAPPHost::SelectMIDIDevice(
ERoute direction,
const char* pPortName)
465 if(direction == ERoute::kInput)
469 mState.mMidiInDev.Set(OFF_TEXT);
477 mMidiIn->closePort();
486 mMidiIn->openPort(port-1);
492 std::string virtualMidiInputName =
"To ";
493 virtualMidiInputName += BUNDLE_NAME;
494 mMidiIn->openVirtualPort(virtualMidiInputName);
499 mMidiIn->openPort(port-2);
503 #error NOT IMPLEMENTED
511 mState.mMidiOutDev.Set(OFF_TEXT);
519 mMidiOut->closePort();
526 mMidiOut->openPort(port-1);
532 std::string virtualMidiOutputName =
"From ";
533 virtualMidiOutputName += BUNDLE_NAME;
534 mMidiOut->openVirtualPort(virtualMidiOutputName);
539 mMidiOut->openPort(port-2);
543 #error NOT IMPLEMENTED
551void IPlugAPPHost::CloseAudio()
553 if (mDAC && mDAC->isStreamOpen())
555 if (mDAC->isStreamRunning())
566 catch (RtAudioError& e)
576bool IPlugAPPHost::InitAudio(uint32_t inId, uint32_t outId, uint32_t sr, uint32_t iovs)
580 RtAudio::StreamParameters iParams, oParams;
581 iParams.deviceId = inId;
582 iParams.nChannels = GetPlug()->
MaxNChannels(ERoute::kInput);
583 iParams.firstChannel = 0;
585 oParams.deviceId = outId;
586 oParams.nChannels = GetPlug()->
MaxNChannels(ERoute::kOutput);
587 oParams.firstChannel = 0;
591 DBGMSG(
"\ntrying to start audio stream @ %i sr, %i buffer size\nindev = %i:%s\noutdev = %i:%s\ninputs = %i\noutputs = %i\n",
594 RtAudio::StreamOptions options;
595 options.flags = RTAUDIO_NONINTERLEAVED;
600 mSampleRate = (double) sr;
602 mAudioEnding =
false;
605 mIPlug->SetBlockSize(APP_SIGNAL_VECTOR_SIZE);
606 mIPlug->SetSampleRate(mSampleRate);
611 mDAC->openStream(&oParams, iParams.nChannels > 0 ? &iParams :
nullptr, RTAUDIO_FLOAT64, sr, &mBufferSize, &AudioCallback,
this, &options );
613 for (
int i = 0; i < iParams.nChannels; i++)
615 mInputBufPtrs.Add(
nullptr);
618 for (
int i = 0; i < oParams.nChannels; i++)
620 mOutputBufPtrs.Add(
nullptr);
625 mActiveState = mState;
627 catch (RtAudioError& e)
636bool IPlugAPPHost::InitMidi()
640 mMidiIn = std::make_unique<RtMidiIn>();
642 catch (RtMidiError &error)
645 error.printMessage();
651 mMidiOut = std::make_unique<RtMidiOut>();
653 catch (RtMidiError &error)
656 error.printMessage();
660 mMidiIn->setCallback(&MIDICallback,
this);
661 mMidiIn->ignoreTypes(
false,
true,
false );
666void ApplyFades(
double *pBuffer,
int nChans,
int nFrames,
bool down)
668 for (
int i = 0; i < nChans; i++)
670 double *pIO = pBuffer + (i * nFrames);
674 for (
int j = 0; j < nFrames; j++)
675 pIO[j] *= ((
double) (nFrames - (j + 1)) / (double) nFrames);
679 for (
int j = 0; j < nFrames; j++)
680 pIO[j] *= ((
double) j / (double) nFrames);
686int IPlugAPPHost::AudioCallback(
void* pOutputBuffer,
void* pInputBuffer, uint32_t nFrames,
double streamTime, RtAudioStreamStatus status,
void* pUserData)
690 int nins = _this->GetPlug()->
MaxNChannels(ERoute::kInput);
691 int nouts = _this->GetPlug()->
MaxNChannels(ERoute::kOutput);
693 double* pInputBufferD =
static_cast<double*
>(pInputBuffer);
694 double* pOutputBufferD =
static_cast<double*
>(pOutputBuffer);
696 bool startWait = _this->mVecWait >= APP_N_VECTOR_WAIT;
697 bool doFade = _this->mVecWait == APP_N_VECTOR_WAIT || _this->mAudioEnding;
699 if (startWait && !_this->mAudioDone)
702 ApplyFades(pInputBufferD, nins, nFrames, _this->mAudioEnding);
704 for (
int i = 0; i < nFrames; i++)
706 _this->mBufIndex %= APP_SIGNAL_VECTOR_SIZE;
708 if (_this->mBufIndex == 0)
710 for (
int c = 0; c < nins; c++)
712 _this->mInputBufPtrs.Set(c, (pInputBufferD + (c * nFrames)) + i);
715 for (
int c = 0; c < nouts; c++)
717 _this->mOutputBufPtrs.Set(c, (pOutputBufferD + (c * nFrames)) + i);
720 _this->mIPlug->AppProcess(_this->mInputBufPtrs.GetList(), _this->mOutputBufPtrs.GetList(), APP_SIGNAL_VECTOR_SIZE);
722 _this->mSamplesElapsed += APP_SIGNAL_VECTOR_SIZE;
725 for (
int c = 0; c < nouts; c++)
727 pOutputBufferD[c * nFrames + i] *= APP_MULT;
734 ApplyFades(pOutputBufferD, nouts, nFrames, _this->mAudioEnding);
736 if (_this->mAudioEnding)
737 _this->mAudioDone =
true;
741 memset(pOutputBufferD, 0, nFrames * nouts *
sizeof(
double));
744 _this->mVecWait = std::min(_this->mVecWait + 1, uint32_t(APP_N_VECTOR_WAIT + 1));
750void IPlugAPPHost::MIDICallback(
double deltatime, std::vector<uint8_t>* pMsg,
void* pUserData)
754 if (pMsg->size() == 0 || _this->mExiting)
757 if (pMsg->size() > 3)
759 if(pMsg->size() > MAX_SYSEX_SIZE)
761 DBGMSG(
"SysEx message exceeds MAX_SYSEX_SIZE\n");
765 SysExData data { 0,
static_cast<int>(pMsg->size()), pMsg->data() };
767 _this->mIPlug->mSysExMsgsFromCallback.Push(data);
770 else if (pMsg->size())
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;
777 _this->mIPlug->mMidiMsgsFromCallback.Push(msg);
782void IPlugAPPHost::ErrorCallback(RtAudioError::Type type,
const std::string &errorText )
IPlug logging a.k.a tracing functionality.
A class that hosts an IPlug as a standalone app and provides Audio/Midi I/O.
void ProbeAudioIO()
find out which devices have input channels & which have output channels, add their ids to the lists
int GetMIDIPortNumber(ERoute direction, const char *name) const
int GetAudioDeviceIdx(const char *name) const
Returns the audio device index linked to a particular name.
std::string GetAudioDeviceName(int idx) const
Returns the name of the audio device at idx.
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.
This structure is used when queueing Sysex messages.