diff --git a/include/audacity/EffectInterface.h b/include/audacity/EffectInterface.h index 067473aab..6e81a9283 100644 --- a/include/audacity/EffectInterface.h +++ b/include/audacity/EffectInterface.h @@ -86,9 +86,9 @@ class EffectHostInterface : public EffectIdentInterface, public: virtual ~EffectHostInterface() {}; - // Both of these are pretty much hacks until everything has been converted and - // it's decided if the client will display the interface or if that'll be done - // by the host. + virtual double GetDuration() = 0; + virtual bool SetDuration(double seconds) = 0; + virtual bool Apply() = 0; virtual void Preview() = 0; }; diff --git a/src/PluginManager.cpp b/src/PluginManager.cpp index 1bf5853c7..bb64a9db9 100644 --- a/src/PluginManager.cpp +++ b/src/PluginManager.cpp @@ -2006,7 +2006,6 @@ PluginDescriptor & PluginManager::CreatePlugin(IdentInterface *ident, PluginType // This will either create a new entry or replace an existing entry mPlugins[plug.GetID()] = plug; - PluginDescriptor &p = mPlugins[plug.GetID()]; return mPlugins[plug.GetID()]; } @@ -2116,6 +2115,10 @@ bool PluginManager::SetConfig(const wxString & key, const wxString & value) { wxString wxval = value.c_str(); result = mConfig->Write(key, wxval); + if (result) + { + result = mConfig->Flush(); + } } return result; @@ -2128,6 +2131,10 @@ bool PluginManager::SetConfig(const wxString & key, const int & value) if (mConfig && !key.empty()) { result = mConfig->Write(key, value); + if (result) + { + result = mConfig->Flush(); + } } return result; @@ -2140,6 +2147,10 @@ bool PluginManager::SetConfig(const wxString & key, const bool & value) if (mConfig && !key.empty()) { result = mConfig->Write(key, value); + if (result) + { + result = mConfig->Flush(); + } } return result; @@ -2152,6 +2163,10 @@ bool PluginManager::SetConfig(const wxString & key, const float & value) if (mConfig && !key.empty()) { result = mConfig->Write(key, value); + if (result) + { + result = mConfig->Flush(); + } } return result; @@ -2164,6 +2179,10 @@ bool PluginManager::SetConfig(const wxString & key, const double & value) if (mConfig && !key.empty()) { result = mConfig->Write(key, value); + if (result) + { + result = mConfig->Flush(); + } } return result; @@ -2176,6 +2195,10 @@ bool PluginManager::SetConfig(const wxString & key, const sampleCount & value) if (mConfig && !key.empty()) { result = mConfig->Write(key, wxString::Format(wxT("%d"), (int) value)); + if (result) + { + result = mConfig->Flush(); + } } return result; diff --git a/src/effects/Effect.cpp b/src/effects/Effect.cpp index 5bd1196e1..3e69d7609 100644 --- a/src/effects/Effect.cpp +++ b/src/effects/Effect.cpp @@ -75,7 +75,7 @@ Effect::Effect() mTracks = NULL; mOutputTracks = NULL; mOutputTracksType = Track::None; - mLength = 0; + mDuration = 0.0; mNumTracks = 0; mNumGroups = 0; mProgress = NULL; @@ -246,8 +246,33 @@ bool Effect::IsInteractive() // EffectHostInterface implementation +double Effect::GetDuration() +{ + if (mT1 > mT0) + { + return mT1 - mT0; + } + + if (mClient->GetType() == EffectTypeGenerate) + { + return sDefaultGenerateLen; + } + + return 0; +} + +bool Effect::SetDuration(double seconds) +{ + mDuration = seconds; + + return true; +} + bool Effect::Apply() { + // This is absolute hackage...but easy + // + // It should callback to the EffectManager to kick off the processing GetActiveProject()->OnEffect(GetID(), true); return true; @@ -384,18 +409,20 @@ bool Effect::SetPrivateConfig(const wxString & group, const wxString & key, cons bool Effect::Startup(EffectClientInterface *client) { - // Need to set host now so client startup can use our services - client->SetHost(this); - - // Bail if the client startup fails - if (!client->Startup()) - { - return false; - } - // Let destructor know we need to be shutdown mClient = client; + // Need to set host now so client startup can use our services + mClient->SetHost(this); + + // Bail if the client startup fails + if (!mClient->Startup()) + { + mClient = NULL; + + return false; + } + mNumAudioIn = mClient->GetAudioInCount(); mNumAudioOut = mClient->GetAudioOutCount(); @@ -416,6 +443,8 @@ bool Effect::Startup(EffectClientInterface *client) } SetEffectFlags(flags); + + return true; } // All legacy effects should have this overridden @@ -588,7 +617,9 @@ bool Effect::Process() return false; } - CopyInputTracks(); + bool isGenerator = mClient->GetType() == EffectTypeGenerate; + + CopyInputTracks(Track::All); bool bGoodResult = true; mInBuffer = NULL; @@ -601,15 +632,34 @@ bool Effect::Process() TrackListIterator iter(mOutputTracks); int count = 0; bool clear = false; - WaveTrack *left = (WaveTrack *) iter.First(); - while (left) + Track* t = iter.First(); + + for (t = iter.First(); t; t = iter.Next()) { + if (t->GetKind() != Track::Wave || !t->GetSelected()) + { + if (t->IsSyncLockSelected()) + { + t->SyncLockAdjust(mT1, mT0 + mDuration); + } + continue; + } + + WaveTrack *left = (WaveTrack *)t; WaveTrack *right; sampleCount len; sampleCount leftStart; sampleCount rightStart; - GetSamples(left, &leftStart, &len); + if (!isGenerator) + { + GetSamples(left, &leftStart, &len); + } + else + { + len = 0; + leftStart = 0; + } mNumChannels = 1; @@ -618,7 +668,10 @@ bool Effect::Process() if (left->GetLinked() && mNumAudioIn > 1) { right = (WaveTrack *) iter.Next(); - GetSamples(right, &rightStart, &len); + if (!isGenerator) + { + GetSamples(right, &rightStart, &len); + } clear = false; mNumChannels = 2; } @@ -725,7 +778,6 @@ bool Effect::Process() break; } - left = (WaveTrack *) iter.Next(); count++; } @@ -797,6 +849,24 @@ bool Effect::ProcessTrack(int count, sampleCount outputBufferCnt = 0; bool cleared = false; + WaveTrack *genLeft = NULL; + WaveTrack *genRight = NULL; + sampleCount genLength = 0; + bool isGenerator = mClient->GetType() == EffectTypeGenerate; + if (isGenerator) + { + genLength = left->GetRate() * mDuration; + delayRemaining = genLength; + cleared = true; + + // Create temporary tracks + genLeft = mFactory->NewWaveTrack(left->GetSampleFormat(), left->GetRate()); + if (right) + { + genRight = mFactory->NewWaveTrack(right->GetSampleFormat(), right->GetRate()); + } + } + // Call the effect until we run out of input or delayed samples while (inputRemaining || delayRemaining) { @@ -910,28 +980,31 @@ bool Effect::ProcessTrack(int count, } // Get the current number of delayed samples and accumulate - sampleCount delay = mClient->GetLatency(); - curDelay += delay; - delayRemaining += delay; + if (!isGenerator) + { + sampleCount delay = mClient->GetLatency(); + curDelay += delay; + delayRemaining += delay; - // If the plugin has delayed the output by more samples than our current - // block size, then we leave the output pointers alone. This effectively - // removes those delayed samples from the output buffer. - if (curDelay >= curBlockSize) - { - curDelay -= curBlockSize; - curBlockSize = 0; - } - // We have some delayed samples, at the beginning of the output samples, - // so overlay them by shifting the remaining output samples. - else if (curDelay > 0) - { - curBlockSize -= curDelay; - for (int i = 0; i < mNumChannels; i++) + // If the plugin has delayed the output by more samples than our current + // block size, then we leave the output pointers alone. This effectively + // removes those delayed samples from the output buffer. + if (curDelay >= curBlockSize) { - memmove(mOutBufPos[i], mOutBufPos[i] + curDelay, SAMPLE_SIZE(floatSample) * curBlockSize); + curDelay -= curBlockSize; + curBlockSize = 0; + } + // We have some delayed samples, at the beginning of the output samples, + // so overlay them by shifting the remaining output samples. + else if (curDelay > 0) + { + curBlockSize -= curDelay; + for (int i = 0; i < mNumChannels; i++) + { + memmove(mOutBufPos[i], mOutBufPos[i] + curDelay, SAMPLE_SIZE(floatSample) * curBlockSize); + } + curDelay = 0; } - curDelay = 0; } // @@ -949,11 +1022,22 @@ bool Effect::ProcessTrack(int count, // Output buffers have filled else { - // Write them out - left->Set((samplePtr) mOutBuffer[0], floatSample, outLeftPos, outputBufferCnt); - if (right) + if (!isGenerator) { - right->Set((samplePtr) mOutBuffer[1], floatSample, outRightPos, outputBufferCnt); + // Write them out + left->Set((samplePtr) mOutBuffer[0], floatSample, outLeftPos, outputBufferCnt); + if (right) + { + right->Set((samplePtr) mOutBuffer[1], floatSample, outRightPos, outputBufferCnt); + } + } + else + { + genLeft->Append((samplePtr) mOutBuffer[0], floatSample, outputBufferCnt); + if (genRight) + { + genRight->Append((samplePtr) mOutBuffer[1], floatSample, outputBufferCnt); + } } // Reset the output buffer positions @@ -996,10 +1080,37 @@ bool Effect::ProcessTrack(int count, // Put any remaining output if (outputBufferCnt) { - left->Set((samplePtr) mOutBuffer[0], floatSample, outLeftPos, outputBufferCnt); - if (right) + if (!isGenerator) { - right->Set((samplePtr) mOutBuffer[1], floatSample, outRightPos, outputBufferCnt); + left->Set((samplePtr) mOutBuffer[0], floatSample, outLeftPos, outputBufferCnt); + if (right) + { + right->Set((samplePtr) mOutBuffer[1], floatSample, outRightPos, outputBufferCnt); + } + } + else + { + genLeft->Append((samplePtr) mOutBuffer[0], floatSample, outputBufferCnt); + if (genRight) + { + genRight->Append((samplePtr) mOutBuffer[1], floatSample, outputBufferCnt); + } + } + } + + if (isGenerator) + { + // Transfer the data from the temporary tracks to the actual ones + genLeft->Flush(); + SetTimeWarper(new StepTimeWarper(mT0 + genLength, genLength - (mT1 - mT0))); + left->ClearAndPaste(mT0, mT1, genLeft, true, true, GetTimeWarper()); + delete genLeft; + + if (genRight) + { + genRight->Flush(); + right->ClearAndPaste(mT0, mT1, genRight, true, true, GetTimeWarper()); + delete genRight; } } @@ -1044,14 +1155,16 @@ void Effect::GetSamples(WaveTrack *track, sampleCount *start, sampleCount *len) double t0 = mT0 < trackStart ? trackStart : mT0; double t1 = mT1 > trackEnd ? trackEnd : mT1; +#if 0 if (GetType() & INSERT_EFFECT) { - t1 = t0 + mLength; + t1 = t0 + mDuration; if (mT0 == mT1) { // Not really part of the calculation, but convenient to put here bool bResult = track->InsertSilence(t0, t1); wxASSERT(bResult); // TO DO: Actually handle this. } } +#endif if (t1 > t0) { *start = track->TimeToLongSamples(t0); diff --git a/src/effects/Effect.h b/src/effects/Effect.h index 5aedbf50a..9936d5515 100644 --- a/src/effects/Effect.h +++ b/src/effects/Effect.h @@ -96,6 +96,9 @@ class AUDACITY_DLL_API Effect : public EffectHostInterface // EffectHostInterface implementation + virtual double GetDuration(); + virtual bool SetDuration(double duration); + virtual bool Apply(); virtual void Preview(); @@ -351,7 +354,7 @@ class AUDACITY_DLL_API Effect : public EffectHostInterface protected: static double sDefaultGenerateLen; int mFlags; - double mLength; + double mDuration; // type of the tracks on mOutputTracks int mOutputTracksType; diff --git a/src/effects/VST/VSTEffect.cpp b/src/effects/VST/VSTEffect.cpp index a1d86a668..5151ed67b 100644 --- a/src/effects/VST/VSTEffect.cpp +++ b/src/effects/VST/VSTEffect.cpp @@ -764,14 +764,10 @@ private: void OnSlider(wxCommandEvent & evt); -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) void OnApply(wxCommandEvent & evt); -#else void OnOk(wxCommandEvent & evt); void OnCancel(wxCommandEvent & evt); void OnPreview(wxCommandEvent & evt); -#endif - void OnDefaults(wxCommandEvent & evt); void OnClose(wxCloseEvent & evt); @@ -858,14 +854,10 @@ enum BEGIN_EVENT_TABLE(VSTEffectDialog, wxDialog) EVT_CLOSE(VSTEffectDialog::OnClose) -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) EVT_BUTTON(wxID_APPLY, VSTEffectDialog::OnApply) -#else EVT_BUTTON(wxID_OK, VSTEffectDialog::OnOk) EVT_BUTTON(wxID_CANCEL, VSTEffectDialog::OnCancel) - EVT_BUTTON(ID_EFFECT_PREVIEW, VSTEffectDialog::OnPreview) -#endif - + EVT_BUTTON(ePreviewID, VSTEffectDialog::OnPreview) EVT_BUTTON(eDefaultsID, VSTEffectDialog::OnDefaults) EVT_COMBOBOX(ID_VST_PROGRAM, VSTEffectDialog::OnProgram) @@ -1511,7 +1503,7 @@ void VSTEffectDialog::BuildFancy() // Add the standard button bar at the bottom #if defined(EXPERIMENTAL_REALTIME_EFFECTS) - vs->Add(CreateStdButtonSizer(this, eApplyButton | eDefaultsButton), 0, wxEXPAND); + vs->Add(CreateStdButtonSizer(this, eApplyButton | eCancelButton | eDefaultsButton), 0, wxEXPAND); #else vs->Add(CreateStdButtonSizer(this, ePreviewButton | eDefaultsButton |eCancelButton | eOkButton), 0, wxEXPAND); #endif @@ -1561,11 +1553,14 @@ void VSTEffectDialog::BuildPlain() vSizer->Add(sw, 1, wxEXPAND | wxALL, 5); // Add the standard button bar at the bottom -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) - vSizer->Add(CreateStdButtonSizer(this, eApplyButton | eDefaultsButton), 0, wxEXPAND); -#else - vSizer->Add(CreateStdButtonSizer(this, ePreviewButton|eDefaultsButton|eCancelButton|eOkButton), 0, wxEXPAND); -#endif + if (mEffect->IsRealtimeCapable()) + { + vSizer->Add(CreateStdButtonSizer(this, eDefaultsButton | eCancelButton | eApplyButton), 0, wxEXPAND); + } + else + { + vSizer->Add(CreateStdButtonSizer(this, ePreviewButton | eDefaultsButton | eCancelButton | eOkButton), 0, wxEXPAND); + } SetSizer(vSizer); @@ -2586,7 +2581,6 @@ void VSTEffectDialog::OnClose(wxCloseEvent & evt) #endif } -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) void VSTEffectDialog::OnApply(wxCommandEvent & WXUNUSED(evt)) { #if defined(__WXMAC__) @@ -2595,9 +2589,11 @@ void VSTEffectDialog::OnApply(wxCommandEvent & WXUNUSED(evt)) Show(false); #endif + mEffect->SaveParameters(wxT("Current")); + mEffect->mHost->Apply(); } -#else + void VSTEffectDialog::OnPreview(wxCommandEvent & WXUNUSED(evt)) { mEffect->mHost->Preview(); @@ -2611,6 +2607,8 @@ void VSTEffectDialog::OnOk(wxCommandEvent & WXUNUSED(evt)) Show(false); #endif + mEffect->SaveParameters(wxT("Current")); + if (mGui) { // mEffect->PowerOff(); @@ -2636,9 +2634,11 @@ void VSTEffectDialog::OnCancel(wxCommandEvent & WXUNUSED(evt)) // mEffect->callDispatcher(effEditClose, 0, 0, NULL, 0.0); } - EndModal(false); + if (IsModal()) + { + EndModal(false); + } } -#endif void VSTEffectDialog::OnDefaults(wxCommandEvent & WXUNUSED(evt)) { @@ -3229,6 +3229,11 @@ wxString VSTEffect::GetDescription() EffectType VSTEffect::GetType() { + if (mAudioIns == 0 && mAudioOuts == 0 && mMidiIns == 0 && mMidiOuts == 0) + { + return EffectTypeNone; + } + if (mAudioIns == 0 && mMidiIns == 0) { return EffectTypeGenerate; diff --git a/src/effects/ladspa/LadspaEffect.cpp b/src/effects/ladspa/LadspaEffect.cpp index cb0eee0ea..9cb1d7ab9 100644 --- a/src/effects/ladspa/LadspaEffect.cpp +++ b/src/effects/ladspa/LadspaEffect.cpp @@ -141,7 +141,7 @@ void LadspaEffectsModule::Terminate() return; } -bool LadspaEffectsModule::AutoRegisterPlugins(PluginManagerInterface & pm) +bool LadspaEffectsModule::AutoRegisterPlugins(PluginManagerInterface & WXUNUSED(pm)) { return false; } @@ -310,7 +310,7 @@ bool LadspaEffectsModule::RegisterPlugin(PluginManagerInterface & pm, const wxSt int index = 0; LADSPA_Descriptor_Function mainFn = NULL; wxDynamicLibrary lib; - if (lib.Load(path, wxDL_LAZY)) { + if (lib.Load(path, wxDL_NOW)) { wxLogNull logNo; mainFn = (LADSPA_Descriptor_Function) lib.GetSymbol(wxT("ladspa_descriptor")); @@ -369,12 +369,15 @@ LadspaEffect::LadspaEffect(const wxString & path, int index) mAudioIns = 0; mAudioOuts = 0; mNumInputControls = 0; + mSampleRate = 44100; mInputPorts = NULL; mOutputPorts = NULL; mInputControls = NULL; mOutputControls = NULL; + mLatencyPort = -1; + mDlg = NULL; } @@ -442,6 +445,11 @@ wxString LadspaEffect::GetDescription() // EffectType LadspaEffect::GetType() { + if (mAudioIns == 0 && mAudioOuts == 0) + { + return EffectTypeNone; + } + if (mAudioIns == 0) { return EffectTypeGenerate; @@ -477,7 +485,7 @@ bool LadspaEffect::IsLegacy() bool LadspaEffect::IsRealtimeCapable() { - return true; + return GetType() == EffectTypeProcess; } // @@ -503,6 +511,7 @@ bool LadspaEffect::Startup() for (unsigned long p = 0; p < mData->PortCount; p++) { LADSPA_PortDescriptor d = mData->PortDescriptors[p]; + // Collect the audio ports if (LADSPA_IS_PORT_AUDIO(d)) { if (LADSPA_IS_PORT_INPUT(d)) @@ -514,8 +523,8 @@ bool LadspaEffect::Startup() mOutputPorts[mAudioOuts++] = p; } } - - if (LADSPA_IS_PORT_CONTROL(d) && LADSPA_IS_PORT_INPUT(d)) + // Determine the port's default value + else if (LADSPA_IS_PORT_CONTROL(d) && LADSPA_IS_PORT_INPUT(d)) { mInteractive = true; @@ -585,6 +594,16 @@ bool LadspaEffect::Startup() mNumInputControls++; mInputControls[p] = val; } + // Ladspa effects have a convention of providing latency on an output + // control port whose name is "latency". + else if (LADSPA_IS_PORT_CONTROL(d) && LADSPA_IS_PORT_OUTPUT(d)) + { + if (strcmp(mData->PortNames[p], "latency") == 0) + { + mLatencyPort = p; + mOutputControls[p] = 0; + } + } } // mHost will be null during registration @@ -609,12 +628,6 @@ bool LadspaEffect::Startup() bool LadspaEffect::Shutdown() { - // mHost will be null during registration - if (mHost) - { - SaveParameters(wxT("Current")); - } - if (mInputPorts) { delete [] mInputPorts; @@ -686,8 +699,12 @@ sampleCount LadspaEffect::GetBlockSize(sampleCount maxBlockSize) sampleCount LadspaEffect::GetLatency() { - // Ladspa effects have a convention of providing latency on an output - // control port name "latency". + if (mLatencyPort >= 0 && !mLatencyDone) + { + mLatencyDone = true; + return mOutputControls[mLatencyPort] * 2; + } + return 0; } @@ -707,9 +724,15 @@ bool LadspaEffect::ProcessInitialize() if (!mReady) { mMaster = InitInstance(mSampleRate); + if (!mMaster) + { + return false; + } mReady = true; } + mLatencyDone = false; + return true; } @@ -797,6 +820,11 @@ sampleCount LadspaEffect::RealtimeProcess(int group, bool LadspaEffect::RealtimeAddProcessor(int numChannels, float sampleRate) { LADSPA_Handle slave = InitInstance(sampleRate); + if (!slave) + { + return false; + } + mSlaves.Add(slave); mSlaveChannels.Add(numChannels); @@ -809,7 +837,12 @@ bool LadspaEffect::ShowInterface(void *parent) return false; } - double length = 0; //mT1 > mT0 ? mT1 - mT0 : sDefaultGenerateLen; + double length = 0; + + if (!IsRealtimeCapable()) + { + length = mHost->GetDuration(); + } if (!mDlg) { @@ -817,18 +850,23 @@ bool LadspaEffect::ShowInterface(void *parent) mDlg->CentreOnParent(); } -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) - mDlg->Show(!mDlg->IsShown()); + if (IsRealtimeCapable()) + { + mDlg->Show(!mDlg->IsShown()); + return true; + } - return true; -#else mDlg->ShowModal(); bool ret = mDlg->GetReturnCode() != 0; + if (ret) + { + mHost->SetDuration(mDlg->GetLength()); + } mDlg->Destroy(); mDlg = NULL; + return ret; -#endif } // @@ -842,7 +880,7 @@ bool LadspaEffect::Load() } LADSPA_Descriptor_Function mainFn = NULL; - if (mLib.Load(mPath, wxDL_LAZY)) + if (mLib.Load(mPath, wxDL_NOW)) { wxLogNull logNo; @@ -908,6 +946,10 @@ LADSPA_Handle LadspaEffect::InitInstance(float sampleRate) { /* Instantiate the plugin */ LADSPA_Handle handle = mData->instantiate(mData, sampleRate); + if (!handle) + { + return NULL; + } for (unsigned long p = 0; p < mData->PortCount; p++) { @@ -958,14 +1000,14 @@ class Slider:public wxSlider { }; - void OnSetFocus(wxFocusEvent &event) + void OnSetFocus(wxFocusEvent & evt) { wxScrolledWindow *p = (wxScrolledWindow *) GetParent(); wxRect r = GetRect(); wxRect rv = p->GetRect(); rv.y = 0; - event.Skip(); + evt.Skip(); int y; int yppu; @@ -1008,14 +1050,14 @@ class TextCtrl:public wxTextCtrl { }; - void OnSetFocus(wxFocusEvent &event) + void OnSetFocus(wxFocusEvent & evt) { wxScrolledWindow *p = (wxScrolledWindow *) GetParent(); wxRect r = GetRect(); wxRect rv = p->GetRect(); rv.y = 0; - event.Skip(); + evt.Skip(); int y; int yppu; @@ -1041,22 +1083,20 @@ class TextCtrl:public wxTextCtrl }; BEGIN_EVENT_TABLE(TextCtrl, wxTextCtrl) - EVT_SET_FOCUS(TextCtrl::OnSetFocus) + EVT_SET_FOCUS(TextCtrl::OnSetFocus) END_EVENT_TABLE() const int LADSPA_SECONDS_ID = 13101; BEGIN_EVENT_TABLE(LadspaEffectDialog, wxDialog) -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) - EVT_BUTTON(wxID_APPLY, LadspaEffectDialog::OnApply) -#else - EVT_BUTTON(wxID_OK, LadspaEffectDialog::OnOK) - EVT_BUTTON(wxID_CANCEL, LadspaEffectDialog::OnCancel) - EVT_BUTTON(ID_EFFECT_PREVIEW, LadspaEffectDialog::OnPreview) -#endif - EVT_SLIDER(wxID_ANY, LadspaEffectDialog::OnSlider) - EVT_TEXT(wxID_ANY, LadspaEffectDialog::OnTextCtrl) - EVT_CHECKBOX(wxID_ANY, LadspaEffectDialog::OnCheckBox) + EVT_BUTTON(wxID_APPLY, LadspaEffectDialog::OnApply) + EVT_BUTTON(wxID_OK, LadspaEffectDialog::OnOK) + EVT_BUTTON(wxID_CANCEL, LadspaEffectDialog::OnCancel) + EVT_BUTTON(ePreviewID, LadspaEffectDialog::OnPreview) + EVT_BUTTON(eDefaultsID, LadspaEffectDialog::OnDefaults) + EVT_SLIDER(wxID_ANY, LadspaEffectDialog::OnSlider) + EVT_TEXT(wxID_ANY, LadspaEffectDialog::OnTextCtrl) + EVT_CHECKBOX(wxID_ANY, LadspaEffectDialog::OnCheckBox) END_EVENT_TABLE() IMPLEMENT_CLASS(LadspaEffectDialog, wxDialog) @@ -1070,17 +1110,16 @@ LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff, :wxDialog(parent, -1, LAT1CTOWX(data->Name), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER), - effect(eff) + mEffect(eff) { - mLength = length; - numParams = 0; + mNumParams = 0; mData = data; mInputControls = inputControls; - sampleRate = sampleRate; + mSampleRate = sampleRate; #ifdef __WXMSW__ // On Windows, for some reason, wxWidgets calls OnTextCtrl during creation // of the text control, and LadspaEffectDialog::OnTextCtrl calls HandleText, - // which assumes all the fields have been initialized. + // which assumes all the mFields have been initialized. // This can give us a bad pointer crash, so manipulate inSlider to // no-op HandleText during creation. inSlider = true; @@ -1089,19 +1128,19 @@ LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff, #endif inText = false; - toggles = new wxCheckBox*[mData->PortCount]; - sliders = new wxSlider*[mData->PortCount]; - fields = new wxTextCtrl*[mData->PortCount]; - labels = new wxStaticText*[mData->PortCount]; - ports = new unsigned long [mData->PortCount]; + mToggles = new wxCheckBox*[mData->PortCount]; + mSliders = new wxSlider*[mData->PortCount]; + mFields = new wxTextCtrl*[mData->PortCount]; + mLabels = new wxStaticText*[mData->PortCount]; + mPorts = new unsigned long [mData->PortCount]; unsigned long p; for(p=0; pPortCount; p++) { LADSPA_PortDescriptor d = mData->PortDescriptors[p]; if (LADSPA_IS_PORT_CONTROL(d) && LADSPA_IS_PORT_INPUT(d)) { - ports[numParams] = p; - numParams++; + mPorts[mNumParams] = p; + mNumParams++; } } @@ -1141,11 +1180,12 @@ LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff, vSizer->Add(w, 1, wxEXPAND|wxALL, 5); // Preview, OK, & Cancel buttons -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) - vSizer->Add(CreateStdButtonSizer(this, eApplyButton | eDefaultsButton), 0, wxEXPAND); -#else - vSizer->Add(CreateStdButtonSizer(this, ePreviewButton | eDefaultsButton |eCancelButton | eOkButton), 0, wxEXPAND); -#endif + if (mEffect->IsRealtimeCapable()) { + vSizer->Add(CreateStdButtonSizer(this, eApplyButton | eCancelButton | eDefaultsButton), 0, wxEXPAND); + } + else { + vSizer->Add(CreateStdButtonSizer(this, ePreviewButton | eDefaultsButton | eCancelButton | eOkButton), 0, wxEXPAND); + } SetSizer(vSizer); @@ -1156,36 +1196,26 @@ LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff, new wxFlexGridSizer(5, 0, 0); gridSizer->AddGrowableCol(3); - for (p = 0; p < numParams; p++) { - wxString labelText = LAT1CTOWX(mData->PortNames[ports[p]]); + for (p = 0; p < mNumParams; p++) { + wxString labelText = LAT1CTOWX(mData->PortNames[mPorts[p]]); item = new wxStaticText(w, 0, labelText + wxT(":")); gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5); wxString fieldText; - LADSPA_PortRangeHint hint = mData->PortRangeHints[ports[p]]; + LADSPA_PortRangeHint hint = mData->PortRangeHints[mPorts[p]]; if (LADSPA_IS_HINT_TOGGLED(hint.HintDescriptor)) { - toggles[p] = new wxCheckBox(w, p, wxT("")); - toggles[p]->SetName(labelText); - toggles[p]->SetValue(mInputControls[ports[p]] > 0); - gridSizer->Add(toggles[p], 0, wxALL, 5); - ConnectFocus(toggles[p]); + mToggles[p] = new wxCheckBox(w, p, wxT("")); + mToggles[p]->SetName(labelText); + mToggles[p]->SetValue(mInputControls[mPorts[p]] > 0); + gridSizer->Add(mToggles[p], 0, wxALL, 5); + ConnectFocus(mToggles[p]); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); } else { - if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor)) - fieldText.Printf(wxT("%d"), (int)(mInputControls[ports[p]] + 0.5)); - else - fieldText = Internat::ToDisplayString(mInputControls[ports[p]]); - - fields[p] = new wxTextCtrl(w, p, fieldText); - fields[p]->SetName(labelText); - gridSizer->Add(fields[p], 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - ConnectFocus(fields[p]); - wxString bound; double lower = 0.0; double upper = 0.0; @@ -1202,11 +1232,21 @@ LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff, hashi = true; } if (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor)) { - lower *= sampleRate * 1000; - upper *= sampleRate; + lower *= mSampleRate; + upper *= mSampleRate; forceint = true; } + if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint) + fieldText.Printf(wxT("%d"), (int)(mInputControls[mPorts[p]] + 0.5)); + else + fieldText = Internat::ToDisplayString(mInputControls[mPorts[p]]); + + mFields[p] = new wxTextCtrl(w, p, fieldText); + mFields[p]->SetName(labelText); + gridSizer->Add(mFields[p], 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); + ConnectFocus(mFields[p]); + wxString str; if (haslo) { if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint) @@ -1220,14 +1260,14 @@ LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff, gridSizer->Add(1, 1, 0); } - sliders[p] = + mSliders[p] = new wxSlider(w, p, 0, 0, 1000, wxDefaultPosition, wxSize(200, -1)); - sliders[p]->SetName(labelText); - gridSizer->Add(sliders[p], 0, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxALL, 5); - ConnectFocus(sliders[p]); + mSliders[p]->SetName(labelText); + gridSizer->Add(mSliders[p], 0, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxALL, 5); + ConnectFocus(mSliders[p]); if (hashi) { if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint) @@ -1244,11 +1284,19 @@ LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff, } // Now add the length control - if (effect->GetType() == EffectTypeGenerate) { - item = new wxStaticText(w, 0, _("Length (seconds)")); - gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); - mSeconds = new wxTextCtrl(w, LADSPA_SECONDS_ID, Internat::ToDisplayString(length)); - mSeconds->SetName(_("Length (seconds)")); + if (mEffect->GetType() == EffectTypeGenerate) { + item = new wxStaticText(w, 0, _("Length (seconds):")); + gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5); + mSeconds = new TimeTextCtrl(w, + wxID_ANY, + _("hh:mm:ss + milliseconds"), + length, + mSampleRate, + wxDefaultPosition, + wxDefaultSize, + true); + mSeconds->SetName(_("Duration")); + mSeconds->EnableMenu(); gridSizer->Add(mSeconds, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5); gridSizer->Add(1, 1, 0); gridSizer->Add(1, 1, 0); @@ -1256,10 +1304,11 @@ LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff, ConnectFocus(mSeconds); } - // Set all of the sliders based on the value in the - // text fields + // Set all of the mSliders based on the value in the + // text mFields inSlider = false; // Now we're ready for HandleText to actually do something. - HandleText(); + mEffect->LoadParameters(wxT("Current")); +// HandleText(); paramSizer->Add(gridSizer, 1, wxEXPAND | wxALL, 5); w->SetSizer(paramSizer); @@ -1271,23 +1320,23 @@ LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff, LadspaEffectDialog::~LadspaEffectDialog() { - delete[]toggles; - delete[]sliders; - delete[]fields; - delete[]labels; - delete[]ports; + delete[]mToggles; + delete[]mSliders; + delete[]mFields; + delete[]mLabels; + delete[]mPorts; } -void LadspaEffectDialog::OnCheckBox(wxCommandEvent &event) +void LadspaEffectDialog::OnCheckBox(wxCommandEvent & evt) { - int p = event.GetId(); + int p = evt.GetId(); - mInputControls[ports[p]] = toggles[p]->GetValue(); + mInputControls[mPorts[p]] = mToggles[p]->GetValue(); } -void LadspaEffectDialog::OnSlider(wxCommandEvent &event) +void LadspaEffectDialog::OnSlider(wxCommandEvent & evt) { - int p = event.GetId(); + int p = evt.GetId(); // if we don't add the following three lines, changing // the value of the slider will change the text, which @@ -1303,20 +1352,20 @@ void LadspaEffectDialog::OnSlider(wxCommandEvent &event) float range; bool forceint = false; - LADSPA_PortRangeHint hint = mData->PortRangeHints[ports[p]]; + LADSPA_PortRangeHint hint = mData->PortRangeHints[mPorts[p]]; if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) lower = hint.LowerBound; if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) upper = hint.UpperBound; if (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor)) { - lower *= sampleRate; - upper *= sampleRate; + lower *= mSampleRate; + upper *= mSampleRate; forceint = true; } range = upper - lower; - val = (sliders[p]->GetValue() / 1000.0) * range + lower; + val = (mSliders[p]->GetValue() / 1000.0) * range + lower; wxString str; if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint) @@ -1324,14 +1373,14 @@ void LadspaEffectDialog::OnSlider(wxCommandEvent &event) else str = Internat::ToDisplayString(val); - fields[p]->SetValue(str); + mFields[p]->SetValue(str); - mInputControls[ports[p]] = val; + mInputControls[mPorts[p]] = val; inSlider = false; } -void LadspaEffectDialog::OnTextCtrl(wxCommandEvent & WXUNUSED(event)) +void LadspaEffectDialog::OnTextCtrl(wxCommandEvent & WXUNUSED(evt)) { HandleText(); } @@ -1346,19 +1395,19 @@ void LadspaEffectDialog::HandleText() if (inSlider) return; inText = true; - for (unsigned long p = 0; p < numParams; p++) { + for (unsigned long p = 0; p < mNumParams; p++) { double dval; float val; float lower = float(0.0); float upper = float(10.0); float range; - LADSPA_PortRangeHint hint = mData->PortRangeHints[ports[p]]; + LADSPA_PortRangeHint hint = mData->PortRangeHints[mPorts[p]]; if (LADSPA_IS_HINT_TOGGLED(hint.HintDescriptor)) { continue; } - dval = Internat::CompatibleToDouble(fields[p]->GetValue()); + dval = Internat::CompatibleToDouble(mFields[p]->GetValue()); val = dval; if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) @@ -1366,8 +1415,8 @@ void LadspaEffectDialog::HandleText() if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) upper = hint.UpperBound; if (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor)) { - lower *= sampleRate; - upper *= sampleRate; + lower *= mSampleRate; + upper *= mSampleRate; } range = upper - lower; @@ -1376,15 +1425,14 @@ void LadspaEffectDialog::HandleText() if (val > upper) val = upper; - mInputControls[ports[p]] = val; + mInputControls[mPorts[p]] = val; - sliders[p]->SetValue((int)(((val-lower)/range) * 1000.0 + 0.5)); + mSliders[p]->SetValue((int)(((val-lower)/range) * 1000.0 + 0.5)); } inText = false; } -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) void LadspaEffectDialog::OnApply(wxCommandEvent & WXUNUSED(evt)) { #if defined(__WXMAC__) @@ -1392,25 +1440,85 @@ void LadspaEffectDialog::OnApply(wxCommandEvent & WXUNUSED(evt)) #else Show(false); #endif + mEffect->SaveParameters(wxT("Current")); - effect->mHost->Apply(); + mEffect->mHost->Apply(); } -#else -void LadspaEffectDialog::OnOK(wxCommandEvent & WXUNUSED(event)) + +void LadspaEffectDialog::OnOK(wxCommandEvent & WXUNUSED(evt)) { + mEffect->SaveParameters(wxT("Current")); + EndModal(TRUE); } -void LadspaEffectDialog::OnCancel(wxCommandEvent & WXUNUSED(event)) +void LadspaEffectDialog::OnCancel(wxCommandEvent & WXUNUSED(evt)) { - EndModal(FALSE); + if (IsModal()) + { + EndModal(FALSE); + } + else + { +#if defined(__WXMAC__) + Close(); +#else + Show(false); +#endif + } } -void LadspaEffectDialog::OnPreview(wxCommandEvent & WXUNUSED(event)) +void LadspaEffectDialog::OnPreview(wxCommandEvent & WXUNUSED(evt)) { - effect->Preview(); + mEffect->mHost->Preview(); +} + +void LadspaEffectDialog::OnDefaults(wxCommandEvent & WXUNUSED(evt)) +{ + mEffect->LoadParameters(wxT("Default")); + RefreshParaemters(); +} + +void LadspaEffectDialog::RefreshParaemters() +{ + for (unsigned long p = 0; p < mNumParams; p++) { + LADSPA_PortRangeHint hint = mData->PortRangeHints[mPorts[p]]; + + if (LADSPA_IS_HINT_TOGGLED(hint.HintDescriptor)) { + mToggles[p]->SetValue(mInputControls[mPorts[p]] > 0); + } + else { + wxString bound; + double lower = 0.0; + double upper = 0.0; + bool haslo = false; + bool hashi = false; + bool forceint = false; + + if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor)) { + lower = hint.LowerBound; + haslo = true; + } + if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor)) { + upper = hint.UpperBound; + hashi = true; + } + if (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor)) { + lower *= mSampleRate; + upper *= mSampleRate; + forceint = true; + } + + wxString fieldText; + if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint) + fieldText.Printf(wxT("%d"), (int)(mInputControls[mPorts[p]] + 0.5)); + else + fieldText = Internat::ToDisplayString(mInputControls[mPorts[p]]); + mFields[p]->ChangeValue(fieldText); + } + } + HandleText(); } -#endif void LadspaEffectDialog::ConnectFocus(wxControl *c) { @@ -1424,15 +1532,15 @@ void LadspaEffectDialog::DisconnectFocus(wxControl *c) wxFocusEventHandler(LadspaEffectDialog::ControlSetFocus)); } -void LadspaEffectDialog::ControlSetFocus(wxFocusEvent &event) +void LadspaEffectDialog::ControlSetFocus(wxFocusEvent & evt) { - wxControl *c = (wxControl *) event.GetEventObject(); + wxControl *c = (wxControl *) evt.GetEventObject(); wxScrolledWindow *p = (wxScrolledWindow *) c->GetParent(); wxRect r = c->GetRect(); wxRect rv = p->GetRect(); rv.y = 0; - event.Skip(); + evt.Skip(); int y; int yppu; @@ -1456,9 +1564,9 @@ void LadspaEffectDialog::ControlSetFocus(wxFocusEvent &event) double LadspaEffectDialog::GetLength() { - if (effect->GetType() == EffectTypeGenerate) { - mLength = Internat::CompatibleToDouble(mSeconds->GetValue()); + if (mEffect->GetType() == EffectTypeGenerate) { + return mSeconds->GetTimeValue(); } - return mLength; + return 0; } diff --git a/src/effects/ladspa/LadspaEffect.h b/src/effects/ladspa/LadspaEffect.h index c2a5cfd83..134bd8b0d 100644 --- a/src/effects/ladspa/LadspaEffect.h +++ b/src/effects/ladspa/LadspaEffect.h @@ -19,6 +19,8 @@ class wxCheckBox; #include "audacity/ModuleInterface.h" #include "audacity/PluginInterface.h" +#include "../../widgets/TimeTextCtrl.h" + #include "ladspa.h" #define LADSPAEFFECTS_VERSION wxT("1.0.0.0"); @@ -126,6 +128,9 @@ private: float *mInputControls; float *mOutputControls; + int mLatencyPort; + bool mLatencyDone; + // Realtime processing LadspaSlaveArray mSlaves; wxArrayInt mSlaveChannels; @@ -185,39 +190,39 @@ class LadspaEffectDialog : public wxDialog ~LadspaEffectDialog(); - void OnCheckBox(wxCommandEvent & event); - void OnSlider(wxCommandEvent & event); - void OnTextCtrl(wxCommandEvent & event); -#if defined(EXPERIMENTAL_REALTIME_EFFECTS) - void OnApply(wxCommandEvent & evt); -#else - void OnOk(wxCommandEvent & evt); - void OnCancel(wxCommandEvent & evt); - void OnPreview(wxCommandEvent & evt); -#endif - void ControlSetFocus(wxFocusEvent & event); - double GetLength(); DECLARE_EVENT_TABLE() private: + void OnCheckBox(wxCommandEvent & evt); + void OnSlider(wxCommandEvent & evt); + void OnTextCtrl(wxCommandEvent & evt); + void OnApply(wxCommandEvent & evt); + void OnOK(wxCommandEvent & evt); + void OnCancel(wxCommandEvent & evt); + void OnPreview(wxCommandEvent & evt); + void OnDefaults(wxCommandEvent & evt); + void HandleText(); + void RefreshParaemters(); void ConnectFocus(wxControl *c); void DisconnectFocus(wxControl *c); + void ControlSetFocus(wxFocusEvent & evt); + +private: bool inSlider; bool inText; - double mLength; - int sampleRate; + int mSampleRate; const LADSPA_Descriptor *mData; - wxSlider **sliders; - wxTextCtrl **fields; - wxStaticText **labels; - wxCheckBox **toggles; - unsigned long *ports; - unsigned long numParams; + wxSlider **mSliders; + wxTextCtrl **mFields; + wxStaticText **mLabels; + wxCheckBox **mToggles; + unsigned long *mPorts; + unsigned long mNumParams; float *mInputControls; - LadspaEffect *effect; - wxTextCtrl *mSeconds; + LadspaEffect *mEffect; + TimeTextCtrl *mSeconds; }; diff --git a/src/effects/lv2/LV2Effect.cpp b/src/effects/lv2/LV2Effect.cpp index f93e4cf33..995eadda7 100644 --- a/src/effects/lv2/LV2Effect.cpp +++ b/src/effects/lv2/LV2Effect.cpp @@ -79,7 +79,7 @@ LV2Effect::LV2Effect(const LilvPlugin *data, fInBuffer = NULL; fOutBuffer = NULL; - mLength = 0; + mDuration = 0; // Allocate buffers for the port indices and the default control values int numPorts = lilv_plugin_get_num_ports(mData); @@ -386,7 +386,7 @@ bool LV2Effect::PromptUser() return false; } - mLength = dlog.GetLength(); + mDuration = dlog.GetLength(); mNoteLength = dlog.GetNoteLength(); mNoteVelocity = dlog.GetNoteVelocity(); mNoteKey = dlog.GetNoteKey();