[IPLUG/VST2/VST3/AU] added void IPlugBase::SetTailSize(unsigned int tailSizeSamples)
[wdl/wdl-ol.git] / WDL / IPlug / IPlugBase.cpp
blobc912016d69ceeb35c8fc31fd6f68f004c184acf3
1 #include "IPlugBase.h"
2 #ifndef OS_IOS
3 #include "IGraphics.h"
4 #include "IControl.h"
5 #endif
6 #include <math.h>
7 #include <stdio.h>
8 #include <time.h>
9 #include "../wdlendian.h"
10 #include "../base64encdec.h"
12 #ifndef VstInt32
13 #ifdef WIN32
14 typedef int VstInt32;
15 #else
16 #include <stdint.h>
17 typedef int32_t VstInt32;
18 #endif
19 #endif
21 const double DEFAULT_SAMPLE_RATE = 44100.0;
23 template <class SRC, class DEST>
24 void CastCopy(DEST* pDest, SRC* pSrc, int n)
26 for (int i = 0; i < n; ++i, ++pDest, ++pSrc)
28 *pDest = (DEST) *pSrc;
32 void GetVersionParts(int version, int* pVer, int* pMaj, int* pMin)
34 *pVer = (version & 0xFFFF0000) >> 16;
35 *pMaj = (version & 0x0000FF00) >> 8;
36 *pMin = version & 0x000000FF;
39 int GetDecimalVersion(int version)
41 int ver, rmaj, rmin;
42 GetVersionParts(version, &ver, &rmaj, &rmin);
43 return 10000 * ver + 100 * rmaj + rmin;
46 void GetVersionStr(int version, char* str)
48 int ver, rmaj, rmin;
49 GetVersionParts(version, &ver, &rmaj, &rmin);
50 sprintf(str, "v%d.%d.%d", ver, rmaj, rmin);
53 #ifndef MAX_PATH
54 #define MAX_PATH 1024
55 #endif
57 IPlugBase::IPlugBase(int nParams,
58 const char* channelIOStr,
59 int nPresets,
60 const char* effectName,
61 const char* productName,
62 const char* mfrName,
63 int vendorVersion,
64 int uniqueID,
65 int mfrID,
66 int latency,
67 bool plugDoesMidi,
68 bool plugDoesChunks,
69 bool plugIsInst,
70 EAPI plugAPI)
71 : mUniqueID(uniqueID)
72 , mMfrID(mfrID)
73 , mVersion(vendorVersion)
74 , mSampleRate(DEFAULT_SAMPLE_RATE)
75 , mBlockSize(0)
76 , mLatency(latency)
77 , mHost(kHostUninit)
78 , mHostVersion(0)
79 , mStateChunks(plugDoesChunks)
80 , mGraphics(0)
81 , mCurrentPresetIdx(0)
82 , mIsInst(plugIsInst)
83 , mDoesMIDI(plugDoesMidi)
84 , mAPI(plugAPI)
85 , mIsBypassed(false)
86 , mDelay(0)
87 , mTailSize(0)
89 Trace(TRACELOC, "%s:%s", effectName, CurrentTime());
91 mPreviousPath.Set("", MAX_PATH);
93 for (int i = 0; i < nParams; ++i)
95 mParams.Add(new IParam);
98 for (int i = 0; i < nPresets; ++i)
100 mPresets.Add(new IPreset(i));
103 strcpy(mEffectName, effectName);
104 strcpy(mProductName, productName);
105 strcpy(mMfrName, mfrName);
107 int nInputs = 0, nOutputs = 0;
109 while (channelIOStr)
111 int nIn = 0, nOut = 0;
112 #ifndef NDEBUG
113 bool channelIOStrValid = sscanf(channelIOStr, "%d-%d", &nIn, &nOut) == 2;
114 assert(channelIOStrValid);
115 #else
116 sscanf(channelIOStr, "%d-%d", &nIn, &nOut);
117 #endif
118 nInputs = IPMAX(nInputs, nIn);
119 nOutputs = IPMAX(nOutputs, nOut);
120 mChannelIO.Add(new ChannelIO(nIn, nOut));
121 channelIOStr = strstr(channelIOStr, " ");
123 if (channelIOStr)
125 ++channelIOStr;
129 mInData.Resize(nInputs);
130 mOutData.Resize(nOutputs);
132 double** ppInData = mInData.Get();
134 for (int i = 0; i < nInputs; ++i, ++ppInData)
136 InChannel* pInChannel = new InChannel;
137 pInChannel->mConnected = false;
138 pInChannel->mSrc = ppInData;
139 mInChannels.Add(pInChannel);
142 double** ppOutData = mOutData.Get();
144 for (int i = 0; i < nOutputs; ++i, ++ppOutData)
146 OutChannel* pOutChannel = new OutChannel;
147 pOutChannel->mConnected = false;
148 pOutChannel->mDest = ppOutData;
149 pOutChannel->mFDest = 0;
150 mOutChannels.Add(pOutChannel);
154 IPlugBase::~IPlugBase()
156 TRACE;
157 #ifndef OS_IOS
158 DELETE_NULL(mGraphics);
159 #endif
160 mParams.Empty(true);
161 mPresets.Empty(true);
162 mInChannels.Empty(true);
163 mOutChannels.Empty(true);
164 mChannelIO.Empty(true);
165 mInputBusLabels.Empty(true);
166 mOutputBusLabels.Empty(true);
168 if (mDelay)
170 DELETE_NULL(mDelay);
174 int IPlugBase::GetHostVersion(bool decimal)
176 GetHost();
177 if (decimal)
179 return GetDecimalVersion(mHostVersion);
181 return mHostVersion;
184 void IPlugBase::GetHostVersionStr(char* str)
186 GetHost();
187 GetVersionStr(mHostVersion, str);
190 bool IPlugBase::LegalIO(int nIn, int nOut)
192 bool legal = false;
193 int i, n = mChannelIO.GetSize();
195 for (i = 0; i < n && !legal; ++i)
197 ChannelIO* pIO = mChannelIO.Get(i);
198 legal = ((nIn < 0 || nIn == pIO->mIn) && (nOut < 0 || nOut == pIO->mOut));
201 Trace(TRACELOC, "%d:%d:%s", nIn, nOut, (legal ? "legal" : "illegal"));
202 return legal;
205 void IPlugBase::LimitToStereoIO()
207 int nIn = NInChannels(), nOut = NOutChannels();
209 if (nIn > 2)
211 SetInputChannelConnections(2, nIn - 2, false);
214 if (nOut > 2)
216 SetOutputChannelConnections(2, nOut - 2, true);
220 void IPlugBase::SetHost(const char* host, int version)
222 mHost = LookUpHost(host);
223 mHostVersion = version;
225 char vStr[32];
226 GetVersionStr(version, vStr);
227 Trace(TRACELOC, "host_%sknown:%s:%s", (mHost == kHostUnknown ? "un" : ""), host, vStr);
229 #ifndef OS_IOS
230 void IPlugBase::AttachGraphics(IGraphics* pGraphics)
232 if (pGraphics)
234 WDL_MutexLock lock(&mMutex);
235 int i, n = mParams.GetSize();
237 for (i = 0; i < n; ++i)
239 pGraphics->SetParameterFromPlug(i, GetParam(i)->GetNormalized(), true);
242 pGraphics->PrepDraw();
243 mGraphics = pGraphics;
246 #endif
248 // Decimal = VVVVRRMM, otherwise 0xVVVVRRMM.
249 int IPlugBase::GetEffectVersion(bool decimal)
251 if (decimal)
253 return GetDecimalVersion(mVersion);
255 else
257 return mVersion;
261 void IPlugBase::GetEffectVersionStr(char* str)
263 GetVersionStr(mVersion, str);
264 #if defined _DEBUG
265 strcat(str, "D");
266 #elif defined TRACER_BUILD
267 strcat(str, "T");
268 #endif
271 const char* IPlugBase::GetAPIString()
273 switch (GetAPI())
275 case kAPIVST2: return "VST2";
276 case kAPIVST3: return "VST3";
277 case kAPIAU: return "AU";
278 case kAPIRTAS: return "RTAS";
279 case kAPIAAX: return "AAX";
280 case kAPISA: return "Standalone";
281 default: return "";
285 const char* IPlugBase::GetArchString()
287 #ifdef ARCH_64BIT
288 return "x64";
289 #else
290 return "x86";
291 #endif
294 double IPlugBase::GetSamplesPerBeat()
296 double tempo = GetTempo();
298 if (tempo > 0.0)
300 return GetSampleRate() * 60.0 / tempo;
303 return 0.0;
306 void IPlugBase::SetSampleRate(double sampleRate)
308 mSampleRate = sampleRate;
311 void IPlugBase::SetBlockSize(int blockSize)
313 if (blockSize != mBlockSize)
315 int i, nIn = NInChannels(), nOut = NOutChannels();
317 for (i = 0; i < nIn; ++i)
319 InChannel* pInChannel = mInChannels.Get(i);
320 pInChannel->mScratchBuf.Resize(blockSize);
321 memset(pInChannel->mScratchBuf.Get(), 0, blockSize * sizeof(double));
324 for (i = 0; i < nOut; ++i)
326 OutChannel* pOutChannel = mOutChannels.Get(i);
327 pOutChannel->mScratchBuf.Resize(blockSize);
328 memset(pOutChannel->mScratchBuf.Get(), 0, blockSize * sizeof(double));
331 mBlockSize = blockSize;
335 void IPlugBase::SetInputChannelConnections(int idx, int n, bool connected)
337 int iEnd = IPMIN(idx + n, mInChannels.GetSize());
339 for (int i = idx; i < iEnd; ++i)
341 InChannel* pInChannel = mInChannels.Get(i);
342 pInChannel->mConnected = connected;
344 if (!connected)
346 *(pInChannel->mSrc) = pInChannel->mScratchBuf.Get();
351 void IPlugBase::SetOutputChannelConnections(int idx, int n, bool connected)
353 int iEnd = IPMIN(idx + n, mOutChannels.GetSize());
355 for (int i = idx; i < iEnd; ++i)
357 OutChannel* pOutChannel = mOutChannels.Get(i);
358 pOutChannel->mConnected = connected;
360 if (!connected)
362 *(pOutChannel->mDest) = pOutChannel->mScratchBuf.Get();
367 bool IPlugBase::IsInChannelConnected(int chIdx)
369 return (chIdx < mInChannels.GetSize() && mInChannels.Get(chIdx)->mConnected);
372 bool IPlugBase::IsOutChannelConnected(int chIdx)
374 return (chIdx < mOutChannels.GetSize() && mOutChannels.Get(chIdx)->mConnected);
377 void IPlugBase::AttachInputBuffers(int idx, int n, double** ppData, int nFrames)
379 int iEnd = IPMIN(idx + n, mInChannels.GetSize());
381 for (int i = idx; i < iEnd; ++i)
383 InChannel* pInChannel = mInChannels.Get(i);
384 if (pInChannel->mConnected)
386 *(pInChannel->mSrc) = *(ppData++);
391 void IPlugBase::AttachInputBuffers(int idx, int n, float** ppData, int nFrames)
393 int iEnd = IPMIN(idx + n, mInChannels.GetSize());
394 for (int i = idx; i < iEnd; ++i)
396 InChannel* pInChannel = mInChannels.Get(i);
397 if (pInChannel->mConnected)
399 double* pScratch = pInChannel->mScratchBuf.Get();
400 CastCopy(pScratch, *(ppData++), nFrames);
401 *(pInChannel->mSrc) = pScratch;
406 void IPlugBase::AttachOutputBuffers(int idx, int n, double** ppData)
408 int iEnd = IPMIN(idx + n, mOutChannels.GetSize());
409 for (int i = idx; i < iEnd; ++i)
411 OutChannel* pOutChannel = mOutChannels.Get(i);
412 if (pOutChannel->mConnected)
414 *(pOutChannel->mDest) = *(ppData++);
419 void IPlugBase::AttachOutputBuffers(int idx, int n, float** ppData)
421 int iEnd = IPMIN(idx + n, mOutChannels.GetSize());
422 for (int i = idx; i < iEnd; ++i)
424 OutChannel* pOutChannel = mOutChannels.Get(i);
425 if (pOutChannel->mConnected)
427 *(pOutChannel->mDest) = pOutChannel->mScratchBuf.Get();
428 pOutChannel->mFDest = *(ppData++);
433 void IPlugBase::PassThroughBuffers(double sampleType, int nFrames)
435 if (mLatency && mDelay)
437 mDelay->ProcessBlock(mInData.Get(), mOutData.Get(), nFrames);
439 else
441 IPlugBase::ProcessDoubleReplacing(mInData.Get(), mOutData.Get(), nFrames);
445 void IPlugBase::PassThroughBuffers(float sampleType, int nFrames)
447 // for 32 bit buffers, first run the delay (if mLatency) on the 64bit IPlug buffers
448 PassThroughBuffers(0., nFrames);
450 int i, n = NOutChannels();
451 OutChannel** ppOutChannel = mOutChannels.GetList();
453 for (i = 0; i < n; ++i, ++ppOutChannel)
455 OutChannel* pOutChannel = *ppOutChannel;
456 if (pOutChannel->mConnected)
458 CastCopy(pOutChannel->mFDest, *(pOutChannel->mDest), nFrames);
463 void IPlugBase::ProcessBuffers(double sampleType, int nFrames)
465 ProcessDoubleReplacing(mInData.Get(), mOutData.Get(), nFrames);
468 void IPlugBase::ProcessBuffers(float sampleType, int nFrames)
470 ProcessDoubleReplacing(mInData.Get(), mOutData.Get(), nFrames);
471 int i, n = NOutChannels();
472 OutChannel** ppOutChannel = mOutChannels.GetList();
474 for (i = 0; i < n; ++i, ++ppOutChannel)
476 OutChannel* pOutChannel = *ppOutChannel;
478 if (pOutChannel->mConnected)
480 CastCopy(pOutChannel->mFDest, *(pOutChannel->mDest), nFrames);
485 void IPlugBase::ProcessBuffersAccumulating(float sampleType, int nFrames)
487 ProcessDoubleReplacing(mInData.Get(), mOutData.Get(), nFrames);
488 int i, n = NOutChannels();
489 OutChannel** ppOutChannel = mOutChannels.GetList();
491 for (i = 0; i < n; ++i, ++ppOutChannel)
493 OutChannel* pOutChannel = *ppOutChannel;
494 if (pOutChannel->mConnected)
496 float* pDest = pOutChannel->mFDest;
497 double* pSrc = *(pOutChannel->mDest);
499 for (int j = 0; j < nFrames; ++j, ++pDest, ++pSrc)
501 *pDest += (float) *pSrc;
507 void IPlugBase::ZeroScratchBuffers()
509 int i, nIn = NInChannels(), nOut = NOutChannels();
511 for (i = 0; i < nIn; ++i)
513 InChannel* pInChannel = mInChannels.Get(i);
514 memset(pInChannel->mScratchBuf.Get(), 0, mBlockSize * sizeof(double));
517 for (i = 0; i < nOut; ++i)
519 OutChannel* pOutChannel = mOutChannels.Get(i);
520 memset(pOutChannel->mScratchBuf.Get(), 0, mBlockSize * sizeof(double));
524 // If latency changes after initialization (often not supported by the host).
525 void IPlugBase::SetLatency(int samples)
527 mLatency = samples;
529 if (mDelay)
531 mDelay->SetDelayTime(mLatency);
535 // this is over-ridden for AAX
536 void IPlugBase::SetParameterFromGUI(int idx, double normalizedValue)
538 Trace(TRACELOC, "%d:%f", idx, normalizedValue);
539 WDL_MutexLock lock(&mMutex);
540 GetParam(idx)->SetNormalized(normalizedValue);
541 InformHostOfParamChange(idx, normalizedValue);
542 OnParamChange(idx);
545 void IPlugBase::OnParamReset()
547 for (int i = 0; i < mParams.GetSize(); ++i)
549 OnParamChange(i);
551 //Reset();
554 // Default passthrough.
555 void IPlugBase::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
557 // Mutex is already locked.
558 int i, nIn = mInChannels.GetSize(), nOut = mOutChannels.GetSize();
559 int j = 0;
560 for (i = 0; i < nOut; ++i)
562 if (i < nIn)
564 memcpy(outputs[i], inputs[i], nFrames * sizeof(double));
565 j++;
568 // zero remaining outs
569 for (/* same j */; j < nOut; ++j)
571 memset(outputs[j], 0, nFrames * sizeof(double));
575 // Default passthrough ONLY USED BY IOS.
576 void IPlugBase::ProcessSingleReplacing(float** inputs, float** outputs, int nFrames)
578 // Mutex is already locked.
579 int i, nIn = mInChannels.GetSize(), nOut = mOutChannels.GetSize();
580 for (i = 0; i < nIn; ++i)
582 memcpy(outputs[i], inputs[i], nFrames * sizeof(float));
584 for (/* same i */; i < nOut; ++i)
586 memset(outputs[i], 0, nFrames * sizeof(float));
590 // Default passthrough.
591 void IPlugBase::ProcessMidiMsg(IMidiMsg* pMsg)
593 SendMidiMsg(pMsg);
596 IPreset* GetNextUninitializedPreset(WDL_PtrList<IPreset>* pPresets)
598 int n = pPresets->GetSize();
599 for (int i = 0; i < n; ++i)
601 IPreset* pPreset = pPresets->Get(i);
602 if (!(pPreset->mInitialized))
604 return pPreset;
607 return 0;
610 void IPlugBase::MakeDefaultPreset(char* name, int nPresets)
612 for (int i = 0; i < nPresets; ++i)
614 IPreset* pPreset = GetNextUninitializedPreset(&mPresets);
615 if (pPreset)
617 pPreset->mInitialized = true;
618 strcpy(pPreset->mName, (name ? name : "Empty"));
619 SerializeState(&(pPreset->mChunk));
624 #define GET_PARAM_FROM_VARARG(paramType, vp, v) \
626 v = 0.0; \
627 switch (paramType) { \
628 case IParam::kTypeBool: \
629 case IParam::kTypeInt: \
630 case IParam::kTypeEnum: { \
631 v = (double) va_arg(vp, int); \
632 break; \
634 case IParam::kTypeDouble: \
635 default: { \
636 v = (double) va_arg(vp, double); \
637 break; \
642 void IPlugBase::MakePreset(char* name, ...)
644 IPreset* pPreset = GetNextUninitializedPreset(&mPresets);
645 if (pPreset)
647 pPreset->mInitialized = true;
648 strcpy(pPreset->mName, name);
650 int i, n = mParams.GetSize();
652 double v = 0.0;
653 va_list vp;
654 va_start(vp, name);
655 for (i = 0; i < n; ++i)
657 GET_PARAM_FROM_VARARG(GetParam(i)->Type(), vp, v);
658 pPreset->mChunk.Put(&v);
663 #define PARAM_UNINIT 99.99e-9
665 void IPlugBase::MakePresetFromNamedParams(char* name, int nParamsNamed, ...)
667 TRACE;
668 IPreset* pPreset = GetNextUninitializedPreset(&mPresets);
669 if (pPreset)
671 pPreset->mInitialized = true;
672 strcpy(pPreset->mName, name);
674 int i = 0, n = mParams.GetSize();
676 WDL_TypedBuf<double> vals;
677 vals.Resize(n);
678 double* pV = vals.Get();
679 for (i = 0; i < n; ++i, ++pV)
681 *pV = PARAM_UNINIT;
684 va_list vp;
685 va_start(vp, nParamsNamed);
686 for (int i = 0; i < nParamsNamed; ++i)
688 int paramIdx = (int) va_arg(vp, int);
689 // This assert will fire if any of the passed-in param values do not match
690 // the type that the param was initialized with (int for bool, int, enum; double for double).
691 assert(paramIdx >= 0 && paramIdx < n);
692 GET_PARAM_FROM_VARARG(GetParam(paramIdx)->Type(), vp, *(vals.Get() + paramIdx));
694 va_end(vp);
696 pV = vals.Get();
697 for (int i = 0; i < n; ++i, ++pV)
699 if (*pV == PARAM_UNINIT) // Any that weren't explicitly set, use the defaults.
701 *pV = GetParam(i)->Value();
703 pPreset->mChunk.Put(pV);
708 void IPlugBase::MakePresetFromChunk(char* name, ByteChunk* pChunk)
710 IPreset* pPreset = GetNextUninitializedPreset(&mPresets);
711 if (pPreset)
713 pPreset->mInitialized = true;
714 strcpy(pPreset->mName, name);
716 pPreset->mChunk.PutChunk(pChunk);
720 void IPlugBase::MakePresetFromBlob(char* name, const char* blob, int sizeOfChunk)
722 ByteChunk presetChunk;
723 presetChunk.Resize(sizeOfChunk);
724 base64decode(blob, presetChunk.GetBytes(), sizeOfChunk);
726 MakePresetFromChunk(name, &presetChunk);
729 #define DEFAULT_USER_PRESET_NAME "user preset"
731 void MakeDefaultUserPresetName(WDL_PtrList<IPreset>* pPresets, char* str)
733 int nDefaultNames = 0;
734 int n = pPresets->GetSize();
735 for (int i = 0; i < n; ++i)
737 IPreset* pPreset = pPresets->Get(i);
738 if (strstr(pPreset->mName, DEFAULT_USER_PRESET_NAME))
740 ++nDefaultNames;
743 sprintf(str, "%s %d", DEFAULT_USER_PRESET_NAME, nDefaultNames + 1);
746 void IPlugBase::EnsureDefaultPreset()
748 TRACE;
749 MakeDefaultPreset("Empty", mPresets.GetSize());
752 void IPlugBase::PruneUninitializedPresets()
754 TRACE;
755 int i = 0;
756 while (i < mPresets.GetSize())
758 IPreset* pPreset = mPresets.Get(i);
759 if (pPreset->mInitialized)
761 ++i;
763 else
765 mPresets.Delete(i, true);
770 bool IPlugBase::RestorePreset(int idx)
772 TRACE;
773 bool restoredOK = false;
774 if (idx >= 0 && idx < mPresets.GetSize())
776 IPreset* pPreset = mPresets.Get(idx);
778 if (!(pPreset->mInitialized))
780 pPreset->mInitialized = true;
781 MakeDefaultUserPresetName(&mPresets, pPreset->mName);
782 restoredOK = SerializeState(&(pPreset->mChunk));
784 else
786 restoredOK = (UnserializeState(&(pPreset->mChunk), 0) > 0);
789 if (restoredOK)
791 mCurrentPresetIdx = idx;
792 PresetsChangedByHost();
793 #ifndef OS_IOS
794 RedrawParamControls();
795 #endif
798 return restoredOK;
801 bool IPlugBase::RestorePreset(const char* name)
803 if (CSTR_NOT_EMPTY(name))
805 int n = mPresets.GetSize();
806 for (int i = 0; i < n; ++i)
808 IPreset* pPreset = mPresets.Get(i);
809 if (!strcmp(pPreset->mName, name))
811 return RestorePreset(i);
815 return false;
818 const char* IPlugBase::GetPresetName(int idx)
820 if (idx >= 0 && idx < mPresets.GetSize())
822 return mPresets.Get(idx)->mName;
824 return "";
827 void IPlugBase::ModifyCurrentPreset(const char* name)
829 if (mCurrentPresetIdx >= 0 && mCurrentPresetIdx < mPresets.GetSize())
831 IPreset* pPreset = mPresets.Get(mCurrentPresetIdx);
832 pPreset->mChunk.Clear();
834 Trace(TRACELOC, "%d %s", mCurrentPresetIdx, pPreset->mName);
836 SerializeState(&(pPreset->mChunk));
838 if (CSTR_NOT_EMPTY(name))
840 strcpy(pPreset->mName, name);
845 bool IPlugBase::SerializePresets(ByteChunk* pChunk)
847 TRACE;
848 bool savedOK = true;
849 int n = mPresets.GetSize();
850 for (int i = 0; i < n && savedOK; ++i)
852 IPreset* pPreset = mPresets.Get(i);
853 pChunk->PutStr(pPreset->mName);
855 Trace(TRACELOC, "%d %s", i, pPreset->mName);
857 pChunk->PutBool(pPreset->mInitialized);
858 if (pPreset->mInitialized)
860 savedOK &= (pChunk->PutChunk(&(pPreset->mChunk)) > 0);
863 return savedOK;
866 int IPlugBase::UnserializePresets(ByteChunk* pChunk, int startPos)
868 TRACE;
869 WDL_String name;
870 int n = mPresets.GetSize(), pos = startPos;
871 for (int i = 0; i < n && pos >= 0; ++i)
873 IPreset* pPreset = mPresets.Get(i);
874 pos = pChunk->GetStr(&name, pos);
875 strcpy(pPreset->mName, name.Get());
877 Trace(TRACELOC, "%d %s", i, pPreset->mName);
879 pos = pChunk->GetBool(&(pPreset->mInitialized), pos);
880 if (pPreset->mInitialized)
882 pos = UnserializeState(pChunk, pos);
883 if (pos > 0)
885 pPreset->mChunk.Clear();
886 SerializeState(&(pPreset->mChunk));
890 RestorePreset(mCurrentPresetIdx);
891 return pos;
894 bool IPlugBase::SerializeParams(ByteChunk* pChunk)
896 TRACE;
898 WDL_MutexLock lock(&mMutex);
899 bool savedOK = true;
900 int i, n = mParams.GetSize();
901 for (i = 0; i < n && savedOK; ++i)
903 IParam* pParam = mParams.Get(i);
904 Trace(TRACELOC, "%d %s %f", i, pParam->GetNameForHost(), pParam->Value());
905 double v = pParam->Value();
906 savedOK &= (pChunk->Put(&v) > 0);
908 return savedOK;
911 int IPlugBase::UnserializeParams(ByteChunk* pChunk, int startPos)
913 TRACE;
915 WDL_MutexLock lock(&mMutex);
916 int i, n = mParams.GetSize(), pos = startPos;
917 for (i = 0; i < n && pos >= 0; ++i)
919 IParam* pParam = mParams.Get(i);
920 double v = 0.0;
921 Trace(TRACELOC, "%d %s %f", i, pParam->GetNameForHost(), pParam->Value());
922 pos = pChunk->Get(&v, pos);
923 pParam->Set(v);
925 OnParamReset();
926 return pos;
929 bool IPlugBase::CompareState(const unsigned char* incomingState, int startPos)
931 bool isEqual = true;
933 const double* data = (const double*) incomingState + startPos;
935 // dirty hack here because protools treats param values as 32 bit int and in IPlug they are 64bit float
936 // if we memcmp() the incoming state with the current they may have tiny differences due to the quantization
937 for (int i = 0; i < NParams(); i++)
939 float v = (float) GetParam(i)->Value();
940 float vi = (float) *(data++);
942 isEqual &= (fabsf(v - vi) < 0.00001);
945 return isEqual;
948 #ifndef OS_IOS
949 void IPlugBase::RedrawParamControls()
951 if (mGraphics)
953 int i, n = mParams.GetSize();
954 for (i = 0; i < n; ++i)
956 double v = mParams.Get(i)->Value();
957 mGraphics->SetParameterFromPlug(i, v, false);
961 #endif
962 void IPlugBase::DirtyParameters()
964 WDL_MutexLock lock(&mMutex);
966 for (int p = 0; p < NParams(); p++)
968 double normalizedValue = GetParam(p)->GetNormalized();
969 InformHostOfParamChange(p, normalizedValue);
973 void IPlugBase::DumpPresetSrcCode(const char* filename, const char* paramEnumNames[])
975 // static bool sDumped = false;
976 bool sDumped = false;
978 if (!sDumped)
980 sDumped = true;
981 int i, n = NParams();
982 FILE* fp = fopen(filename, "w");
983 fprintf(fp, " MakePresetFromNamedParams(\"name\", %d", n);
984 for (i = 0; i < n; ++i)
986 IParam* pParam = GetParam(i);
987 char paramVal[32];
988 switch (pParam->Type())
990 case IParam::kTypeBool:
991 sprintf(paramVal, "%s", (pParam->Bool() ? "true" : "false"));
992 break;
993 case IParam::kTypeInt:
994 sprintf(paramVal, "%d", pParam->Int());
995 break;
996 case IParam::kTypeEnum:
997 sprintf(paramVal, "%d", pParam->Int());
998 break;
999 case IParam::kTypeDouble:
1000 default:
1001 sprintf(paramVal, "%.6f", pParam->Value());
1002 break;
1004 fprintf(fp, ",\n %s, %s", paramEnumNames[i], paramVal);
1006 fprintf(fp, ");\n");
1007 fclose(fp);
1011 #ifndef MAX_BLOB_LENGTH
1012 #define MAX_BLOB_LENGTH 1024
1013 #endif
1015 void IPlugBase::DumpPresetBlob(const char* filename)
1017 FILE* fp = fopen(filename, "w");
1018 fprintf(fp, "MakePresetFromBlob(\"name\", \"");
1020 char buf[MAX_BLOB_LENGTH];
1022 ByteChunk* pPresetChunk = &mPresets.Get(mCurrentPresetIdx)->mChunk;
1023 BYTE* byteStart = pPresetChunk->GetBytes();
1025 base64encode(byteStart, buf, pPresetChunk->Size());
1027 fprintf(fp, "%s\", %i);\n", buf, pPresetChunk->Size());
1028 fclose(fp);
1031 void IPlugBase::SetInputLabel(int idx, const char* pLabel)
1033 if (idx >= 0 && idx < NInChannels())
1035 mInChannels.Get(idx)->mLabel.Set(pLabel);
1039 void IPlugBase::SetOutputLabel(int idx, const char* pLabel)
1041 if (idx >= 0 && idx < NOutChannels())
1043 mOutChannels.Get(idx)->mLabel.Set(pLabel);
1047 void IPlugBase::SetInputBusLabel(int idx, const char* pLabel)
1049 if (idx >= 0 && idx < 2) // only possible to have two input buses
1051 if (mInputBusLabels.Get(idx))
1053 mInputBusLabels.Delete(idx, true);
1056 mInputBusLabels.Insert(idx, new WDL_String(pLabel, strlen(pLabel)));
1060 void IPlugBase::SetOutputBusLabel(int idx, const char* pLabel)
1062 if (idx >= 0)
1064 if (mOutputBusLabels.Get(idx))
1066 mOutputBusLabels.Delete(idx, true);
1069 mOutputBusLabels.Insert(idx, new WDL_String(pLabel, strlen(pLabel)));
1073 const int kFXPVersionNum = 1;
1074 const int kFXBVersionNum = 2;
1076 // confusing... bytechunk will force storage as little endian on big endian platforms,
1077 // so when we use it here, since vst fxp/fxb files are big endian, we need to swap the endianess
1078 // regardless of the endianness of the host, and on big endian hosts it will get swapped back to
1079 // big endian
1080 #ifndef OS_IOS
1081 bool IPlugBase::SaveProgramAsFXP(const char* defaultFileName)
1083 if (mGraphics)
1085 WDL_String fileName(defaultFileName, strlen(defaultFileName));
1086 mGraphics->PromptForFile(&fileName, kFileSave, &mPreviousPath, "fxp");
1088 if (fileName.GetLength())
1090 FILE* fp = fopen(fileName.Get(), "wb");
1092 ByteChunk pgm;
1094 VstInt32 chunkMagic = WDL_bswap32('CcnK');
1095 VstInt32 byteSize = 0;
1096 VstInt32 fxpMagic;
1097 VstInt32 fxpVersion = WDL_bswap32(kFXPVersionNum);
1098 VstInt32 pluginID = WDL_bswap32(GetUniqueID());
1099 VstInt32 pluginVersion = WDL_bswap32(GetEffectVersion(true));
1100 VstInt32 numParams = WDL_bswap32(NParams());
1101 char prgName[28];
1102 memset(prgName, 0, 28);
1103 strcpy(prgName, GetPresetName(GetCurrentPresetIdx()));
1105 pgm.Put(&chunkMagic);
1107 if (DoesStateChunks())
1109 ByteChunk state;
1110 VstInt32 chunkSize;
1112 fxpMagic = WDL_bswap32('FPCh');
1114 InitChunkWithIPlugVer(&state);
1115 SerializeState(&state);
1117 chunkSize = WDL_bswap32(state.Size());
1118 byteSize = WDL_bswap32(state.Size() + 60);
1120 pgm.Put(&byteSize);
1121 pgm.Put(&fxpMagic);
1122 pgm.Put(&fxpVersion);
1123 pgm.Put(&pluginID);
1124 pgm.Put(&pluginVersion);
1125 pgm.Put(&numParams);
1126 pgm.PutBytes(prgName, 28); // not PutStr (we want all 28 bytes)
1127 pgm.Put(&chunkSize);
1128 pgm.PutBytes(state.GetBytes(), state.Size());
1130 else
1132 fxpMagic = WDL_bswap32('FxCk');
1133 //byteSize = WDL_bswap32(20 + 28 + (NParams() * 4) );
1134 pgm.Put(&byteSize);
1135 pgm.Put(&fxpMagic);
1136 pgm.Put(&fxpVersion);
1137 pgm.Put(&pluginID);
1138 pgm.Put(&pluginVersion);
1139 pgm.Put(&numParams);
1140 pgm.PutBytes(prgName, 28); // not PutStr (we want all 28 bytes)
1142 for (int i = 0; i< NParams(); i++)
1144 WDL_EndianFloat v32;
1145 v32.f = (float) mParams.Get(i)->GetNormalized();
1146 unsigned int swapped = WDL_bswap32(v32.int32);
1147 pgm.Put(&swapped);
1151 fwrite(pgm.GetBytes(), pgm.Size(), 1, fp);
1152 fclose(fp);
1154 return true;
1157 return false;
1160 bool IPlugBase::SaveBankAsFXB(const char* defaultFileName)
1162 if (mGraphics)
1164 WDL_String fileName(defaultFileName, strlen(defaultFileName));
1165 mGraphics->PromptForFile(&fileName, kFileSave, &mPreviousPath, "fxb");
1167 if (fileName.GetLength())
1169 FILE* fp = fopen(fileName.Get(), "wb");
1171 ByteChunk bnk;
1173 VstInt32 chunkMagic = WDL_bswap32('CcnK');
1174 VstInt32 byteSize = 0;
1175 VstInt32 fxbMagic;
1176 VstInt32 fxbVersion = WDL_bswap32(kFXBVersionNum);
1177 VstInt32 pluginID = WDL_bswap32(GetUniqueID());
1178 VstInt32 pluginVersion = WDL_bswap32(GetEffectVersion(true));
1179 VstInt32 numPgms = WDL_bswap32(NPresets());
1180 VstInt32 currentPgm = WDL_bswap32(GetCurrentPresetIdx());
1181 char future[124];
1182 memset(future, 0, 124);
1184 bnk.Put(&chunkMagic);
1186 if (DoesStateChunks())
1188 ByteChunk state;
1189 VstInt32 chunkSize;
1191 fxbMagic = WDL_bswap32('FBCh');
1193 InitChunkWithIPlugVer(&state);
1194 SerializePresets(&state);
1196 chunkSize = WDL_bswap32(state.Size());
1197 byteSize = WDL_bswap32(160 + state.Size() );
1199 bnk.Put(&byteSize);
1200 bnk.Put(&fxbMagic);
1201 bnk.Put(&fxbVersion);
1202 bnk.Put(&pluginID);
1203 bnk.Put(&pluginVersion);
1204 bnk.Put(&numPgms);
1205 bnk.Put(&currentPgm);
1206 bnk.PutBytes(&future, 124);
1208 bnk.Put(&chunkSize);
1209 bnk.PutBytes(state.GetBytes(), state.Size());
1211 else
1213 fxbMagic = WDL_bswap32('FxBk');
1215 bnk.Put(&byteSize);
1216 bnk.Put(&fxbMagic);
1217 bnk.Put(&fxbVersion);
1218 bnk.Put(&pluginID);
1219 bnk.Put(&pluginVersion);
1220 bnk.Put(&numPgms);
1221 bnk.Put(&currentPgm);
1222 bnk.PutBytes(&future, 124);
1224 VstInt32 fxpMagic = WDL_bswap32('FxCk');
1225 VstInt32 fxpVersion = WDL_bswap32(kFXPVersionNum);
1226 VstInt32 numParams = WDL_bswap32(NParams());
1228 for (int p = 0; p < NPresets(); p++)
1230 IPreset* pPreset = mPresets.Get(p);
1232 char prgName[28];
1233 memset(prgName, 0, 28);
1234 strcpy(prgName, pPreset->mName);
1236 bnk.Put(&chunkMagic);
1237 //byteSize = WDL_bswap32(20 + 28 + (NParams() * 4) );
1238 bnk.Put(&byteSize);
1239 bnk.Put(&fxpMagic);
1240 bnk.Put(&fxpVersion);
1241 bnk.Put(&pluginID);
1242 bnk.Put(&pluginVersion);
1243 bnk.Put(&numParams);
1244 bnk.PutBytes(prgName, 28);
1246 int pos = 0;
1248 for (int i = 0; i< NParams(); i++)
1250 double v = 0.0;
1251 pos = pPreset->mChunk.Get(&v, pos);
1253 WDL_EndianFloat v32;
1254 v32.f = (float) mParams.Get(i)->GetNormalized(v);
1255 unsigned int swapped = WDL_bswap32(v32.int32);
1256 bnk.Put(&swapped);
1261 fwrite(bnk.GetBytes(), bnk.Size(), 1, fp);
1262 fclose(fp);
1264 return true;
1267 return false;
1270 bool IPlugBase::LoadProgramFromFXP()
1272 if (mGraphics)
1274 WDL_String fileName;
1275 mGraphics->PromptForFile(&fileName, kFileOpen, &mPreviousPath, "fxp");
1277 if (fileName.GetLength())
1279 FILE* fp = fopen(fileName.Get(), "rb");
1281 if (fp)
1283 ByteChunk pgm;
1284 long fileSize;
1286 fseek(fp , 0 , SEEK_END);
1287 fileSize = ftell(fp);
1288 rewind(fp);
1290 pgm.Resize(fileSize);
1291 fread(pgm.GetBytes(), fileSize, 1, fp);
1293 fclose(fp);
1295 int pos = 0;
1297 VstInt32 chunkMagic;
1298 VstInt32 byteSize = 0;
1299 VstInt32 fxpMagic;
1300 VstInt32 fxpVersion;
1301 VstInt32 pluginID;
1302 VstInt32 pluginVersion;
1303 VstInt32 numParams;
1304 char prgName[28];
1306 pos = pgm.Get(&chunkMagic, pos);
1307 chunkMagic = WDL_bswap_if_le(chunkMagic);
1308 pos = pgm.Get(&byteSize, pos);
1309 byteSize = WDL_bswap_if_le(byteSize);
1310 pos = pgm.Get(&fxpMagic, pos);
1311 fxpMagic = WDL_bswap_if_le(fxpMagic);
1312 pos = pgm.Get(&fxpVersion, pos);
1313 fxpVersion = WDL_bswap_if_le(fxpVersion);
1314 pos = pgm.Get(&pluginID, pos);
1315 pluginID = WDL_bswap_if_le(pluginID);
1316 pos = pgm.Get(&pluginVersion, pos);
1317 pluginVersion = WDL_bswap_if_le(pluginVersion);
1318 pos = pgm.Get(&numParams, pos);
1319 numParams = WDL_bswap_if_le(numParams);
1320 pos = pgm.GetBytes(prgName, 28, pos);
1322 if (chunkMagic != 'CcnK') return false;
1323 if (fxpVersion != kFXPVersionNum) return false; // TODO: what if a host saves as a different version?
1324 if (pluginID != GetUniqueID()) return false;
1325 //if (pluginVersion != GetEffectVersion(true)) return false; // TODO: provide mechanism for loading earlier versions
1326 if (numParams != NParams()) return false; // TODO: provide mechanism for loading earlier versions with less params
1328 if (DoesStateChunks() && fxpMagic == 'FPCh')
1330 VstInt32 chunkSize;
1331 pos = pgm.Get(&chunkSize, pos);
1332 chunkSize = WDL_bswap_if_le(chunkSize);
1334 GetIPlugVerFromChunk(&pgm, &pos);
1335 UnserializeState(&pgm, pos);
1336 ModifyCurrentPreset(prgName);
1337 RestorePreset(GetCurrentPresetIdx());
1338 InformHostOfProgramChange();
1340 return true;
1342 else if (fxpMagic == 'FxCk')
1344 for (int i = 0; i< NParams(); i++)
1346 WDL_EndianFloat v32;
1347 pos = pgm.Get(&v32.int32, pos);
1348 v32.int32 = WDL_bswap_if_le(v32.int32);
1349 mParams.Get(i)->SetNormalized((double) v32.f);
1352 ModifyCurrentPreset(prgName);
1353 RestorePreset(GetCurrentPresetIdx());
1354 InformHostOfProgramChange();
1356 return true;
1361 return false;
1364 bool IPlugBase::LoadBankFromFXB()
1366 if (mGraphics)
1368 WDL_String fileName;
1369 mGraphics->PromptForFile(&fileName, kFileOpen, &mPreviousPath, "fxb");
1371 if (fileName.GetLength())
1373 FILE* fp = fopen(fileName.Get(), "rb");
1375 if (fp)
1377 ByteChunk bnk;
1378 long fileSize;
1380 fseek(fp , 0 , SEEK_END);
1381 fileSize = ftell(fp);
1382 rewind(fp);
1384 bnk.Resize(fileSize);
1385 fread(bnk.GetBytes(), fileSize, 1, fp);
1387 fclose(fp);
1389 int pos = 0;
1391 VstInt32 chunkMagic;
1392 VstInt32 byteSize = 0;
1393 VstInt32 fxbMagic;
1394 VstInt32 fxbVersion;
1395 VstInt32 pluginID;
1396 VstInt32 pluginVersion;
1397 VstInt32 numPgms;
1398 VstInt32 currentPgm;
1399 char future[124];
1400 memset(future, 0, 124);
1402 pos = bnk.Get(&chunkMagic, pos);
1403 chunkMagic = WDL_bswap_if_le(chunkMagic);
1404 pos = bnk.Get(&byteSize, pos);
1405 byteSize = WDL_bswap_if_le(byteSize);
1406 pos = bnk.Get(&fxbMagic, pos);
1407 fxbMagic = WDL_bswap_if_le(fxbMagic);
1408 pos = bnk.Get(&fxbVersion, pos);
1409 fxbVersion = WDL_bswap_if_le(fxbVersion);
1410 pos = bnk.Get(&pluginID, pos);
1411 pluginID = WDL_bswap_if_le(pluginID);
1412 pos = bnk.Get(&pluginVersion, pos);
1413 pluginVersion = WDL_bswap_if_le(pluginVersion);
1414 pos = bnk.Get(&numPgms, pos);
1415 numPgms = WDL_bswap_if_le(numPgms);
1416 pos = bnk.Get(&currentPgm, pos);
1417 currentPgm = WDL_bswap_if_le(currentPgm);
1418 pos = bnk.GetBytes(future, 124, pos);
1420 if (chunkMagic != 'CcnK') return false;
1421 //if (fxbVersion != kFXBVersionNum) return false; // TODO: what if a host saves as a different version?
1422 if (pluginID != GetUniqueID()) return false;
1423 //if (pluginVersion != GetEffectVersion(true)) return false; // TODO: provide mechanism for loading earlier versions
1424 //if (numPgms != NPresets()) return false; // TODO: provide mechanism for loading earlier versions with less params
1426 if (DoesStateChunks() && fxbMagic == 'FBCh')
1428 VstInt32 chunkSize;
1429 pos = bnk.Get(&chunkSize, pos);
1430 chunkSize = WDL_bswap_if_le(chunkSize);
1432 GetIPlugVerFromChunk(&bnk, &pos);
1433 UnserializePresets(&bnk, pos);
1434 //RestorePreset(currentPgm);
1435 InformHostOfProgramChange();
1436 return true;
1438 else if (fxbMagic == 'FxBk')
1440 VstInt32 chunkMagic;
1441 VstInt32 byteSize;
1442 VstInt32 fxpMagic;
1443 VstInt32 fxpVersion;
1444 VstInt32 pluginID;
1445 VstInt32 pluginVersion;
1446 VstInt32 numParams;
1447 char prgName[28];
1449 for(int i = 0; i<numPgms; i++)
1451 pos = bnk.Get(&chunkMagic, pos);
1452 chunkMagic = WDL_bswap_if_le(chunkMagic);
1454 pos = bnk.Get(&byteSize, pos);
1455 byteSize = WDL_bswap_if_le(byteSize);
1457 pos = bnk.Get(&fxpMagic, pos);
1458 fxpMagic = WDL_bswap_if_le(fxpMagic);
1460 pos = bnk.Get(&fxpVersion, pos);
1461 fxpVersion = WDL_bswap_if_le(fxpVersion);
1463 pos = bnk.Get(&pluginID, pos);
1464 pluginID = WDL_bswap_if_le(pluginID);
1466 pos = bnk.Get(&pluginVersion, pos);
1467 pluginVersion = WDL_bswap_if_le(pluginVersion);
1469 pos = bnk.Get(&numParams, pos);
1470 numParams = WDL_bswap_if_le(numParams);
1472 if (chunkMagic != 'CcnK') return false;
1473 if (fxpMagic != 'FxCk') return false;
1474 if (fxpVersion != kFXPVersionNum) return false;
1475 if (numParams != NParams()) return false;
1477 pos = bnk.GetBytes(prgName, 28, pos);
1479 RestorePreset(i);
1481 for (int j = 0; j< NParams(); j++)
1483 WDL_EndianFloat v32;
1484 pos = bnk.Get(&v32.int32, pos);
1485 v32.int32 = WDL_bswap_if_le(v32.int32);
1486 mParams.Get(j)->SetNormalized((double) v32.f);
1489 ModifyCurrentPreset(prgName);
1492 RestorePreset(currentPgm);
1493 InformHostOfProgramChange();
1495 return true;
1500 return false;
1503 #endif
1505 void IPlugBase::InitChunkWithIPlugVer(ByteChunk* pChunk)
1507 pChunk->Clear();
1508 int magic = IPLUG_VERSION_MAGIC;
1509 pChunk->Put(&magic);
1510 int ver = IPLUG_VERSION;
1511 pChunk->Put(&ver);
1514 int IPlugBase::GetIPlugVerFromChunk(ByteChunk* pChunk, int* pPos)
1516 int magic = 0, ver = 0;
1517 int pos = pChunk->Get(&magic, *pPos);
1518 if (pos > *pPos && magic == IPLUG_VERSION_MAGIC)
1520 *pPos = pChunk->Get(&ver, pos);
1522 return ver;
1525 bool IPlugBase::SendMidiMsgs(WDL_TypedBuf<IMidiMsg>* pMsgs)
1527 bool rc = true;
1528 int n = pMsgs->GetSize();
1529 IMidiMsg* pMsg = pMsgs->Get();
1530 for (int i = 0; i < n; ++i, ++pMsg) {
1531 rc &= SendMidiMsg(pMsg);
1533 return rc;