diff --git a/include/audacity/EffectInterface.h b/include/audacity/EffectInterface.h index 0118b5f27..067473aab 100644 --- a/include/audacity/EffectInterface.h +++ b/include/audacity/EffectInterface.h @@ -127,7 +127,7 @@ public: virtual bool RealtimeFinalize() = 0; virtual bool RealtimeSuspend() = 0; virtual bool RealtimeResume() = 0; - virtual sampleCount RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size) = 0; + virtual sampleCount RealtimeProcess(int group, float **inbuf, float **outbuf, sampleCount size) = 0; virtual bool ShowInterface(void *parent) = 0; }; diff --git a/src/AudioIO.cpp b/src/AudioIO.cpp index f187ee8d8..178c1891f 100644 --- a/src/AudioIO.cpp +++ b/src/AudioIO.cpp @@ -1362,7 +1362,7 @@ int AudioIO::StartStream(WaveTrackArray playbackTracks, #if defined(EXPERIMENTAL_REALTIME_EFFECTS) if (mNumPlaybackChannels > 0) { - EffectManager::Get().RealtimeInitialize(&mPlaybackTracks); + EffectManager::Get().RealtimeInitialize(); } #endif @@ -3499,8 +3499,6 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, numSolo++; #endif - int logicalCnt = 0; - int chanCnt = 0; WaveTrack **chans = (WaveTrack **) alloca(numPlaybackChannels * sizeof(WaveTrack *)); float **tempBufs = (float **) alloca(numPlaybackChannels * sizeof(float *)); for (int c = 0; c < numPlaybackChannels; c++) @@ -3508,6 +3506,9 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, tempBufs[c] = (float *) alloca(framesPerBuffer * sizeof(float)); } + int group = 0; + int chanCnt = 0; + float rate = 0.0; for (t = 0; t < numPlaybackTracks; t++) { WaveTrack *vt = gAudioIO->mPlaybackTracks[t]; @@ -3527,7 +3528,14 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, if (vt->GetMute() && !vt->GetSolo()) cut = true; + rate = vt->GetRate(); linkFlag = vt->GetLinked(); + + // If we have a mono track, clear the right channel + if (!linkFlag) + { + memset(tempBufs[1], 0, framesPerBuffer * sizeof(float)); + } } #define ORIGINAL_DO_NOT_PLAY_ALL_MUTED_TRACKS_TO_END @@ -3574,7 +3582,7 @@ int audacityAudioCallback(const void *inputBuffer, void *outputBuffer, #endif #if defined(EXPERIMENTAL_REALTIME_EFFECTS) - len = EffectManager::Get().RealtimeProcess(logicalCnt++, tempBufs, len); + len = EffectManager::Get().RealtimeProcess(group++, chanCnt, rate, tempBufs, len); #endif // If our buffer is empty and the time indicator is past // the end, then we've actually finished playing the entire diff --git a/src/AudioIO.h b/src/AudioIO.h index 483edd6dc..bfb73ac8e 100644 --- a/src/AudioIO.h +++ b/src/AudioIO.h @@ -15,7 +15,6 @@ #include "portaudio.h" #include "Audacity.h" -#include "effects/Effect.h" #include "Experimental.h" #ifdef USE_MIDI diff --git a/src/ModuleManager.cpp b/src/ModuleManager.cpp index d20cb39cf..4578ea8f2 100644 --- a/src/ModuleManager.cpp +++ b/src/ModuleManager.cpp @@ -409,7 +409,6 @@ bool ModuleManager::DiscoverProviders(wxArrayString & providers) for (int i = 0, cnt = provList.GetCount(); i < cnt; i++) { - wxPrintf(wxT("provider %s\n"), provList[i].c_str()); providers.push_back(provList[i]); } diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index da0dcdd9c..8419f2e70 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -136,20 +136,21 @@ CheckListAx::CheckListAx( wxListCtrl * window ): CheckListAx::~CheckListAx() { - } +} void CheckListAx::SetSelected( int item ) { - if (mLastId != -1) { + if (mLastId != -1) + { NotifyEvent( wxACC_EVENT_OBJECT_SELECTIONREMOVE, mParent, wxOBJID_CLIENT, mLastId ); mLastId = -1; -} + } if (item != -1) -{ + { NotifyEvent( wxACC_EVENT_OBJECT_FOCUS, mParent, wxOBJID_CLIENT, @@ -241,14 +242,14 @@ wxAccStatus CheckListAx::GetKeyboardShortcut( int WXUNUSED(childId), wxString *s // Returns the rectangle for this object (id = 0) or a child element (id > 0). // rect is in screen coordinates. wxAccStatus CheckListAx::GetLocation( wxRect& rect, int elementId ) - { +{ if( elementId == wxACC_SELF ) - { + { rect = mParent->GetRect(); rect.SetPosition( mParent->GetParent()->ClientToScreen( rect.GetPosition() ) ); - } - else - { + } + else + { if( elementId <= mParent->GetItemCount() ) { mParent->GetItemRect( elementId - 1, rect, wxLIST_RECT_LABEL ); @@ -1287,9 +1288,15 @@ PluginManager & PluginManager::Get() void PluginManager::Initialize() { - Load(); + bool loaded = Load(); ModuleManager::Get().EarlyInit(); + if (!loaded) + { + PluginRegistrationDialog dlg; + dlg.ShowModal(); + } + CheckForUpdates(); bool doRescan; @@ -1333,12 +1340,12 @@ void PluginManager::Terminate() } } -void PluginManager::Load() +bool PluginManager::Load() { // IF already open THEN nothing to do. if (mConfig != NULL) { - return; + return true; } // Create the config @@ -1353,7 +1360,7 @@ void PluginManager::Load() { // Must start over mConfig->DeleteAll(); - return; + return false; } // Load all provider plugins first @@ -1363,6 +1370,8 @@ void PluginManager::Load() LoadGroup(wxT("effects"), PluginTypeEffect); LoadGroup(wxT("exporters"), PluginTypeExporter); LoadGroup(wxT("importers"), PluginTypeImporter); + + return true; } void PluginManager::LoadGroup(const wxChar * group, PluginType type) diff --git a/src/PluginManager.h b/src/PluginManager.h index 4f17def10..c5566634e 100644 --- a/src/PluginManager.h +++ b/src/PluginManager.h @@ -223,7 +223,7 @@ public: void CheckForUpdates(); private: - void Load(); + bool Load(); void LoadGroup(const wxChar *group, PluginType type); void Save(); void SaveGroup(const wxChar *group, PluginType type); diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 740565363..a48fc7fdc 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -1291,25 +1291,13 @@ bool Effect::RealtimeInitialize() #if defined(EXPERIMENTAL_REALTIME_EFFECTS) if (mClient) { - mNumChannels = 0; + mNumGroups = -1; return mClient->RealtimeInitialize(); } #endif return false; } -bool Effect::RealtimeAddProcessor(int numChannels, float sampleRate) -{ -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) - if (mClient) - { - mNumChannels = numChannels; - return mClient->RealtimeAddProcessor(numChannels, sampleRate); - } -#endif - - return false; -} bool Effect::RealtimeFinalize() { @@ -1347,53 +1335,111 @@ bool Effect::RealtimeResume() return false; } -sampleCount Effect::RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size) +sampleCount Effect::RealtimeProcess(int group, + int chans, + float rate, + float **inbuf, + float **outbuf, + sampleCount numSamples) { #if defined(EXPERIMENTAL_REALTIME_EFFECTS) - float **tin = inbuf; - if (mNumAudioIn > mNumChannels) + // + // The caller passes the number of channels to process and specifies + // the number of input and output buffers. There will always be the + // same number of output buffers as there are input buffers. + // + // Effects always require a certain number of input and output buffers, + // so if the number of channels we're curently processing are different + // that was the effect expects, then we use a few methods of satisfying + // the effects requirements. + float **clientIn = (float **) alloca(mNumAudioIn * sizeof(float *)); + float **clientOut = (float **) alloca(mNumAudioOut * sizeof(float *)); + float *dummybuf = (float *) alloca(numSamples * sizeof(float)); + sampleCount len = 0; + int ichans = chans; + int ochans = chans; + int gchans = chans; + int indx = 0; + int ondx = 0; + + // Call the client until we run out of input or output channels + while (ichans > 0 && ochans > 0) { - float *dummybuf = (float *) alloca(size * sizeof(float)); - tin = (float **) alloca(mNumAudioIn * sizeof(float *)); - for (int i = 0; i < mNumChannels; i++) + // If we don't have enough input channels to accomodate the client's + // requirements, then we replicate the input channels until the + // client's needs are met. + if (ichans < mNumAudioIn) { - tin[i] = inbuf[i]; + for (int i = 0; i < mNumAudioIn; i++) + { + if (indx == ichans) + { + indx = 0; + } + clientIn[i] = inbuf[indx++]; + } + + // All input channels have been consumed + ichans = 0; } - for (int i = mNumChannels; i < mNumAudioIn; i++) + // Otherwise fullfil the client's needs with as many input channels as possible. + // After calling the client with this set, we will loop back up to process more + // of the input/output channels. + else if (ichans >= mNumAudioIn) { - tin[i] = dummybuf; + gchans = 0; + for (int i = 0; i < mNumAudioIn; i++, ichans--, gchans++) + { + clientIn[i] = inbuf[indx++]; + } } - for (int i = 0; i < size; i++) + + // If we don't have enough output channels to accomodate the client's + // requirements, then we provide all of the output channels and fulfill + // the client's needs with dummy buffers. These will just get tossed. + if (ochans < mNumAudioOut) { - dummybuf[i] = 0.0; + for (int i = 0; i < mNumAudioOut; i++) + { + if (i < ochans) + { + clientOut[i] = outbuf[i]; + } + else + { + clientOut[i] = dummybuf; + } + } + + // All output channels have been consumed + ochans = 0; } + // Otherwise fullfil the client's needs with as many output channels as possible. + // After calling the client with this set, we will loop back up to process more + // of the input/output channels. + else if (ochans >= mNumAudioOut) + { + for (int i = 0; i < mNumAudioOut; i++, ochans--) + { + clientOut[i] = outbuf[ondx++]; + } + } + + // If the current group hasn't yet been seen, then we must + // add a new processor to handle this channel (sub)group + if (group >= mNumGroups) + { + mClient->RealtimeAddProcessor(gchans, rate); + } + + // Finally call the plugin to process the block + len = mClient->RealtimeProcess(group++, clientIn, clientOut, numSamples); } - float **tout = outbuf; - if (mNumAudioOut > mNumChannels) - { - float *dummybuf = (float *) alloca(size * sizeof(float)); - tout = (float **) alloca(mNumAudioOut * sizeof(float *)); - for (int i = 0; i < mNumChannels; i++) - { - tout[i] = outbuf[i]; - } - for (int i = mNumChannels; i < mNumAudioOut; i++) - { - tout[i] = dummybuf; - } - for (int i = 0; i < size; i++) - { - dummybuf[i] = 0.0; - } - } + // Remember the number of channel groups we've seen + mNumGroups++; - // Finally call the plugin to process the block - // LLL FIXME - // If the number of channels is greater than the number of in/out the effect - // can handle, then call RealtimeProcess for each "set" of tracks over its - // capabilities. - return mClient->RealtimeProcess(index, tin, tout, size); + return len; #else return 0; #endif @@ -1540,6 +1586,26 @@ void Effect::Preview(bool dryOnly) mTracks = saveTracks; } +int Effect::GetAudioInCount() +{ + if (mClient) + { + return mClient->GetAudioInCount(); + } + + return 0; +} + +int Effect::GetAudioOutCount() +{ + if (mClient) + { + return mClient->GetAudioInCount(); + } + + return 0; +} + EffectDialog::EffectDialog(wxWindow * parent, const wxString & title, int type, diff --git a/src/effects/Effect.h b/src/effects/Effect.h index 8edf0b066..a4886dbaf 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -239,13 +239,20 @@ class AUDACITY_DLL_API Effect : public EffectHostInterface // important for sorting. static wxString StripAmpersand(const wxString& str); + int GetAudioInCount(); + int GetAudioOutCount(); + // Realtime Effect Processing bool RealtimeInitialize(); - bool RealtimeAddProcessor(int numChannels, float sampleRate); bool RealtimeFinalize(); bool RealtimeSuspend(); bool RealtimeResume(); - sampleCount RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size); + sampleCount RealtimeProcess(int group, + int chans, + float rate, + float **inbuf, + float **outbuf, + sampleCount numSamples); // // protected virtual methods diff --git a/src/effects/EffectManager.cpp b/src/effects/EffectManager.cpp index 12b188bbc..a44052308 100644 --- a/src/effects/EffectManager.cpp +++ b/src/effects/EffectManager.cpp @@ -175,13 +175,12 @@ EffectManager::EffectManager() #endif #if defined(EXPERIMENTAL_REALTIME_EFFECTS) - mRealtimeMutex.Lock(); + mRealtimeLock.Enter(); mRealtimeEffects = NULL; mRealtimeCount = 0; - mRealtimeActive = false; - mRealtimeSuspended = false; - mRealtimeMutex.Unlock(); + mRealtimeSuspended = true; mRealtimeLatency = 0; + mRealtimeLock.Leave(); #endif #if defined(EXPERIMENTAL_EFFECTS_RACK) @@ -387,119 +386,228 @@ EffectRack *EffectManager::GetRack() void EffectManager::ShowRack() { - GetRack()->Show(); + GetRack()->Show(!GetRack()->IsShown()); } #endif #if defined(EXPERIMENTAL_REALTIME_EFFECTS) -void EffectManager::RealtimeInitialize(const WaveTrackArray *tracks) +void EffectManager::RealtimeSetEffects(const EffectArray & effects) { - mRealtimeMutex.Lock(); - mRealtimeTracks = tracks; - for (int e = 0; e < mRealtimeCount; e++) + int newCount = (int) effects.GetCount(); + Effect **newEffects = new Effect *[newCount]; + for (int i = 0; i < newCount; i++) { - mRealtimeEffects[e]->RealtimeInitialize(); + newEffects[i] = effects[i]; + } - for (size_t i = 0, cnt = tracks->GetCount(); i < cnt; i++) + // Block RealtimeProcess() + RealtimeSuspend(); + + // Tell any effects no longer in the chain to clean up + for (int i = 0; i < mRealtimeCount; i++) + { + Effect *e = mRealtimeEffects[i]; + + // Scan the new chain for the effect + for (int j = 0; j < newCount; j++) { - WaveTrack *t = (*tracks)[i]; - if (t->GetLinked()) + // Found it so we're done + if (e == newEffects[j]) { - mRealtimeEffects[e]->RealtimeAddProcessor(2, t->GetRate()); - i++; + e = NULL; + break; } - else + } + + // Must not have been in the new chain, so tell it to cleanup + if (e) + { + e->RealtimeFinalize(); + } + } + + // Tell any new effects to get ready + for (int i = 0; i < newCount; i++) + { + Effect *e = newEffects[i]; + + // Scan the old chain for the effect + for (int j = 0; j < mRealtimeCount; j++) + { + // Found it so tell effect to get ready + if (e == mRealtimeEffects[j]) { - mRealtimeEffects[e]->RealtimeAddProcessor(1, t->GetRate()); + e = NULL; } } + + // Must not have been in the old chain, so tell it to initialize + if (e) + { + e->RealtimeInitialize(); + } } - mRealtimeActive = true; + // Get rid of the old chain + if (mRealtimeEffects) + { + delete [] mRealtimeEffects; + } - mRealtimeMutex.Unlock(); + // And install the new one + mRealtimeEffects = newEffects; + mRealtimeCount = newCount; + // Allow RealtimeProcess() to, well, process + RealtimeResume(); +} +#endif + +void EffectManager::RealtimeInitialize() +{ + // No need to do anything if there are no effects + if (!mRealtimeCount) + { + return; + } + + // The audio thread should not be running yet, but protect anyway + RealtimeSuspend(); + + // Tell each effect to get ready for action + for (int i = 0; i < mRealtimeCount; i++) + { + mRealtimeEffects[i]->RealtimeInitialize(); + } + + // Get things moving RealtimeResume(); } void EffectManager::RealtimeFinalize() { + // Make sure nothing is going on RealtimeSuspend(); - mRealtimeActive = false; + // It is now safe to clean up mRealtimeLatency = 0; - mRealtimeTracks = NULL; - mRealtimeMutex.Lock(); + // Tell each effect to clean up as well for (int i = 0; i < mRealtimeCount; i++) { mRealtimeEffects[i]->RealtimeFinalize(); } - mRealtimeMutex.Unlock(); } void EffectManager::RealtimeSuspend() { + mRealtimeLock.Enter(); + + // Already suspended...bail + if (mRealtimeSuspended) + { + mRealtimeLock.Leave(); + return; + } + + // Show that we aren't going to be doing anything mRealtimeSuspended = true; - mRealtimeMutex.Lock(); + // And make sure the effects don't either for (int i = 0; i < mRealtimeCount; i++) { mRealtimeEffects[i]->RealtimeSuspend(); } - mRealtimeMutex.Unlock(); + + mRealtimeLock.Leave(); } void EffectManager::RealtimeResume() { - mRealtimeMutex.Lock(); + mRealtimeLock.Enter(); + + // Already running...bail + if (!mRealtimeSuspended) + { + mRealtimeLock.Leave(); + return; + } + + // Tell the effects to get ready for more action for (int i = 0; i < mRealtimeCount; i++) { mRealtimeEffects[i]->RealtimeResume(); } - mRealtimeMutex.Unlock(); + // And we should too mRealtimeSuspended = false; + + mRealtimeLock.Leave(); } -sampleCount EffectManager::RealtimeProcess(int index, float **buffers, sampleCount numSamples) +// +// This will be called in a different thread than the main GUI thread. +// +sampleCount EffectManager::RealtimeProcess(int group, int chans, float rate, float **buffers, sampleCount numSamples) { + // Protect ourselves from the main thread + mRealtimeLock.Enter(); + // Can be suspended because of the audio stream being paused or because effects - // have been suspended. - if (mRealtimeSuspended) + // have been suspended, so allow the samples to pass as-is. + if (mRealtimeSuspended || mRealtimeCount == 0) { - return 0; + mRealtimeLock.Leave(); + return numSamples; } + // Remember when we started so we can calculate the amount of latency we + // are introducing wxMilliClock_t start = wxGetLocalTimeMillis(); - float *olc = (float *) alloca(sizeof(float) * numSamples); - float *orc = (float *) alloca(sizeof(float) * numSamples); + // Allocate the in/out buffer arrays + float **ibuf = (float **) alloca(chans * sizeof(float *)); + float **obuf = (float **) alloca(chans * sizeof(float *)); - float *ibuf[2] = {buffers[0], buffers[1]}; - float *obuf[2] = {olc, orc}; + // And populate the input with the buffers we've been given while allocating + // new output buffers + for (int i = 0; i < chans; i++) + { + ibuf[i] = buffers[i]; + obuf[i] = (float *) alloca(numSamples * sizeof(float)); + } - mRealtimeMutex.Lock(); + // Now call each effect in the chain while swapping buffer pointers to feed the + // output of one effect as the input to the next effect for (int i = 0; i < mRealtimeCount; i++) { - mRealtimeEffects[i]->RealtimeProcess(index, ibuf, obuf, numSamples); + mRealtimeEffects[i]->RealtimeProcess(group, chans, rate, ibuf, obuf, numSamples); - float *tbuf[2] = {ibuf[0], ibuf[1]}; - ibuf[0] = obuf[0]; - ibuf[1] = obuf[1]; - obuf[0] = tbuf[0]; - obuf[1] = tbuf[1]; + for (int j = 0; j < chans; j++) + { + float *temp; + temp = ibuf[j]; + ibuf[j] = obuf[j]; + obuf[j] = temp; + } } - mRealtimeMutex.Unlock(); - if (obuf[0] == buffers[0]) + // Once we're done, we might wind up with the last effect storing its results + // in the temporary buffers. If that's the case, we need to copy it over to + // the caller's buffers. This happens when the number of effects is odd. + if (mRealtimeCount & 1) { - memcpy(buffers[1], ibuf[1], sizeof(float) * numSamples); - memcpy(buffers[0], ibuf[0], sizeof(float) * numSamples); + for (int i = 0; i < chans; i++) + { + memcpy(buffers[i], ibuf[i], numSamples * sizeof(float)); + } } + // Remember the latency mRealtimeLatency = (int) (wxGetLocalTimeMillis() - start).GetValue(); + mRealtimeLock.Leave(); + // // This is wrong...needs to handle tails // @@ -511,39 +619,6 @@ int EffectManager::GetRealtimeLatency() return mRealtimeLatency; } -void EffectManager::SetRealtime(const EffectArray & effects) -{ - Effect **rteffects = new Effect *[effects.GetCount()]; - if (rteffects) - { - for (int i = 0, cnt = effects.GetCount(); i < cnt; i++) - { - rteffects[i] = effects[i]; - } - - mRealtimeMutex.Lock(); - Effect **rtold = mRealtimeEffects; - mRealtimeEffects = rteffects; - mRealtimeCount = effects.GetCount(); - - if (mRealtimeActive) - { - const WaveTrackArray *tracks = mRealtimeTracks; - RealtimeFinalize(); - RealtimeInitialize(tracks); - } - - mRealtimeMutex.Unlock(); - - if (rtold) - { - delete [] rtold; - } - - } -} -#endif - Effect *EffectManager::GetEffect(const PluginID & ID) { Effect *effect; @@ -575,7 +650,7 @@ const PluginID & EffectManager::GetEffectByIdentifier(const wxString & strTarget { if (strTarget == wxEmptyString) // set GetEffectIdentifier to wxT("") to not show an effect in Batch mode { - return PluginID(wxEmptyString); + return PluginID(wxString(wxEmptyString)); } PluginManager & pm = PluginManager::Get(); @@ -589,7 +664,7 @@ const PluginID & EffectManager::GetEffectByIdentifier(const wxString & strTarget plug = pm.GetNextPlugin(PluginTypeEffect); } - return PluginID(wxEmptyString); + return PluginID(wxString(wxEmptyString)); } #ifdef EFFECT_CATEGORIES diff --git a/src/effects/EffectManager.h b/src/effects/EffectManager.h index dd43e19fc..00c700bb0 100644 --- a/src/effects/EffectManager.h +++ b/src/effects/EffectManager.h @@ -94,12 +94,12 @@ class AUDACITY_DLL_API EffectManager #if defined(EXPERIMENTAL_REALTIME_EFFECTS) // Realtime effect processing - void RealtimeInitialize(const WaveTrackArray *tracks); + void RealtimeSetEffects(const EffectArray & mActive); + void RealtimeInitialize(); void RealtimeFinalize(); void RealtimeSuspend(); void RealtimeResume(); - sampleCount RealtimeProcess(int index, float **buffers, sampleCount numSamples); - void SetRealtime(const EffectArray & mActive); + sampleCount RealtimeProcess(int group, int chans, float rate, float **buffers, sampleCount numSamples); int GetRealtimeLatency(); #endif @@ -157,17 +157,11 @@ private: #endif #if defined(EXPERIMENTAL_REALTIME_EFFECTS) - wxMutex mRealtimeMutex; + wxCriticalSection mRealtimeLock; Effect **mRealtimeEffects; int mRealtimeCount; int mRealtimeLatency; - bool mRealtimeActive; bool mRealtimeSuspended; - const WaveTrackArray *mRealtimeTracks; - - float **inbuffers; - float **outbuffers; - #endif #ifdef EFFECT_CATEGORIES diff --git a/src/effects/EffectRack.cpp b/src/effects/EffectRack.cpp index 4e7262173..ee1e27d8f 100644 --- a/src/effects/EffectRack.cpp +++ b/src/effects/EffectRack.cpp @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -33,7 +34,6 @@ #include "EffectRack.h" #include "../Prefs.h" #include "../Project.h" -#include "../widgets/AButton.h" #include "../../images/EffectRack/EffectRack.h" @@ -80,7 +80,6 @@ EffectRack::EffectRack() wxSYSTEM_MENU | wxCLOSE_BOX | wxCAPTION | -// wxSIMPLE_BORDER | wxFRAME_NO_TASKBAR | wxFRAME_FLOAT_ON_PARENT) { @@ -89,20 +88,20 @@ EffectRack::EffectRack() mLastLatency = 0; mTimer.SetOwner(this); - mRemovePushed = CreateImage(remove_16x16_xpm, false, true); - mRemoveRaised = CreateImage(remove_16x16_xpm, true, true); - mPowerPushed = CreateImage(power_on_16x16_xpm, false, false); - mPowerRaised = CreateImage(power_off_16x16_xpm, true, false); - mFavPushed = CreateImage(fav_down_16x16_xpm, false, false); - mFavRaised = CreateImage(fav_up_16x16_xpm, true, false); - mSettingsPushed = CreateImage(settings_up_16x16_xpm, false, true); - mSettingsRaised = CreateImage(settings_down_16x16_xpm, true, true); - mUpDisabled = CreateImage(up_9x16_xpm, true, true); - mUpPushed = CreateImage(up_9x16_xpm, false, true); - mUpRaised = CreateImage(up_9x16_xpm, true, true); - mDownDisabled = CreateImage(down_9x16_xpm, true, true); - mDownPushed = CreateImage(down_9x16_xpm, false, true); - mDownRaised = CreateImage(down_9x16_xpm, true, true); + mPowerPushed = CreateBitmap(power_on_16x16_xpm, false, false); + mPowerRaised = CreateBitmap(power_off_16x16_xpm, true, false); + mSettingsPushed = CreateBitmap(settings_up_16x16_xpm, false, true); + mSettingsRaised = CreateBitmap(settings_down_16x16_xpm, true, true); + mUpDisabled = CreateBitmap(up_9x16_xpm, true, true); + mUpPushed = CreateBitmap(up_9x16_xpm, false, true); + mUpRaised = CreateBitmap(up_9x16_xpm, true, true); + mDownDisabled = CreateBitmap(down_9x16_xpm, true, true); + mDownPushed = CreateBitmap(down_9x16_xpm, false, true); + mDownRaised = CreateBitmap(down_9x16_xpm, true, true); + mFavPushed = CreateBitmap(fav_down_16x16_xpm, false, false); + mFavRaised = CreateBitmap(fav_up_16x16_xpm, true, false); + mRemovePushed = CreateBitmap(remove_16x16_xpm, false, true); + mRemoveRaised = CreateBitmap(remove_16x16_xpm, true, true); wxBoxSizer *bs = new wxBoxSizer(wxVERTICAL); mPanel = new wxPanel(this, wxID_ANY); @@ -110,12 +109,12 @@ EffectRack::EffectRack() SetSizer(bs); wxBoxSizer *hs = new wxBoxSizer(wxHORIZONTAL); - hs->Add(new wxButton(mPanel, wxID_APPLY, _("&Apply")), 0, wxALIGN_LEFT); + hs->Add(new wxButton(mPanel, wxID_APPLY, _("&Apply")), 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); hs->AddStretchSpacer(); mLatency = new wxStaticText(mPanel, wxID_ANY, _("Latency: 0")); hs->Add(mLatency, 0, wxALIGN_CENTER); hs->AddStretchSpacer(); - hs->Add(new wxToggleButton(mPanel, wxID_CLEAR, _("&Bypass")), 0, wxALIGN_RIGHT); + hs->Add(new wxToggleButton(mPanel, wxID_CLEAR, _("&Bypass")), 0, wxALIGN_RIGHT | wxALIGN_CENTER_VERTICAL); bs = new wxBoxSizer(wxVERTICAL); bs->Add(hs, 0, wxEXPAND); @@ -156,20 +155,12 @@ EffectRack::~EffectRack() for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++) { - wxSizerItem *si; - - si = mMainSizer->GetItem(i * NUMCOLS + COL_FAV); - AButton *fav = static_cast(si->GetWindow()); - - if (fav && fav->IsDown()) + if (mFavState[i]) { - si = mMainSizer->GetItem(i * NUMCOLS + COL_POWER); - AButton *power = static_cast(si->GetWindow()); - Effect *effect = mEffects[i]; gPrefs->Write(wxString::Format(wxT("/EffectsRack/Slot%02d"), i), wxString::Format(wxT("%d,%s"), - power->IsDown(), + mPowerState[i], effect->GetID().c_str())); } } @@ -182,99 +173,67 @@ void EffectRack::Add(Effect *effect, bool active, bool favorite) return; } - AButton *ab; + wxBitmapButton *bb; - ab = new AButton(mPanel, - ID_POWER + mNumEffects, - wxDefaultPosition, - wxDefaultSize, - mPowerRaised, - mPowerRaised, - mPowerPushed, - mPowerPushed, - true); - ab->SetToolTip(_("Set effect active state")); + bb = new wxBitmapButton(mPanel, ID_POWER + mNumEffects, mPowerRaised); + bb->SetBitmapSelected(mPowerRaised); + bb->SetName(_("Active State")); + bb->SetToolTip(_("Set effect active state")); + mPowerState.Add(active); if (active) { - ab->PushDown(); + bb->SetBitmapLabel(mPowerPushed); + bb->SetBitmapSelected(mPowerPushed); } else { - ab->PopUp(); + bb->SetBitmapLabel(mPowerRaised); + bb->SetBitmapSelected(mPowerRaised); } - mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - ab = new AButton(mPanel, - ID_EDITOR + mNumEffects, - wxDefaultPosition, - wxDefaultSize, - mSettingsRaised, - mSettingsRaised, - mSettingsPushed, - mSettingsPushed, - false); - ab->SetToolTip(_("Open/close effect editor")); - ab->PopUp(); - mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + bb = new wxBitmapButton(mPanel, ID_EDITOR + mNumEffects, mSettingsRaised); + bb->SetBitmapSelected(mSettingsPushed); + bb->SetName(_("Show/Hide Editor")); + bb->SetToolTip(_("Open/close effect editor")); + mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - ab = new AButton(mPanel, - ID_UP + mNumEffects, - wxDefaultPosition, - wxDefaultSize, - mUpRaised, - mUpRaised, - mUpPushed, - mUpDisabled, - false); - ab->SetToolTip(_("Move effect up in the rack")); - ab->PopUp(); - mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + bb = new wxBitmapButton(mPanel, ID_UP + mNumEffects, mUpRaised); + bb->SetBitmapSelected(mUpPushed); + bb->SetBitmapDisabled(mUpDisabled); + bb->SetName(_("Move Up")); + bb->SetToolTip(_("Move effect up in the rack")); + mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - ab = new AButton(mPanel, - ID_DOWN + mNumEffects, - wxDefaultPosition, - wxDefaultSize, - mDownRaised, - mDownRaised, - mDownPushed, - mDownDisabled, - false); - ab->SetToolTip(_("Move effect down in the rack")); - ab->PopUp(); - mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + bb = new wxBitmapButton(mPanel, ID_DOWN + mNumEffects, mDownRaised); + bb->SetBitmapSelected(mDownPushed); + bb->SetBitmapDisabled(mDownDisabled); + bb->SetName(_("Move Down")); + bb->SetToolTip(_("Move effect down in the rack")); + mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - ab = new AButton(mPanel, - ID_FAV + mNumEffects, - wxDefaultPosition, - wxDefaultSize, - mFavRaised, - mFavRaised, - mFavPushed, - mFavPushed, - true); - ab->SetToolTip(_("Mark effect as a favorite")); + bb = new wxBitmapButton(mPanel, ID_FAV + mNumEffects, mFavRaised); + bb->SetBitmapSelected(mFavPushed); + bb->SetName(_("Favorite")); + bb->SetToolTip(_("Mark effect as a favorite")); + mFavState.Add(favorite); if (favorite) { - ab->PushDown(); + bb->SetBitmapLabel(mFavPushed); + bb->SetBitmapSelected(mFavPushed); } else { - ab->PopUp(); + bb->SetBitmapLabel(mFavRaised); + bb->SetBitmapSelected(mFavRaised); } - mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); - ab = new AButton(mPanel, - ID_REMOVE + mNumEffects, - wxDefaultPosition, - wxDefaultSize, - mRemoveRaised, - mRemoveRaised, - mRemovePushed, - mRemovePushed, - false); - ab->SetToolTip(_("Remove effect from the rack")); - ab->PopUp(); - mMainSizer->Add(ab, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); + bb = new wxBitmapButton(mPanel, ID_REMOVE + mNumEffects, mRemoveRaised); + bb->SetBitmapSelected(mRemovePushed); + bb->SetName(_("Remove")); + bb->SetToolTip(_("Remove effect from the rack")); + mMainSizer->Add(bb, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL); wxStaticText *text = new wxStaticText(mPanel, ID_NAME + mNumEffects, effect->GetName()); text->SetToolTip(_("Name of the effect")); @@ -322,13 +281,15 @@ void EffectRack::OnApply(wxCommandEvent & AUNUSED(evt)) for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++) { - AButton *btn = static_cast(FindWindowById(ID_POWER + i)); - if (btn->IsDown()) + if (mPowerState[i]) { project->OnEffect(mEffects[i]->GetID(), true); - btn->PopUp(); - btn->Refresh(); + mPowerState[i] = false; + + wxBitmapButton *btn = static_cast(FindWindowById(ID_POWER + i)); + btn->SetBitmapLabel(mPowerRaised); + btn->SetBitmapSelected(mPowerRaised); } } @@ -343,16 +304,28 @@ void EffectRack::OnBypass(wxCommandEvent & evt) void EffectRack::OnPower(wxCommandEvent & evt) { - evt.Skip(); + wxBitmapButton *btn = static_cast(evt.GetEventObject()); + + int index = GetEffectIndex(btn); + mPowerState[index] = !mPowerState[index]; + if (mPowerState[index]) + { + btn->SetBitmapLabel(mPowerPushed); + btn->SetBitmapSelected(mPowerPushed); + } + else + { + btn->SetBitmapLabel(mPowerRaised); + btn->SetBitmapSelected(mPowerRaised); + } UpdateActive(); } void EffectRack::OnEditor(wxCommandEvent & evt) { - AButton *btn = static_cast(evt.GetEventObject()); + wxBitmapButton *btn = static_cast(evt.GetEventObject()); - btn->PopUp(); evt.Skip(); int index = GetEffectIndex(btn); @@ -366,9 +339,8 @@ void EffectRack::OnEditor(wxCommandEvent & evt) void EffectRack::OnUp(wxCommandEvent & evt) { - AButton *btn = static_cast(evt.GetEventObject()); + wxBitmapButton *btn = static_cast(evt.GetEventObject()); - btn->PopUp(); evt.Skip(); int index = GetEffectIndex(btn); @@ -382,9 +354,8 @@ void EffectRack::OnUp(wxCommandEvent & evt) void EffectRack::OnDown(wxCommandEvent & evt) { - AButton *btn = static_cast(evt.GetEventObject()); + wxBitmapButton *btn = static_cast(evt.GetEventObject()); - btn->PopUp(); evt.Skip(); size_t index = GetEffectIndex(btn); @@ -398,14 +369,26 @@ void EffectRack::OnDown(wxCommandEvent & evt) void EffectRack::OnFav(wxCommandEvent & evt) { - evt.Skip(); + wxBitmapButton *btn = static_cast(evt.GetEventObject()); + + int index = GetEffectIndex(btn); + mFavState[index] = !mFavState[index]; + if (mFavState[index]) + { + btn->SetBitmapLabel(mFavPushed); + btn->SetBitmapSelected(mFavPushed); + } + else + { + btn->SetBitmapLabel(mFavRaised); + btn->SetBitmapSelected(mFavRaised); + } } void EffectRack::OnRemove(wxCommandEvent & evt) { - AButton *btn = static_cast(evt.GetEventObject()); + wxBitmapButton *btn = static_cast(evt.GetEventObject()); - btn->PopUp(); evt.Skip(); int index = GetEffectIndex(btn); @@ -415,6 +398,8 @@ void EffectRack::OnRemove(wxCommandEvent & evt) } mEffects.RemoveAt(index); + mPowerState.RemoveAt(index); + mFavState.RemoveAt(index); if (mEffects.GetCount() == 0) { @@ -428,7 +413,9 @@ void EffectRack::OnRemove(wxCommandEvent & evt) for (int i = 0; i < NUMCOLS; i++) { - delete mMainSizer->GetItem(index)->GetWindow(); + wxWindow *w = mMainSizer->GetItem(index)->GetWindow(); + mMainSizer->Detach(index); + delete w; } mMainSizer->Layout(); @@ -437,7 +424,7 @@ void EffectRack::OnRemove(wxCommandEvent & evt) UpdateActive(); } -wxImage EffectRack::CreateImage(const char *xpm[], bool up, bool pusher) +wxBitmap EffectRack::CreateBitmap(const char *xpm[], bool up, bool pusher) { wxMemoryDC dc; wxBitmap pic(xpm); @@ -464,7 +451,7 @@ wxImage EffectRack::CreateImage(const char *xpm[], bool up, bool pusher) dc.SelectObject(wxNullBitmap); - return mod.ConvertToImage(); + return mod; } int EffectRack::GetEffectIndex(wxWindow *win) @@ -495,6 +482,14 @@ void EffectRack::MoveRowUp(int row) mEffects.RemoveAt(row); mEffects.Insert(effect, row - 1); + int state = mPowerState[row]; + mPowerState.RemoveAt(row); + mPowerState.Insert(state, row - 1); + + state = mFavState[row]; + mFavState.RemoveAt(row); + mFavState.Insert(state, row - 1); + row *= NUMCOLS; for (int i = 0; i < NUMCOLS; i++) @@ -522,16 +517,14 @@ void EffectRack::UpdateActive() { for (size_t i = 0, cnt = mEffects.GetCount(); i < cnt; i++) { - wxSizerItem *si = mMainSizer->GetItem(i * NUMCOLS + COL_POWER); - AButton *power = static_cast(si->GetWindow()); - if (power && power->IsDown()) + if (mPowerState[i]) { mActive.Add(mEffects[i]); } } } - EffectManager::Get().SetRealtime(mActive); + EffectManager::Get().RealtimeSetEffects(mActive); } #endif diff --git a/src/effects/EffectRack.h b/src/effects/EffectRack.h index 1f557f61f..72d8b5e3d 100644 --- a/src/effects/EffectRack.h +++ b/src/effects/EffectRack.h @@ -19,6 +19,7 @@ #if defined(EXPERIMENTAL_EFFECTS_RACK) #include +#include #include #include #include @@ -39,7 +40,7 @@ public: private: - wxImage CreateImage(const char *xpm[], bool up, bool pusher); + wxBitmap CreateBitmap(const char *xpm[], bool up, bool pusher); int GetEffectIndex(wxWindow *win); void MoveRowUp(int row); void UpdateActive(); @@ -60,20 +61,23 @@ private: wxStaticText *mLatency; int mLastLatency; - wxImage mRemovePushed; - wxImage mRemoveRaised; - wxImage mPowerPushed; - wxImage mPowerRaised; - wxImage mFavPushed; - wxImage mFavRaised; - wxImage mSettingsPushed; - wxImage mSettingsRaised; - wxImage mUpPushed; - wxImage mUpRaised; - wxImage mUpDisabled; - wxImage mDownPushed; - wxImage mDownRaised; - wxImage mDownDisabled; + wxBitmap mPowerPushed; + wxBitmap mPowerRaised; + wxBitmap mSettingsPushed; + wxBitmap mSettingsRaised; + wxBitmap mUpPushed; + wxBitmap mUpRaised; + wxBitmap mUpDisabled; + wxBitmap mDownPushed; + wxBitmap mDownRaised; + wxBitmap mDownDisabled; + wxBitmap mFavPushed; + wxBitmap mFavRaised; + wxBitmap mRemovePushed; + wxBitmap mRemoveRaised; + + wxArrayInt mPowerState; + wxArrayInt mFavState; int mNumEffects; diff --git a/src/effects/VST/VSTEffect.cpp b/src/effects/VST/VSTEffect.cpp index 98d1edbfb..0e59e6836 100644 --- a/src/effects/VST/VSTEffect.cpp +++ b/src/effects/VST/VSTEffect.cpp @@ -242,7 +242,8 @@ public: bool IsRealtimeCapable() { - return false; + // TODO: This should check to see if setting parameters can be + // automated. If so, then realtime is supported. return mType == EffectTypeProcess; } @@ -329,7 +330,7 @@ void VSTEffectsModule::Terminate() return; } -bool VSTEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) +bool VSTEffectsModule::AutoRegisterPlugins(PluginManagerInterface & WXUNUSED(pm)) { // We don't auto-register return true; @@ -849,6 +850,7 @@ private: enum { ID_VST_PROGRAM = 11000, + ID_VST_PROGRAMTEXT, ID_VST_LOAD, ID_VST_SAVE, ID_VST_SLIDERS, @@ -949,7 +951,7 @@ OSStatus VSTEffectDialog::OnOverlayEvent(EventHandlerCallRef handler, EventRef e sizeof(evtwin), NULL, &evtwin); -#define DEBUG_VST + #if defined(DEBUG_VST) int cls = GetEventClass(event); printf("OVERLAY class %4.4s kind %d ewin %p owin %p mwin %p anf %p fnf %p\n", @@ -1662,7 +1664,7 @@ wxSizer *VSTEffectDialog::BuildProgramBar() int progn = mEffect->callDispatcher(effGetProgram, 0, 0, NULL, 0.0); // An unset program is perfectly valid, do not force a default. - if (progn >= 0 && progn < progs.GetCount()) + if (progn >= 0 && progn < (int) progs.GetCount()) { val = progs[progn]; } @@ -1743,7 +1745,7 @@ void VSTEffectDialog::RefreshParameters(int skip) } } -void VSTEffectDialog::OnUpdateDisplay(wxCommandEvent & evt) +void VSTEffectDialog::OnUpdateDisplay(wxCommandEvent & WXUNUSED(evt)) { int i; @@ -1793,6 +1795,7 @@ void VSTEffectDialog::OnSlider(wxCommandEvent & evt) void VSTEffectDialog::OnProgram(wxCommandEvent & evt) { mEffect->callDispatcher(effSetProgram, 0, evt.GetInt(), NULL, 0.0); + RefreshParameters(); } @@ -1818,14 +1821,16 @@ void VSTEffectDialog::OnProgramText(wxCommandEvent & WXUNUSED(evt)) mEffect->SetString(effSetProgramName, name, i); // Some effects do not allow you to change the name and you can't always trust the - // return value, so just get ask for the name again. + // return value, so just ask for the name again. name = mEffect->GetString(effGetProgramNameIndexed, i); mProgram->SetString(i, name); - // On Windows, must reselect after doing a SetString()...at least that's + // On Windows, you must reselect after doing a SetString()...at least that's // what seems to be required. +#if defined(__WXMSW__) mProgram->SetStringSelection(name); +#endif // Which also means we have to reposition the caret. if (ip >= 0) @@ -2276,7 +2281,6 @@ bool VSTEffectDialog::LoadXML(const wxFileName & fn) void VSTEffectDialog::OnSave(wxCommandEvent & WXUNUSED(evt)) { - int i = mProgram->GetCurrentSelection(); wxString path; // Ask the user for the real name @@ -2942,13 +2946,12 @@ private: typedef AEffect *(*vstPluginMain)(audioMasterCallback audioMaster); intptr_t VSTEffect::AudioMaster(AEffect * effect, - int32_t opcode, - int32_t index, - intptr_t value, - void * ptr, - float opt) + int32_t opcode, + int32_t index, + intptr_t value, + void * ptr, + float opt) { - VSTEffect *vst = (effect ? (VSTEffect *) effect->user : NULL); // Handles operations during initialization...before VSTEffect has had a @@ -3052,6 +3055,8 @@ intptr_t VSTEffect::AudioMaster(AEffect * effect, { char *s = (char *) ptr; if (strcmp(s, "acceptIOChanges") == 0 || + strcmp(s, "sendVstTimeInfo") == 0 || + strcmp(s, "startStopProcess") == 0 || strcmp(s, "sizeWindow") == 0) { return 1; @@ -3069,9 +3074,15 @@ intptr_t VSTEffect::AudioMaster(AEffect * effect, } #if defined(EXPERIMENTAL_REALTIME_EFFECTS) + case audioMasterBeginEdit: + case audioMasterEndEdit: + return 0; + case audioMasterAutomate: if (vst) + { vst->Automate(index, opt); + } return 0; #else @@ -3132,6 +3143,11 @@ VSTEffect::VSTEffect(const wxString & path, VSTEffect *master) mUseBufferDelay = true; mReady = false; + mMasterIn = NULL; + mMasterInLen = 0; + mMasterOut = NULL; + mMasterOutLen = 0; + memset(&mTimeInfo, 0, sizeof(mTimeInfo)); mTimeInfo.samplePos = 0.0; mTimeInfo.sampleRate = 44100.0; // this is a bogus value, but it's only for the display @@ -3153,70 +3169,9 @@ VSTEffect::~VSTEffect() Unload(); } -// -// EffectClientInterface Implementation -// -void VSTEffect::SetHost(EffectHostInterface *host) -{ - mHost = host; - Startup(); -} - -bool VSTEffect::Startup() -{ - if (!mAEffect) - { - Load(); - } - - if (!mAEffect) - { - return false; - } - - // mHost will be null when running in the subprocess - if (mHost) - { - mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), mUserBlockSize, 8192); - mHost->GetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay, true); - - mBlockSize = mUserBlockSize; - - bool haveDefaults; - mHost->GetPrivateConfig(wxT("Default"), wxT("Initialized"), haveDefaults, false); - if (!haveDefaults) - { - SaveParameters(wxT("Default")); - mHost->SetPrivateConfig(wxT("Default"), wxT("Initialized"), true); - } - - LoadParameters(wxT("Current")); - } - - return true; -} - -bool VSTEffect::Shutdown() -{ - SaveParameters(wxT("Current")); - - return true; -} - -EffectType VSTEffect::GetType() -{ - if (mAudioIns == 0 && mMidiIns == 0) - { - return EffectTypeGenerate; - } - - if (mAudioOuts == 0 && mMidiOuts == 0) - { - return EffectTypeAnalyze; - } - - return EffectTypeProcess; -} +// ============================================================================ +// IdentInterface Implementation +// ============================================================================ wxString VSTEffect::GetID() { @@ -3270,6 +3225,26 @@ wxString VSTEffect::GetDescription() return mDescription; } +// ============================================================================ +// EffectIdentInterface Implementation +// ============================================================================ + +EffectType VSTEffect::GetType() +{ + if (mAudioIns == 0 && mMidiIns == 0) + { + return EffectTypeGenerate; + } + + if (mAudioOuts == 0 && mMidiOuts == 0) + { + return EffectTypeAnalyze; + } + + return EffectTypeProcess; +} + + wxString VSTEffect::GetFamily() { return VSTPLUGINTYPE; @@ -3295,6 +3270,57 @@ bool VSTEffect::IsRealtimeCapable() return true; } +// ============================================================================ +// EffectClientInterface Implementation +// ============================================================================ + +void VSTEffect::SetHost(EffectHostInterface *host) +{ + mHost = host; + Startup(); +} + +bool VSTEffect::Startup() +{ + if (!mAEffect) + { + Load(); + } + + if (!mAEffect) + { + return false; + } + + // mHost will be null when running in the subprocess + if (mHost) + { + mHost->GetSharedConfig(wxT("Settings"), wxT("BufferSize"), mUserBlockSize, 8192); + mHost->GetSharedConfig(wxT("Settings"), wxT("UseBufferDelay"), mUseBufferDelay, true); + + mBlockSize = mUserBlockSize; + + bool haveDefaults; + mHost->GetPrivateConfig(wxT("Default"), wxT("Initialized"), haveDefaults, false); + if (!haveDefaults) + { + SaveParameters(wxT("Default")); + mHost->SetPrivateConfig(wxT("Default"), wxT("Initialized"), true); + } + + LoadParameters(wxT("Current")); + } + + return true; +} + +bool VSTEffect::Shutdown() +{ + SaveParameters(wxT("Current")); + + return true; +} + int VSTEffect::GetAudioInCount() { return mAudioIns; @@ -3317,8 +3343,6 @@ int VSTEffect::GetMidiOutCount() sampleCount VSTEffect::GetBlockSize(sampleCount maxBlockSize) { - sampleCount prevSize = mBlockSize; - if (mUserBlockSize > maxBlockSize) { mBlockSize = maxBlockSize; @@ -3398,16 +3422,32 @@ sampleCount VSTEffect::ProcessBlock(float **inbuf, float **outbuf, sampleCount s { // Go let the plugin moleste the samples callProcessReplacing(inbuf, outbuf, size); + + // And track the position mTimeInfo.samplePos += ((double) size / mTimeInfo.sampleRate); return size; } +int VSTEffect::GetChannelCount() +{ + return mNumChannels; +} + +void VSTEffect::SetChannelCount(int numChannels) +{ + mNumChannels = numChannels; +} + bool VSTEffect::RealtimeInitialize() { // This is really just a dummy value and one to make the dialog happy since // all processing is handled by slaves. SetSampleRate(44100); + mMasterIn = NULL; + mMasterInLen = 0; + mMasterOut = NULL; + mMasterOutLen = 0; return ProcessInitialize(); } @@ -3418,18 +3458,61 @@ bool VSTEffect::RealtimeAddProcessor(int numChannels, float sampleRate) mSlaves.Add(slave); slave->SetSampleRate(sampleRate); + slave->SetChannelCount(numChannels); + + int clen = 0; + if (mAEffect->flags & effFlagsProgramChunks) + { + void *chunk = NULL; + + clen = (int) callDispatcher(effGetChunk, 1, 0, &chunk, 0.0); + if (clen != 0) + { + slave->callDispatcher(effSetChunk, 1, clen, chunk, 0.0); + } + } + + if (clen == 0) + { + for (int i = 0; i < mAEffect->numParams; i++) + { + slave->callSetParameter(i, callGetParameter(i)); + } + } return ProcessInitialize(); } +static int asdf=0; bool VSTEffect::RealtimeFinalize() { + asdf=1; for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++) { delete mSlaves[i]; } mSlaves.Clear(); + if (mMasterIn) + { + for (int i = 0; i < mAudioIns; i++) + { + delete [] mMasterIn[i]; + } + delete [] mMasterIn; + mMasterIn = NULL; + } + + if (mMasterOut) + { + for (int i = 0; i < mAudioOuts; i++) + { + delete [] mMasterOut[i]; + } + delete [] mMasterOut; + mMasterOut = NULL; + } + return ProcessFinalize(); } @@ -3447,14 +3530,75 @@ bool VSTEffect::RealtimeResume() return true; } -sampleCount VSTEffect::RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size) +sampleCount VSTEffect::RealtimeProcess(int group, float **inbuf, float **outbuf, sampleCount size) { - if (index < 0 || index >= mSlaves.GetCount()) + if (group < 0 || group >= (int) mSlaves.GetCount()) { return 0; } - return mSlaves[index]->ProcessBlock(inbuf, outbuf, size); + if (group == 0) + { + if (mMasterIn == NULL || mMasterInLen < size) + { + if (mMasterIn) + { + for (int i = 0; i < mAudioIns; i++) + { + delete [] mMasterIn[i]; + } + delete [] mMasterIn; + } + + mMasterIn = new float *[mAudioIns]; + for (int i = 0; i < mAudioIns; i++) + { + mMasterIn[i] = new float[size]; + } + mMasterInLen = size; + } + + for (int i = 0; i < mAudioIns; i++) + { + memset(mMasterIn[i], 0, size * sizeof(float)); + } + } + + int chanCnt = wxMin(mSlaves[group]->GetChannelCount(), mAudioIns); + for (int c = 0; c < chanCnt; c++) + { + for (int i = 0; i < size; i++) + { + mMasterIn[c][i] += inbuf[c][i]; + } + } + + if (group == (int) mSlaves.GetCount() - 1) + { + if (mMasterOut == NULL || mMasterOutLen < size) + { + if (mMasterOut) + { + for (int i = 0; i < mAudioOuts; i++) + { + delete [] mMasterOut[i]; + } + delete [] mMasterOut; + mMasterOut = NULL; + } + + mMasterOut = new float *[mAudioOuts]; + for (int i = 0; i < mAudioOuts; i++) + { + mMasterOut[i] = new float[size]; + } + mMasterOutLen = size; + } + + ProcessBlock(mMasterIn, mMasterOut, size); + } + + return mSlaves[group]->ProcessBlock(inbuf, outbuf, size); } // @@ -3532,6 +3676,10 @@ bool VSTEffect::ShowInterface(void *parent) #endif } +// ============================================================================ +// VSTEffect implementation +// ============================================================================ + void VSTEffect::InterfaceClosed() { mDlg = NULL; @@ -3667,7 +3815,7 @@ bool VSTEffect::Load() // Save the library reference mModule = lib; } - +wxLogDebug(wxT("Loaded")); #else // Attempt to load it @@ -3802,11 +3950,12 @@ void VSTEffect::Unload() if (mAEffect) { - // Turn the power off + // Turn the power off PowerOff(); // Finally, close the plugin callDispatcher(effClose, 0, 0, NULL, 0.0); + mAEffect = NULL; } if (mModule) @@ -4086,11 +4235,17 @@ float VSTEffect::callGetParameter(int index) void VSTEffect::callSetParameter(int index, float value) { - mAEffect->setParameter(mAEffect, index, value); - - for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++) + if (callDispatcher(effCanBeAutomated, 0, index, NULL, 0.0)) { - mSlaves[i]->callSetParameter(index, value); + mAEffect->setParameter(mAEffect, index, value); + + for (size_t i = 0, cnt = mSlaves.GetCount(); i < cnt; i++) + { + if (mSlaves[i]->callDispatcher(effCanBeAutomated, 0, index, NULL, 0.0)) + { + mSlaves[i]->callSetParameter(index, value); + } + } } } diff --git a/src/effects/VST/VSTEffect.h b/src/effects/VST/VSTEffect.h index 56dafd572..769b84be4 100644 --- a/src/effects/VST/VSTEffect.h +++ b/src/effects/VST/VSTEffect.h @@ -99,7 +99,10 @@ class VSTEffect : public EffectClientInterface virtual bool RealtimeFinalize(); virtual bool RealtimeSuspend(); virtual bool RealtimeResume(); - virtual sampleCount RealtimeProcess(int index, float **inbuf, float **outbuf, sampleCount size); + virtual sampleCount RealtimeProcess(int group, + float **inbuf, + float **outbuf, + sampleCount numSamples); virtual bool ShowInterface(void *parent); @@ -129,8 +132,8 @@ private: static int b64decode(wxString in, void *out); // Realtime - bool IsSlave(); - + int GetChannelCount(); + void SetChannelCount(int numChannels); // Utility methods @@ -206,6 +209,11 @@ private: // Realtime processing VSTEffect *mMaster; // non-NULL if a slave VSTEffectArray mSlaves; + int mNumChannels; + float **mMasterIn; + int mMasterInLen; + float **mMasterOut; + int mMasterOutLen; friend class VSTEffectDialog; friend class VSTEffectsModule; diff --git a/src/effects/VST/aeffectx.h b/src/effects/VST/aeffectx.h index ae498f3b1..f902aea03 100644 --- a/src/effects/VST/aeffectx.h +++ b/src/effects/VST/aeffectx.h @@ -111,6 +111,8 @@ const int effIdentify = 22; // from http://www.asseca.org/vst-24-specs/efIdentif const int effGetChunk = 23; // from Ardour const int effSetChunk = 24; // from Ardour const int effProcessEvents = 25; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efCanBeAutomated.html +const int effCanBeAutomated = 26; // The next one was gleaned from http://www.kvraudio.com/forum/viewtopic.php?p=1905347 const int effGetProgramNameIndexed = 29; const int effGetEffectName = 45; @@ -122,6 +124,15 @@ const int effCanDo = 51; // currently unused // The next one was gleaned from http://www.asseca.org/vst-24-specs/efIdle.html const int effIdle = 53; const int effGetVstVersion = 58; // currently unused +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginSetProgram.html +const int effBeginSetProgram = 67; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efEndSetProgram.html +const int effEndSetProgram = 68; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginLoadBank.html +const int effBeginLoadBank = 75; +// The next one was gleaned from http://www.asseca.org/vst-24-specs/efBeginLoadProgram.html +const int effBeginLoadProgram = 76; + // The next two were gleaned from http://www.kvraudio.com/forum/printview.php?t=143587&start=0 const int effStartProcess = 71; const int effStopProcess = 72;