9 #include "../wdlendian.h"
10 #include "../base64encdec.h"
17 typedef int32_t VstInt32
;
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
)
42 GetVersionParts(version
, &ver
, &rmaj
, &rmin
);
43 return 10000 * ver
+ 100 * rmaj
+ rmin
;
46 void GetVersionStr(int version
, char* str
)
49 GetVersionParts(version
, &ver
, &rmaj
, &rmin
);
50 sprintf(str
, "v%d.%d.%d", ver
, rmaj
, rmin
);
57 IPlugBase::IPlugBase(int nParams
,
58 const char* channelIOStr
,
60 const char* effectName
,
61 const char* productName
,
73 , mVersion(vendorVersion
)
74 , mSampleRate(DEFAULT_SAMPLE_RATE
)
79 , mStateChunks(plugDoesChunks
)
81 , mCurrentPresetIdx(0)
83 , mDoesMIDI(plugDoesMidi
)
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;
111 int nIn
= 0, nOut
= 0;
113 bool channelIOStrValid
= sscanf(channelIOStr
, "%d-%d", &nIn
, &nOut
) == 2;
114 assert(channelIOStrValid
);
116 sscanf(channelIOStr
, "%d-%d", &nIn
, &nOut
);
118 nInputs
= IPMAX(nInputs
, nIn
);
119 nOutputs
= IPMAX(nOutputs
, nOut
);
120 mChannelIO
.Add(new ChannelIO(nIn
, nOut
));
121 channelIOStr
= strstr(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()
158 DELETE_NULL(mGraphics
);
161 mPresets
.Empty(true);
162 mInChannels
.Empty(true);
163 mOutChannels
.Empty(true);
164 mChannelIO
.Empty(true);
165 mInputBusLabels
.Empty(true);
166 mOutputBusLabels
.Empty(true);
174 int IPlugBase::GetHostVersion(bool decimal
)
179 return GetDecimalVersion(mHostVersion
);
184 void IPlugBase::GetHostVersionStr(char* str
)
187 GetVersionStr(mHostVersion
, str
);
190 bool IPlugBase::LegalIO(int nIn
, int nOut
)
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"));
205 void IPlugBase::LimitToStereoIO()
207 int nIn
= NInChannels(), nOut
= NOutChannels();
211 SetInputChannelConnections(2, nIn
- 2, false);
216 SetOutputChannelConnections(2, nOut
- 2, true);
220 void IPlugBase::SetHost(const char* host
, int version
)
222 mHost
= LookUpHost(host
);
223 mHostVersion
= version
;
226 GetVersionStr(version
, vStr
);
227 Trace(TRACELOC
, "host_%sknown:%s:%s", (mHost
== kHostUnknown
? "un" : ""), host
, vStr
);
230 void IPlugBase::AttachGraphics(IGraphics
* 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
;
248 // Decimal = VVVVRRMM, otherwise 0xVVVVRRMM.
249 int IPlugBase::GetEffectVersion(bool decimal
)
253 return GetDecimalVersion(mVersion
);
261 void IPlugBase::GetEffectVersionStr(char* str
)
263 GetVersionStr(mVersion
, str
);
266 #elif defined TRACER_BUILD
271 const char* IPlugBase::GetAPIString()
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";
285 const char* IPlugBase::GetArchString()
294 double IPlugBase::GetSamplesPerBeat()
296 double tempo
= GetTempo();
300 return GetSampleRate() * 60.0 / tempo
;
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
;
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
;
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
);
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
)
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
);
545 void IPlugBase::OnParamReset()
547 for (int i
= 0; i
< mParams
.GetSize(); ++i
)
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();
560 for (i
= 0; i
< nOut
; ++i
)
564 memcpy(outputs
[i
], inputs
[i
], nFrames
* sizeof(double));
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
)
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
))
610 void IPlugBase::MakeDefaultPreset(char* name
, int nPresets
)
612 for (int i
= 0; i
< nPresets
; ++i
)
614 IPreset
* pPreset
= GetNextUninitializedPreset(&mPresets
);
617 pPreset
->mInitialized
= true;
618 strcpy(pPreset
->mName
, (name
? name
: "Empty"));
619 SerializeState(&(pPreset
->mChunk
));
624 #define GET_PARAM_FROM_VARARG(paramType, vp, v) \
627 switch (paramType) { \
628 case IParam::kTypeBool: \
629 case IParam::kTypeInt: \
630 case IParam::kTypeEnum: { \
631 v = (double) va_arg(vp, int); \
634 case IParam::kTypeDouble: \
636 v = (double) va_arg(vp, double); \
642 void IPlugBase::MakePreset(char* name
, ...)
644 IPreset
* pPreset
= GetNextUninitializedPreset(&mPresets
);
647 pPreset
->mInitialized
= true;
648 strcpy(pPreset
->mName
, name
);
650 int i
, n
= mParams
.GetSize();
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
, ...)
668 IPreset
* pPreset
= GetNextUninitializedPreset(&mPresets
);
671 pPreset
->mInitialized
= true;
672 strcpy(pPreset
->mName
, name
);
674 int i
= 0, n
= mParams
.GetSize();
676 WDL_TypedBuf
<double> vals
;
678 double* pV
= vals
.Get();
679 for (i
= 0; i
< n
; ++i
, ++pV
)
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
));
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
);
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
))
743 sprintf(str
, "%s %d", DEFAULT_USER_PRESET_NAME
, nDefaultNames
+ 1);
746 void IPlugBase::EnsureDefaultPreset()
749 MakeDefaultPreset("Empty", mPresets
.GetSize());
752 void IPlugBase::PruneUninitializedPresets()
756 while (i
< mPresets
.GetSize())
758 IPreset
* pPreset
= mPresets
.Get(i
);
759 if (pPreset
->mInitialized
)
765 mPresets
.Delete(i
, true);
770 bool IPlugBase::RestorePreset(int idx
)
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
));
786 restoredOK
= (UnserializeState(&(pPreset
->mChunk
), 0) > 0);
791 mCurrentPresetIdx
= idx
;
792 PresetsChangedByHost();
794 RedrawParamControls();
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
);
818 const char* IPlugBase::GetPresetName(int idx
)
820 if (idx
>= 0 && idx
< mPresets
.GetSize())
822 return mPresets
.Get(idx
)->mName
;
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
)
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);
866 int IPlugBase::UnserializePresets(ByteChunk
* pChunk
, int startPos
)
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
);
885 pPreset
->mChunk
.Clear();
886 SerializeState(&(pPreset
->mChunk
));
890 RestorePreset(mCurrentPresetIdx
);
894 bool IPlugBase::SerializeParams(ByteChunk
* pChunk
)
898 WDL_MutexLock
lock(&mMutex
);
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);
911 int IPlugBase::UnserializeParams(ByteChunk
* pChunk
, int startPos
)
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
);
921 Trace(TRACELOC
, "%d %s %f", i
, pParam
->GetNameForHost(), pParam
->Value());
922 pos
= pChunk
->Get(&v
, pos
);
929 bool IPlugBase::CompareState(const unsigned char* incomingState
, int startPos
)
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);
949 void IPlugBase::RedrawParamControls()
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);
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;
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
);
988 switch (pParam
->Type())
990 case IParam::kTypeBool
:
991 sprintf(paramVal
, "%s", (pParam
->Bool() ? "true" : "false"));
993 case IParam::kTypeInt
:
994 sprintf(paramVal
, "%d", pParam
->Int());
996 case IParam::kTypeEnum
:
997 sprintf(paramVal
, "%d", pParam
->Int());
999 case IParam::kTypeDouble
:
1001 sprintf(paramVal
, "%.6f", pParam
->Value());
1004 fprintf(fp
, ",\n %s, %s", paramEnumNames
[i
], paramVal
);
1006 fprintf(fp
, ");\n");
1011 #ifndef MAX_BLOB_LENGTH
1012 #define MAX_BLOB_LENGTH 1024
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());
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
)
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
1081 bool IPlugBase::SaveProgramAsFXP(const char* defaultFileName
)
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");
1094 VstInt32 chunkMagic
= WDL_bswap32('CcnK');
1095 VstInt32 byteSize
= 0;
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());
1102 memset(prgName
, 0, 28);
1103 strcpy(prgName
, GetPresetName(GetCurrentPresetIdx()));
1105 pgm
.Put(&chunkMagic
);
1107 if (DoesStateChunks())
1112 fxpMagic
= WDL_bswap32('FPCh');
1114 InitChunkWithIPlugVer(&state
);
1115 SerializeState(&state
);
1117 chunkSize
= WDL_bswap32(state
.Size());
1118 byteSize
= WDL_bswap32(state
.Size() + 60);
1122 pgm
.Put(&fxpVersion
);
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());
1132 fxpMagic
= WDL_bswap32('FxCk');
1133 //byteSize = WDL_bswap32(20 + 28 + (NParams() * 4) );
1136 pgm
.Put(&fxpVersion
);
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
);
1151 fwrite(pgm
.GetBytes(), pgm
.Size(), 1, fp
);
1160 bool IPlugBase::SaveBankAsFXB(const char* defaultFileName
)
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");
1173 VstInt32 chunkMagic
= WDL_bswap32('CcnK');
1174 VstInt32 byteSize
= 0;
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());
1182 memset(future
, 0, 124);
1184 bnk
.Put(&chunkMagic
);
1186 if (DoesStateChunks())
1191 fxbMagic
= WDL_bswap32('FBCh');
1193 InitChunkWithIPlugVer(&state
);
1194 SerializePresets(&state
);
1196 chunkSize
= WDL_bswap32(state
.Size());
1197 byteSize
= WDL_bswap32(160 + state
.Size() );
1201 bnk
.Put(&fxbVersion
);
1203 bnk
.Put(&pluginVersion
);
1205 bnk
.Put(¤tPgm
);
1206 bnk
.PutBytes(&future
, 124);
1208 bnk
.Put(&chunkSize
);
1209 bnk
.PutBytes(state
.GetBytes(), state
.Size());
1213 fxbMagic
= WDL_bswap32('FxBk');
1217 bnk
.Put(&fxbVersion
);
1219 bnk
.Put(&pluginVersion
);
1221 bnk
.Put(¤tPgm
);
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
);
1233 memset(prgName
, 0, 28);
1234 strcpy(prgName
, pPreset
->mName
);
1236 bnk
.Put(&chunkMagic
);
1237 //byteSize = WDL_bswap32(20 + 28 + (NParams() * 4) );
1240 bnk
.Put(&fxpVersion
);
1242 bnk
.Put(&pluginVersion
);
1243 bnk
.Put(&numParams
);
1244 bnk
.PutBytes(prgName
, 28);
1248 for (int i
= 0; i
< NParams(); i
++)
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
);
1261 fwrite(bnk
.GetBytes(), bnk
.Size(), 1, fp
);
1270 bool IPlugBase::LoadProgramFromFXP()
1274 WDL_String fileName
;
1275 mGraphics
->PromptForFile(&fileName
, kFileOpen
, &mPreviousPath
, "fxp");
1277 if (fileName
.GetLength())
1279 FILE* fp
= fopen(fileName
.Get(), "rb");
1286 fseek(fp
, 0 , SEEK_END
);
1287 fileSize
= ftell(fp
);
1290 pgm
.Resize(fileSize
);
1291 fread(pgm
.GetBytes(), fileSize
, 1, fp
);
1297 VstInt32 chunkMagic
;
1298 VstInt32 byteSize
= 0;
1300 VstInt32 fxpVersion
;
1302 VstInt32 pluginVersion
;
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')
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();
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();
1364 bool IPlugBase::LoadBankFromFXB()
1368 WDL_String fileName
;
1369 mGraphics
->PromptForFile(&fileName
, kFileOpen
, &mPreviousPath
, "fxb");
1371 if (fileName
.GetLength())
1373 FILE* fp
= fopen(fileName
.Get(), "rb");
1380 fseek(fp
, 0 , SEEK_END
);
1381 fileSize
= ftell(fp
);
1384 bnk
.Resize(fileSize
);
1385 fread(bnk
.GetBytes(), fileSize
, 1, fp
);
1391 VstInt32 chunkMagic
;
1392 VstInt32 byteSize
= 0;
1394 VstInt32 fxbVersion
;
1396 VstInt32 pluginVersion
;
1398 VstInt32 currentPgm
;
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(¤tPgm
, 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')
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();
1438 else if (fxbMagic
== 'FxBk')
1440 VstInt32 chunkMagic
;
1443 VstInt32 fxpVersion
;
1445 VstInt32 pluginVersion
;
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
);
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();
1505 void IPlugBase::InitChunkWithIPlugVer(ByteChunk
* pChunk
)
1508 int magic
= IPLUG_VERSION_MAGIC
;
1509 pChunk
->Put(&magic
);
1510 int ver
= IPLUG_VERSION
;
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
);
1525 bool IPlugBase::SendMidiMsgs(WDL_TypedBuf
<IMidiMsg
>* pMsgs
)
1528 int n
= pMsgs
->GetSize();
1529 IMidiMsg
* pMsg
= pMsgs
->Get();
1530 for (int i
= 0; i
< n
; ++i
, ++pMsg
) {
1531 rc
&= SendMidiMsg(pMsg
);