DumpPresetBlob MAX_BLOB_LENGTH 2048: TODO fix that nastyness
[wdl/wdl-ol.git] / WDL / IPlug / IPlugBase.cpp
blobe241bfab6773c36406af6a48f232bf10cbdee72b
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 for (int i = 0; i < nParams; ++i)
93 mParams.Add(new IParam);
96 for (int i = 0; i < nPresets; ++i)
98 mPresets.Add(new IPreset(i));
101 strcpy(mEffectName, effectName);
102 strcpy(mProductName, productName);
103 strcpy(mMfrName, mfrName);
105 int nInputs = 0, nOutputs = 0;
107 while (channelIOStr)
109 int nIn = 0, nOut = 0;
110 #ifndef NDEBUG
111 bool channelIOStrValid = sscanf(channelIOStr, "%d-%d", &nIn, &nOut) == 2;
112 assert(channelIOStrValid);
113 #else
114 sscanf(channelIOStr, "%d-%d", &nIn, &nOut);
115 #endif
116 nInputs = IPMAX(nInputs, nIn);
117 nOutputs = IPMAX(nOutputs, nOut);
118 mChannelIO.Add(new ChannelIO(nIn, nOut));
119 channelIOStr = strstr(channelIOStr, " ");
121 if (channelIOStr)
123 ++channelIOStr;
127 mInData.Resize(nInputs);
128 mOutData.Resize(nOutputs);
130 double** ppInData = mInData.Get();
132 for (int i = 0; i < nInputs; ++i, ++ppInData)
134 InChannel* pInChannel = new InChannel;
135 pInChannel->mConnected = false;
136 pInChannel->mSrc = ppInData;
137 mInChannels.Add(pInChannel);
140 double** ppOutData = mOutData.Get();
142 for (int i = 0; i < nOutputs; ++i, ++ppOutData)
144 OutChannel* pOutChannel = new OutChannel;
145 pOutChannel->mConnected = false;
146 pOutChannel->mDest = ppOutData;
147 pOutChannel->mFDest = 0;
148 mOutChannels.Add(pOutChannel);
152 IPlugBase::~IPlugBase()
154 TRACE;
155 #ifndef OS_IOS
156 DELETE_NULL(mGraphics);
157 #endif
158 mParams.Empty(true);
159 mPresets.Empty(true);
160 mInChannels.Empty(true);
161 mOutChannels.Empty(true);
162 mChannelIO.Empty(true);
163 mInputBusLabels.Empty(true);
164 mOutputBusLabels.Empty(true);
166 if (mDelay)
168 DELETE_NULL(mDelay);
172 int IPlugBase::GetHostVersion(bool decimal)
174 GetHost();
175 if (decimal)
177 return GetDecimalVersion(mHostVersion);
179 return mHostVersion;
182 void IPlugBase::GetHostVersionStr(char* str)
184 GetHost();
185 GetVersionStr(mHostVersion, str);
188 bool IPlugBase::LegalIO(int nIn, int nOut)
190 bool legal = false;
191 int i, n = mChannelIO.GetSize();
193 for (i = 0; i < n && !legal; ++i)
195 ChannelIO* pIO = mChannelIO.Get(i);
196 legal = ((nIn < 0 || nIn == pIO->mIn) && (nOut < 0 || nOut == pIO->mOut));
199 Trace(TRACELOC, "%d:%d:%s", nIn, nOut, (legal ? "legal" : "illegal"));
200 return legal;
203 void IPlugBase::LimitToStereoIO()
205 int nIn = NInChannels(), nOut = NOutChannels();
207 if (nIn > 2)
209 SetInputChannelConnections(2, nIn - 2, false);
212 if (nOut > 2)
214 SetOutputChannelConnections(2, nOut - 2, true);
218 void IPlugBase::SetHost(const char* host, int version)
220 mHost = LookUpHost(host);
221 mHostVersion = version;
223 char vStr[32];
224 GetVersionStr(version, vStr);
225 Trace(TRACELOC, "host_%sknown:%s:%s", (mHost == kHostUnknown ? "un" : ""), host, vStr);
227 #ifndef OS_IOS
228 void IPlugBase::AttachGraphics(IGraphics* pGraphics)
230 if (pGraphics)
232 WDL_MutexLock lock(&mMutex);
233 int i, n = mParams.GetSize();
235 for (i = 0; i < n; ++i)
237 pGraphics->SetParameterFromPlug(i, GetParam(i)->GetNormalized(), true);
240 pGraphics->PrepDraw();
241 mGraphics = pGraphics;
244 #endif
246 // Decimal = VVVVRRMM, otherwise 0xVVVVRRMM.
247 int IPlugBase::GetEffectVersion(bool decimal)
249 if (decimal)
251 return GetDecimalVersion(mVersion);
253 else
255 return mVersion;
259 void IPlugBase::GetEffectVersionStr(char* str)
261 GetVersionStr(mVersion, str);
262 #if defined _DEBUG
263 strcat(str, "D");
264 #elif defined TRACER_BUILD
265 strcat(str, "T");
266 #endif
269 const char* IPlugBase::GetAPIString()
271 switch (GetAPI())
273 case kAPIVST2: return "VST2";
274 case kAPIVST3: return "VST3";
275 case kAPIAU: return "AU";
276 case kAPIRTAS: return "RTAS";
277 case kAPIAAX: return "AAX";
278 case kAPISA: return "Standalone";
279 default: return "";
283 const char* IPlugBase::GetArchString()
285 #ifdef ARCH_64BIT
286 return "x64";
287 #else
288 return "x86";
289 #endif
292 double IPlugBase::GetSamplesPerBeat()
294 double tempo = GetTempo();
296 if (tempo > 0.0)
298 return GetSampleRate() * 60.0 / tempo;
301 return 0.0;
304 void IPlugBase::SetSampleRate(double sampleRate)
306 mSampleRate = sampleRate;
309 void IPlugBase::SetBlockSize(int blockSize)
311 if (blockSize != mBlockSize)
313 int i, nIn = NInChannels(), nOut = NOutChannels();
315 for (i = 0; i < nIn; ++i)
317 InChannel* pInChannel = mInChannels.Get(i);
318 pInChannel->mScratchBuf.Resize(blockSize);
319 memset(pInChannel->mScratchBuf.Get(), 0, blockSize * sizeof(double));
322 for (i = 0; i < nOut; ++i)
324 OutChannel* pOutChannel = mOutChannels.Get(i);
325 pOutChannel->mScratchBuf.Resize(blockSize);
326 memset(pOutChannel->mScratchBuf.Get(), 0, blockSize * sizeof(double));
329 mBlockSize = blockSize;
333 void IPlugBase::SetInputChannelConnections(int idx, int n, bool connected)
335 int iEnd = IPMIN(idx + n, mInChannels.GetSize());
337 for (int i = idx; i < iEnd; ++i)
339 InChannel* pInChannel = mInChannels.Get(i);
340 pInChannel->mConnected = connected;
342 if (!connected)
344 *(pInChannel->mSrc) = pInChannel->mScratchBuf.Get();
349 void IPlugBase::SetOutputChannelConnections(int idx, int n, bool connected)
351 int iEnd = IPMIN(idx + n, mOutChannels.GetSize());
353 for (int i = idx; i < iEnd; ++i)
355 OutChannel* pOutChannel = mOutChannels.Get(i);
356 pOutChannel->mConnected = connected;
358 if (!connected)
360 *(pOutChannel->mDest) = pOutChannel->mScratchBuf.Get();
365 bool IPlugBase::IsInChannelConnected(int chIdx)
367 return (chIdx < mInChannels.GetSize() && mInChannels.Get(chIdx)->mConnected);
370 bool IPlugBase::IsOutChannelConnected(int chIdx)
372 return (chIdx < mOutChannels.GetSize() && mOutChannels.Get(chIdx)->mConnected);
375 void IPlugBase::AttachInputBuffers(int idx, int n, double** ppData, int nFrames)
377 int iEnd = IPMIN(idx + n, mInChannels.GetSize());
379 for (int i = idx; i < iEnd; ++i)
381 InChannel* pInChannel = mInChannels.Get(i);
382 if (pInChannel->mConnected)
384 *(pInChannel->mSrc) = *(ppData++);
389 void IPlugBase::AttachInputBuffers(int idx, int n, float** ppData, int nFrames)
391 int iEnd = IPMIN(idx + n, mInChannels.GetSize());
392 for (int i = idx; i < iEnd; ++i)
394 InChannel* pInChannel = mInChannels.Get(i);
395 if (pInChannel->mConnected)
397 double* pScratch = pInChannel->mScratchBuf.Get();
398 CastCopy(pScratch, *(ppData++), nFrames);
399 *(pInChannel->mSrc) = pScratch;
404 void IPlugBase::AttachOutputBuffers(int idx, int n, double** ppData)
406 int iEnd = IPMIN(idx + n, mOutChannels.GetSize());
407 for (int i = idx; i < iEnd; ++i)
409 OutChannel* pOutChannel = mOutChannels.Get(i);
410 if (pOutChannel->mConnected)
412 *(pOutChannel->mDest) = *(ppData++);
417 void IPlugBase::AttachOutputBuffers(int idx, int n, float** ppData)
419 int iEnd = IPMIN(idx + n, mOutChannels.GetSize());
420 for (int i = idx; i < iEnd; ++i)
422 OutChannel* pOutChannel = mOutChannels.Get(i);
423 if (pOutChannel->mConnected)
425 *(pOutChannel->mDest) = pOutChannel->mScratchBuf.Get();
426 pOutChannel->mFDest = *(ppData++);
431 void IPlugBase::PassThroughBuffers(double sampleType, int nFrames)
433 if (mLatency && mDelay)
435 mDelay->ProcessBlock(mInData.Get(), mOutData.Get(), nFrames);
437 else
439 IPlugBase::ProcessDoubleReplacing(mInData.Get(), mOutData.Get(), nFrames);
443 void IPlugBase::PassThroughBuffers(float sampleType, int nFrames)
445 // for 32 bit buffers, first run the delay (if mLatency) on the 64bit IPlug buffers
446 PassThroughBuffers(0., nFrames);
448 int i, n = NOutChannels();
449 OutChannel** ppOutChannel = mOutChannels.GetList();
451 for (i = 0; i < n; ++i, ++ppOutChannel)
453 OutChannel* pOutChannel = *ppOutChannel;
454 if (pOutChannel->mConnected)
456 CastCopy(pOutChannel->mFDest, *(pOutChannel->mDest), nFrames);
461 void IPlugBase::ProcessBuffers(double sampleType, int nFrames)
463 ProcessDoubleReplacing(mInData.Get(), mOutData.Get(), nFrames);
466 void IPlugBase::ProcessBuffers(float sampleType, int nFrames)
468 ProcessDoubleReplacing(mInData.Get(), mOutData.Get(), nFrames);
469 int i, n = NOutChannels();
470 OutChannel** ppOutChannel = mOutChannels.GetList();
472 for (i = 0; i < n; ++i, ++ppOutChannel)
474 OutChannel* pOutChannel = *ppOutChannel;
476 if (pOutChannel->mConnected)
478 CastCopy(pOutChannel->mFDest, *(pOutChannel->mDest), nFrames);
483 void IPlugBase::ProcessBuffersAccumulating(float sampleType, int nFrames)
485 ProcessDoubleReplacing(mInData.Get(), mOutData.Get(), nFrames);
486 int i, n = NOutChannels();
487 OutChannel** ppOutChannel = mOutChannels.GetList();
489 for (i = 0; i < n; ++i, ++ppOutChannel)
491 OutChannel* pOutChannel = *ppOutChannel;
492 if (pOutChannel->mConnected)
494 float* pDest = pOutChannel->mFDest;
495 double* pSrc = *(pOutChannel->mDest);
497 for (int j = 0; j < nFrames; ++j, ++pDest, ++pSrc)
499 *pDest += (float) *pSrc;
505 void IPlugBase::ZeroScratchBuffers()
507 int i, nIn = NInChannels(), nOut = NOutChannels();
509 for (i = 0; i < nIn; ++i)
511 InChannel* pInChannel = mInChannels.Get(i);
512 memset(pInChannel->mScratchBuf.Get(), 0, mBlockSize * sizeof(double));
515 for (i = 0; i < nOut; ++i)
517 OutChannel* pOutChannel = mOutChannels.Get(i);
518 memset(pOutChannel->mScratchBuf.Get(), 0, mBlockSize * sizeof(double));
522 // If latency changes after initialization (often not supported by the host).
523 void IPlugBase::SetLatency(int samples)
525 mLatency = samples;
527 if (mDelay)
529 mDelay->SetDelayTime(mLatency);
533 // this is over-ridden for AAX
534 void IPlugBase::SetParameterFromGUI(int idx, double normalizedValue)
536 Trace(TRACELOC, "%d:%f", idx, normalizedValue);
537 WDL_MutexLock lock(&mMutex);
538 GetParam(idx)->SetNormalized(normalizedValue);
539 InformHostOfParamChange(idx, normalizedValue);
540 OnParamChange(idx);
543 void IPlugBase::OnParamReset()
545 for (int i = 0; i < mParams.GetSize(); ++i)
547 OnParamChange(i);
549 //Reset();
552 // Default passthrough.
553 void IPlugBase::ProcessDoubleReplacing(double** inputs, double** outputs, int nFrames)
555 // Mutex is already locked.
556 int i, nIn = mInChannels.GetSize(), nOut = mOutChannels.GetSize();
557 int j = 0;
558 for (i = 0; i < nOut; ++i)
560 if (i < nIn)
562 memcpy(outputs[i], inputs[i], nFrames * sizeof(double));
563 j++;
566 // zero remaining outs
567 for (/* same j */; j < nOut; ++j)
569 memset(outputs[j], 0, nFrames * sizeof(double));
573 // Default passthrough ONLY USED BY IOS.
574 void IPlugBase::ProcessSingleReplacing(float** inputs, float** outputs, int nFrames)
576 // Mutex is already locked.
577 int i, nIn = mInChannels.GetSize(), nOut = mOutChannels.GetSize();
578 for (i = 0; i < nIn; ++i)
580 memcpy(outputs[i], inputs[i], nFrames * sizeof(float));
582 for (/* same i */; i < nOut; ++i)
584 memset(outputs[i], 0, nFrames * sizeof(float));
588 // Default passthrough.
589 void IPlugBase::ProcessMidiMsg(IMidiMsg* pMsg)
591 SendMidiMsg(pMsg);
594 IPreset* GetNextUninitializedPreset(WDL_PtrList<IPreset>* pPresets)
596 int n = pPresets->GetSize();
597 for (int i = 0; i < n; ++i)
599 IPreset* pPreset = pPresets->Get(i);
600 if (!(pPreset->mInitialized))
602 return pPreset;
605 return 0;
608 void IPlugBase::MakeDefaultPreset(char* name, int nPresets)
610 for (int i = 0; i < nPresets; ++i)
612 IPreset* pPreset = GetNextUninitializedPreset(&mPresets);
613 if (pPreset)
615 pPreset->mInitialized = true;
616 strcpy(pPreset->mName, (name ? name : "Empty"));
617 SerializeState(&(pPreset->mChunk));
622 #define GET_PARAM_FROM_VARARG(paramType, vp, v) \
624 v = 0.0; \
625 switch (paramType) { \
626 case IParam::kTypeBool: \
627 case IParam::kTypeInt: \
628 case IParam::kTypeEnum: { \
629 v = (double) va_arg(vp, int); \
630 break; \
632 case IParam::kTypeDouble: \
633 default: { \
634 v = (double) va_arg(vp, double); \
635 break; \
640 void IPlugBase::MakePreset(char* name, ...)
642 IPreset* pPreset = GetNextUninitializedPreset(&mPresets);
643 if (pPreset)
645 pPreset->mInitialized = true;
646 strcpy(pPreset->mName, name);
648 int i, n = mParams.GetSize();
650 double v = 0.0;
651 va_list vp;
652 va_start(vp, name);
653 for (i = 0; i < n; ++i)
655 GET_PARAM_FROM_VARARG(GetParam(i)->Type(), vp, v);
656 pPreset->mChunk.Put(&v);
661 #define PARAM_UNINIT 99.99e-9
663 void IPlugBase::MakePresetFromNamedParams(char* name, int nParamsNamed, ...)
665 TRACE;
666 IPreset* pPreset = GetNextUninitializedPreset(&mPresets);
667 if (pPreset)
669 pPreset->mInitialized = true;
670 strcpy(pPreset->mName, name);
672 int i = 0, n = mParams.GetSize();
674 WDL_TypedBuf<double> vals;
675 vals.Resize(n);
676 double* pV = vals.Get();
677 for (i = 0; i < n; ++i, ++pV)
679 *pV = PARAM_UNINIT;
682 va_list vp;
683 va_start(vp, nParamsNamed);
684 for (int i = 0; i < nParamsNamed; ++i)
686 int paramIdx = (int) va_arg(vp, int);
687 // This assert will fire if any of the passed-in param values do not match
688 // the type that the param was initialized with (int for bool, int, enum; double for double).
689 assert(paramIdx >= 0 && paramIdx < n);
690 GET_PARAM_FROM_VARARG(GetParam(paramIdx)->Type(), vp, *(vals.Get() + paramIdx));
692 va_end(vp);
694 pV = vals.Get();
695 for (int i = 0; i < n; ++i, ++pV)
697 if (*pV == PARAM_UNINIT) // Any that weren't explicitly set, use the defaults.
699 *pV = GetParam(i)->Value();
701 pPreset->mChunk.Put(pV);
706 void IPlugBase::MakePresetFromChunk(char* name, ByteChunk* pChunk)
708 IPreset* pPreset = GetNextUninitializedPreset(&mPresets);
709 if (pPreset)
711 pPreset->mInitialized = true;
712 strcpy(pPreset->mName, name);
714 pPreset->mChunk.PutChunk(pChunk);
718 void IPlugBase::MakePresetFromBlob(char* name, const char* blob, int sizeOfChunk)
720 ByteChunk presetChunk;
721 presetChunk.Resize(sizeOfChunk);
722 base64decode(blob, presetChunk.GetBytes(), sizeOfChunk);
724 MakePresetFromChunk(name, &presetChunk);
727 #define DEFAULT_USER_PRESET_NAME "user preset"
729 void MakeDefaultUserPresetName(WDL_PtrList<IPreset>* pPresets, char* str)
731 int nDefaultNames = 0;
732 int n = pPresets->GetSize();
733 for (int i = 0; i < n; ++i)
735 IPreset* pPreset = pPresets->Get(i);
736 if (strstr(pPreset->mName, DEFAULT_USER_PRESET_NAME))
738 ++nDefaultNames;
741 sprintf(str, "%s %d", DEFAULT_USER_PRESET_NAME, nDefaultNames + 1);
744 void IPlugBase::EnsureDefaultPreset()
746 TRACE;
747 MakeDefaultPreset("Empty", mPresets.GetSize());
750 void IPlugBase::PruneUninitializedPresets()
752 TRACE;
753 int i = 0;
754 while (i < mPresets.GetSize())
756 IPreset* pPreset = mPresets.Get(i);
757 if (pPreset->mInitialized)
759 ++i;
761 else
763 mPresets.Delete(i, true);
768 bool IPlugBase::RestorePreset(int idx)
770 TRACE;
771 bool restoredOK = false;
772 if (idx >= 0 && idx < mPresets.GetSize())
774 IPreset* pPreset = mPresets.Get(idx);
776 if (!(pPreset->mInitialized))
778 pPreset->mInitialized = true;
779 MakeDefaultUserPresetName(&mPresets, pPreset->mName);
780 restoredOK = SerializeState(&(pPreset->mChunk));
782 else
784 restoredOK = (UnserializeState(&(pPreset->mChunk), 0) > 0);
787 if (restoredOK)
789 mCurrentPresetIdx = idx;
790 PresetsChangedByHost();
791 #ifndef OS_IOS
792 RedrawParamControls();
793 #endif
796 return restoredOK;
799 bool IPlugBase::RestorePreset(const char* name)
801 if (CSTR_NOT_EMPTY(name))
803 int n = mPresets.GetSize();
804 for (int i = 0; i < n; ++i)
806 IPreset* pPreset = mPresets.Get(i);
807 if (!strcmp(pPreset->mName, name))
809 return RestorePreset(i);
813 return false;
816 const char* IPlugBase::GetPresetName(int idx)
818 if (idx >= 0 && idx < mPresets.GetSize())
820 return mPresets.Get(idx)->mName;
822 return "";
825 void IPlugBase::ModifyCurrentPreset(const char* name)
827 if (mCurrentPresetIdx >= 0 && mCurrentPresetIdx < mPresets.GetSize())
829 IPreset* pPreset = mPresets.Get(mCurrentPresetIdx);
830 pPreset->mChunk.Clear();
832 Trace(TRACELOC, "%d %s", mCurrentPresetIdx, pPreset->mName);
834 SerializeState(&(pPreset->mChunk));
836 if (CSTR_NOT_EMPTY(name))
838 strcpy(pPreset->mName, name);
843 bool IPlugBase::SerializePresets(ByteChunk* pChunk)
845 TRACE;
846 bool savedOK = true;
847 int n = mPresets.GetSize();
848 for (int i = 0; i < n && savedOK; ++i)
850 IPreset* pPreset = mPresets.Get(i);
851 pChunk->PutStr(pPreset->mName);
853 Trace(TRACELOC, "%d %s", i, pPreset->mName);
855 pChunk->PutBool(pPreset->mInitialized);
856 if (pPreset->mInitialized)
858 savedOK &= (pChunk->PutChunk(&(pPreset->mChunk)) > 0);
861 return savedOK;
864 int IPlugBase::UnserializePresets(ByteChunk* pChunk, int startPos)
866 TRACE;
867 WDL_String name;
868 int n = mPresets.GetSize(), pos = startPos;
869 for (int i = 0; i < n && pos >= 0; ++i)
871 IPreset* pPreset = mPresets.Get(i);
872 pos = pChunk->GetStr(&name, pos);
873 strcpy(pPreset->mName, name.Get());
875 Trace(TRACELOC, "%d %s", i, pPreset->mName);
877 pos = pChunk->GetBool(&(pPreset->mInitialized), pos);
878 if (pPreset->mInitialized)
880 pos = UnserializeState(pChunk, pos);
881 if (pos > 0)
883 pPreset->mChunk.Clear();
884 SerializeState(&(pPreset->mChunk));
888 RestorePreset(mCurrentPresetIdx);
889 return pos;
892 bool IPlugBase::SerializeParams(ByteChunk* pChunk)
894 TRACE;
896 WDL_MutexLock lock(&mMutex);
897 bool savedOK = true;
898 int i, n = mParams.GetSize();
899 for (i = 0; i < n && savedOK; ++i)
901 IParam* pParam = mParams.Get(i);
902 Trace(TRACELOC, "%d %s %f", i, pParam->GetNameForHost(), pParam->Value());
903 double v = pParam->Value();
904 savedOK &= (pChunk->Put(&v) > 0);
906 return savedOK;
909 int IPlugBase::UnserializeParams(ByteChunk* pChunk, int startPos)
911 TRACE;
913 WDL_MutexLock lock(&mMutex);
914 int i, n = mParams.GetSize(), pos = startPos;
915 for (i = 0; i < n && pos >= 0; ++i)
917 IParam* pParam = mParams.Get(i);
918 double v = 0.0;
919 Trace(TRACELOC, "%d %s %f", i, pParam->GetNameForHost(), pParam->Value());
920 pos = pChunk->Get(&v, pos);
921 pParam->Set(v);
923 OnParamReset();
924 return pos;
927 bool IPlugBase::CompareState(const unsigned char* incomingState, int startPos)
929 bool isEqual = true;
931 const double* data = (const double*) incomingState + startPos;
933 // dirty hack here because protools treats param values as 32 bit int and in IPlug they are 64bit float
934 // if we memcmp() the incoming state with the current they may have tiny differences due to the quantization
935 for (int i = 0; i < NParams(); i++)
937 float v = (float) GetParam(i)->Value();
938 float vi = (float) *(data++);
940 isEqual &= (fabsf(v - vi) < 0.00001);
943 return isEqual;
946 #ifndef OS_IOS
947 void IPlugBase::RedrawParamControls()
949 if (mGraphics)
951 int i, n = mParams.GetSize();
952 for (i = 0; i < n; ++i)
954 double v = mParams.Get(i)->Value();
955 mGraphics->SetParameterFromPlug(i, v, false);
959 #endif
960 void IPlugBase::DirtyParameters()
962 WDL_MutexLock lock(&mMutex);
964 for (int p = 0; p < NParams(); p++)
966 double normalizedValue = GetParam(p)->GetNormalized();
967 InformHostOfParamChange(p, normalizedValue);
971 void IPlugBase::DumpPresetSrcCode(const char* filename, const char* paramEnumNames[])
973 // static bool sDumped = false;
974 bool sDumped = false;
976 if (!sDumped)
978 sDumped = true;
979 int i, n = NParams();
980 FILE* fp = fopen(filename, "w");
981 fprintf(fp, " MakePresetFromNamedParams(\"name\", %d", n);
982 for (i = 0; i < n; ++i)
984 IParam* pParam = GetParam(i);
985 char paramVal[32];
986 switch (pParam->Type())
988 case IParam::kTypeBool:
989 sprintf(paramVal, "%s", (pParam->Bool() ? "true" : "false"));
990 break;
991 case IParam::kTypeInt:
992 sprintf(paramVal, "%d", pParam->Int());
993 break;
994 case IParam::kTypeEnum:
995 sprintf(paramVal, "%d", pParam->Int());
996 break;
997 case IParam::kTypeDouble:
998 default:
999 sprintf(paramVal, "%.6f", pParam->Value());
1000 break;
1002 fprintf(fp, ",\n %s, %s", paramEnumNames[i], paramVal);
1004 fprintf(fp, ");\n");
1005 fclose(fp);
1009 #ifndef MAX_BLOB_LENGTH
1010 #define MAX_BLOB_LENGTH 2048
1011 #endif
1013 void IPlugBase::DumpPresetBlob(const char* filename)
1015 FILE* fp = fopen(filename, "w");
1016 fprintf(fp, "MakePresetFromBlob(\"name\", \"");
1018 char buf[MAX_BLOB_LENGTH];
1020 ByteChunk* pPresetChunk = &mPresets.Get(mCurrentPresetIdx)->mChunk;
1021 BYTE* byteStart = pPresetChunk->GetBytes();
1023 base64encode(byteStart, buf, pPresetChunk->Size());
1025 fprintf(fp, "%s\", %i);\n", buf, pPresetChunk->Size());
1026 fclose(fp);
1029 void IPlugBase::DumpBankBlob(const char* filename)
1031 FILE* fp = fopen(filename, "w");
1033 if (!fp)
1034 return;
1036 char buf[MAX_BLOB_LENGTH] = "";
1038 for (int i = 0; i< NPresets(); i++)
1040 IPreset* pPreset = mPresets.Get(i);
1041 fprintf(fp, "MakePresetFromBlob(\"%s\", \"", pPreset->mName);
1043 ByteChunk* pPresetChunk = &pPreset->mChunk;
1044 base64encode(pPresetChunk->GetBytes(), buf, pPresetChunk->Size());
1046 fprintf(fp, "%s\", %i);\n", buf, pPresetChunk->Size());
1049 fclose(fp);
1052 void IPlugBase::SetInputLabel(int idx, const char* pLabel)
1054 if (idx >= 0 && idx < NInChannels())
1056 mInChannels.Get(idx)->mLabel.Set(pLabel);
1060 void IPlugBase::SetOutputLabel(int idx, const char* pLabel)
1062 if (idx >= 0 && idx < NOutChannels())
1064 mOutChannels.Get(idx)->mLabel.Set(pLabel);
1068 void IPlugBase::SetInputBusLabel(int idx, const char* pLabel)
1070 if (idx >= 0 && idx < 2) // only possible to have two input buses
1072 if (mInputBusLabels.Get(idx))
1074 mInputBusLabels.Delete(idx, true);
1077 mInputBusLabels.Insert(idx, new WDL_String(pLabel, strlen(pLabel)));
1081 void IPlugBase::SetOutputBusLabel(int idx, const char* pLabel)
1083 if (idx >= 0)
1085 if (mOutputBusLabels.Get(idx))
1087 mOutputBusLabels.Delete(idx, true);
1090 mOutputBusLabels.Insert(idx, new WDL_String(pLabel, strlen(pLabel)));
1094 const int kFXPVersionNum = 1;
1095 const int kFXBVersionNum = 2;
1097 // confusing... bytechunk will force storage as little endian on big endian platforms,
1098 // so when we use it here, since vst fxp/fxb files are big endian, we need to swap the endianess
1099 // regardless of the endianness of the host, and on big endian hosts it will get swapped back to
1100 // big endian
1101 bool IPlugBase::SaveProgramAsFXP(WDL_String* fileName)
1103 if (fileName->GetLength())
1105 FILE* fp = fopen(fileName->Get(), "wb");
1107 ByteChunk pgm;
1109 VstInt32 chunkMagic = WDL_bswap32('CcnK');
1110 VstInt32 byteSize = 0;
1111 VstInt32 fxpMagic;
1112 VstInt32 fxpVersion = WDL_bswap32(kFXPVersionNum);
1113 VstInt32 pluginID = WDL_bswap32(GetUniqueID());
1114 VstInt32 pluginVersion = WDL_bswap32(GetEffectVersion(true));
1115 VstInt32 numParams = WDL_bswap32(NParams());
1116 char prgName[28];
1117 memset(prgName, 0, 28);
1118 strcpy(prgName, GetPresetName(GetCurrentPresetIdx()));
1120 pgm.Put(&chunkMagic);
1122 if (DoesStateChunks())
1124 ByteChunk state;
1125 VstInt32 chunkSize;
1127 fxpMagic = WDL_bswap32('FPCh');
1129 InitChunkWithIPlugVer(&state);
1130 SerializeState(&state);
1132 chunkSize = WDL_bswap32(state.Size());
1133 byteSize = WDL_bswap32(state.Size() + 60);
1135 pgm.Put(&byteSize);
1136 pgm.Put(&fxpMagic);
1137 pgm.Put(&fxpVersion);
1138 pgm.Put(&pluginID);
1139 pgm.Put(&pluginVersion);
1140 pgm.Put(&numParams);
1141 pgm.PutBytes(prgName, 28); // not PutStr (we want all 28 bytes)
1142 pgm.Put(&chunkSize);
1143 pgm.PutBytes(state.GetBytes(), state.Size());
1145 else
1147 fxpMagic = WDL_bswap32('FxCk');
1148 //byteSize = WDL_bswap32(20 + 28 + (NParams() * 4) );
1149 pgm.Put(&byteSize);
1150 pgm.Put(&fxpMagic);
1151 pgm.Put(&fxpVersion);
1152 pgm.Put(&pluginID);
1153 pgm.Put(&pluginVersion);
1154 pgm.Put(&numParams);
1155 pgm.PutBytes(prgName, 28); // not PutStr (we want all 28 bytes)
1157 for (int i = 0; i< NParams(); i++)
1159 WDL_EndianFloat v32;
1160 v32.f = (float) mParams.Get(i)->GetNormalized();
1161 unsigned int swapped = WDL_bswap32(v32.int32);
1162 pgm.Put(&swapped);
1166 fwrite(pgm.GetBytes(), pgm.Size(), 1, fp);
1167 fclose(fp);
1169 return true;
1171 return false;
1174 bool IPlugBase::SaveBankAsFXB(WDL_String* fileName)
1176 if (fileName->GetLength())
1178 FILE* fp = fopen(fileName->Get(), "wb");
1180 ByteChunk bnk;
1182 VstInt32 chunkMagic = WDL_bswap32('CcnK');
1183 VstInt32 byteSize = 0;
1184 VstInt32 fxbMagic;
1185 VstInt32 fxbVersion = WDL_bswap32(kFXBVersionNum);
1186 VstInt32 pluginID = WDL_bswap32(GetUniqueID());
1187 VstInt32 pluginVersion = WDL_bswap32(GetEffectVersion(true));
1188 VstInt32 numPgms = WDL_bswap32(NPresets());
1189 VstInt32 currentPgm = WDL_bswap32(GetCurrentPresetIdx());
1190 char future[124];
1191 memset(future, 0, 124);
1193 bnk.Put(&chunkMagic);
1195 if (DoesStateChunks())
1197 ByteChunk state;
1198 VstInt32 chunkSize;
1200 fxbMagic = WDL_bswap32('FBCh');
1202 InitChunkWithIPlugVer(&state);
1203 SerializePresets(&state);
1205 chunkSize = WDL_bswap32(state.Size());
1206 byteSize = WDL_bswap32(160 + state.Size() );
1208 bnk.Put(&byteSize);
1209 bnk.Put(&fxbMagic);
1210 bnk.Put(&fxbVersion);
1211 bnk.Put(&pluginID);
1212 bnk.Put(&pluginVersion);
1213 bnk.Put(&numPgms);
1214 bnk.Put(&currentPgm);
1215 bnk.PutBytes(&future, 124);
1217 bnk.Put(&chunkSize);
1218 bnk.PutBytes(state.GetBytes(), state.Size());
1220 else
1222 fxbMagic = WDL_bswap32('FxBk');
1224 bnk.Put(&byteSize);
1225 bnk.Put(&fxbMagic);
1226 bnk.Put(&fxbVersion);
1227 bnk.Put(&pluginID);
1228 bnk.Put(&pluginVersion);
1229 bnk.Put(&numPgms);
1230 bnk.Put(&currentPgm);
1231 bnk.PutBytes(&future, 124);
1233 VstInt32 fxpMagic = WDL_bswap32('FxCk');
1234 VstInt32 fxpVersion = WDL_bswap32(kFXPVersionNum);
1235 VstInt32 numParams = WDL_bswap32(NParams());
1237 for (int p = 0; p < NPresets(); p++)
1239 IPreset* pPreset = mPresets.Get(p);
1241 char prgName[28];
1242 memset(prgName, 0, 28);
1243 strcpy(prgName, pPreset->mName);
1245 bnk.Put(&chunkMagic);
1246 //byteSize = WDL_bswap32(20 + 28 + (NParams() * 4) );
1247 bnk.Put(&byteSize);
1248 bnk.Put(&fxpMagic);
1249 bnk.Put(&fxpVersion);
1250 bnk.Put(&pluginID);
1251 bnk.Put(&pluginVersion);
1252 bnk.Put(&numParams);
1253 bnk.PutBytes(prgName, 28);
1255 int pos = 0;
1257 for (int i = 0; i< NParams(); i++)
1259 double v = 0.0;
1260 pos = pPreset->mChunk.Get(&v, pos);
1262 WDL_EndianFloat v32;
1263 v32.f = (float) mParams.Get(i)->GetNormalized(v);
1264 unsigned int swapped = WDL_bswap32(v32.int32);
1265 bnk.Put(&swapped);
1270 fwrite(bnk.GetBytes(), bnk.Size(), 1, fp);
1271 fclose(fp);
1273 return true;
1275 else
1276 return false;
1279 bool IPlugBase::LoadProgramFromFXP(WDL_String* fileName)
1281 if (fileName->GetLength())
1283 FILE* fp = fopen(fileName->Get(), "rb");
1285 if (fp)
1287 ByteChunk pgm;
1288 long fileSize;
1290 fseek(fp , 0 , SEEK_END);
1291 fileSize = ftell(fp);
1292 rewind(fp);
1294 pgm.Resize(fileSize);
1295 fread(pgm.GetBytes(), fileSize, 1, fp);
1297 fclose(fp);
1299 int pos = 0;
1301 VstInt32 chunkMagic;
1302 VstInt32 byteSize = 0;
1303 VstInt32 fxpMagic;
1304 VstInt32 fxpVersion;
1305 VstInt32 pluginID;
1306 VstInt32 pluginVersion;
1307 VstInt32 numParams;
1308 char prgName[28];
1310 pos = pgm.Get(&chunkMagic, pos);
1311 chunkMagic = WDL_bswap_if_le(chunkMagic);
1312 pos = pgm.Get(&byteSize, pos);
1313 byteSize = WDL_bswap_if_le(byteSize);
1314 pos = pgm.Get(&fxpMagic, pos);
1315 fxpMagic = WDL_bswap_if_le(fxpMagic);
1316 pos = pgm.Get(&fxpVersion, pos);
1317 fxpVersion = WDL_bswap_if_le(fxpVersion);
1318 pos = pgm.Get(&pluginID, pos);
1319 pluginID = WDL_bswap_if_le(pluginID);
1320 pos = pgm.Get(&pluginVersion, pos);
1321 pluginVersion = WDL_bswap_if_le(pluginVersion);
1322 pos = pgm.Get(&numParams, pos);
1323 numParams = WDL_bswap_if_le(numParams);
1324 pos = pgm.GetBytes(prgName, 28, pos);
1326 if (chunkMagic != 'CcnK') return false;
1327 if (fxpVersion != kFXPVersionNum) return false; // TODO: what if a host saves as a different version?
1328 if (pluginID != GetUniqueID()) return false;
1329 //if (pluginVersion != GetEffectVersion(true)) return false; // TODO: provide mechanism for loading earlier versions
1330 //if (numParams != NParams()) return false; // TODO: provide mechanism for loading earlier versions with less params
1332 if (DoesStateChunks() && fxpMagic == 'FPCh')
1334 VstInt32 chunkSize;
1335 pos = pgm.Get(&chunkSize, pos);
1336 chunkSize = WDL_bswap_if_le(chunkSize);
1338 GetIPlugVerFromChunk(&pgm, &pos);
1339 UnserializeState(&pgm, pos);
1340 ModifyCurrentPreset(prgName);
1341 RestorePreset(GetCurrentPresetIdx());
1342 InformHostOfProgramChange();
1344 return true;
1346 else if (fxpMagic == 'FxCk')
1348 for (int i = 0; i< NParams(); i++)
1350 WDL_EndianFloat v32;
1351 pos = pgm.Get(&v32.int32, pos);
1352 v32.int32 = WDL_bswap_if_le(v32.int32);
1353 mParams.Get(i)->SetNormalized((double) v32.f);
1356 ModifyCurrentPreset(prgName);
1357 RestorePreset(GetCurrentPresetIdx());
1358 InformHostOfProgramChange();
1360 return true;
1365 return false;
1368 bool IPlugBase::LoadBankFromFXB(WDL_String* fileName)
1370 if (fileName->GetLength())
1372 FILE* fp = fopen(fileName->Get(), "rb");
1374 if (fp)
1376 ByteChunk bnk;
1377 long fileSize;
1379 fseek(fp , 0 , SEEK_END);
1380 fileSize = ftell(fp);
1381 rewind(fp);
1383 bnk.Resize(fileSize);
1384 fread(bnk.GetBytes(), fileSize, 1, fp);
1386 fclose(fp);
1388 int pos = 0;
1390 VstInt32 chunkMagic;
1391 VstInt32 byteSize = 0;
1392 VstInt32 fxbMagic;
1393 VstInt32 fxbVersion;
1394 VstInt32 pluginID;
1395 VstInt32 pluginVersion;
1396 VstInt32 numPgms;
1397 VstInt32 currentPgm;
1398 char future[124];
1399 memset(future, 0, 124);
1401 pos = bnk.Get(&chunkMagic, pos);
1402 chunkMagic = WDL_bswap_if_le(chunkMagic);
1403 pos = bnk.Get(&byteSize, pos);
1404 byteSize = WDL_bswap_if_le(byteSize);
1405 pos = bnk.Get(&fxbMagic, pos);
1406 fxbMagic = WDL_bswap_if_le(fxbMagic);
1407 pos = bnk.Get(&fxbVersion, pos);
1408 fxbVersion = WDL_bswap_if_le(fxbVersion);
1409 pos = bnk.Get(&pluginID, pos);
1410 pluginID = WDL_bswap_if_le(pluginID);
1411 pos = bnk.Get(&pluginVersion, pos);
1412 pluginVersion = WDL_bswap_if_le(pluginVersion);
1413 pos = bnk.Get(&numPgms, pos);
1414 numPgms = WDL_bswap_if_le(numPgms);
1415 pos = bnk.Get(&currentPgm, pos);
1416 currentPgm = WDL_bswap_if_le(currentPgm);
1417 pos = bnk.GetBytes(future, 124, pos);
1419 if (chunkMagic != 'CcnK') return false;
1420 //if (fxbVersion != kFXBVersionNum) return false; // TODO: what if a host saves as a different version?
1421 if (pluginID != GetUniqueID()) return false;
1422 //if (pluginVersion != GetEffectVersion(true)) return false; // TODO: provide mechanism for loading earlier versions
1423 //if (numPgms != NPresets()) return false; // TODO: provide mechanism for loading earlier versions with less params
1425 if (DoesStateChunks() && fxbMagic == 'FBCh')
1427 VstInt32 chunkSize;
1428 pos = bnk.Get(&chunkSize, pos);
1429 chunkSize = WDL_bswap_if_le(chunkSize);
1431 GetIPlugVerFromChunk(&bnk, &pos);
1432 UnserializePresets(&bnk, pos);
1433 //RestorePreset(currentPgm);
1434 InformHostOfProgramChange();
1435 return true;
1437 else if (fxbMagic == 'FxBk')
1439 VstInt32 chunkMagic;
1440 VstInt32 byteSize;
1441 VstInt32 fxpMagic;
1442 VstInt32 fxpVersion;
1443 VstInt32 pluginID;
1444 VstInt32 pluginVersion;
1445 VstInt32 numParams;
1446 char prgName[28];
1448 for(int i = 0; i<numPgms; i++)
1450 pos = bnk.Get(&chunkMagic, pos);
1451 chunkMagic = WDL_bswap_if_le(chunkMagic);
1453 pos = bnk.Get(&byteSize, pos);
1454 byteSize = WDL_bswap_if_le(byteSize);
1456 pos = bnk.Get(&fxpMagic, pos);
1457 fxpMagic = WDL_bswap_if_le(fxpMagic);
1459 pos = bnk.Get(&fxpVersion, pos);
1460 fxpVersion = WDL_bswap_if_le(fxpVersion);
1462 pos = bnk.Get(&pluginID, pos);
1463 pluginID = WDL_bswap_if_le(pluginID);
1465 pos = bnk.Get(&pluginVersion, pos);
1466 pluginVersion = WDL_bswap_if_le(pluginVersion);
1468 pos = bnk.Get(&numParams, pos);
1469 numParams = WDL_bswap_if_le(numParams);
1471 if (chunkMagic != 'CcnK') return false;
1472 if (fxpMagic != 'FxCk') return false;
1473 if (fxpVersion != kFXPVersionNum) return false;
1474 if (numParams != NParams()) return false;
1476 pos = bnk.GetBytes(prgName, 28, pos);
1478 RestorePreset(i);
1480 for (int j = 0; j< NParams(); j++)
1482 WDL_EndianFloat v32;
1483 pos = bnk.Get(&v32.int32, pos);
1484 v32.int32 = WDL_bswap_if_le(v32.int32);
1485 mParams.Get(j)->SetNormalized((double) v32.f);
1488 ModifyCurrentPreset(prgName);
1491 RestorePreset(currentPgm);
1492 InformHostOfProgramChange();
1494 return true;
1499 return false;
1502 void IPlugBase::InitChunkWithIPlugVer(ByteChunk* pChunk)
1504 pChunk->Clear();
1505 int magic = IPLUG_VERSION_MAGIC;
1506 pChunk->Put(&magic);
1507 int ver = IPLUG_VERSION;
1508 pChunk->Put(&ver);
1511 int IPlugBase::GetIPlugVerFromChunk(ByteChunk* pChunk, int* pPos)
1513 int magic = 0, ver = 0;
1514 int pos = pChunk->Get(&magic, *pPos);
1515 if (pos > *pPos && magic == IPLUG_VERSION_MAGIC)
1517 *pPos = pChunk->Get(&ver, pos);
1519 return ver;
1522 bool IPlugBase::SendMidiMsgs(WDL_TypedBuf<IMidiMsg>* pMsgs)
1524 bool rc = true;
1525 int n = pMsgs->GetSize();
1526 IMidiMsg* pMsg = pMsgs->Get();
1527 for (int i = 0; i < n; ++i, ++pMsg) {
1528 rc &= SendMidiMsg(pMsg);
1530 return rc;