1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 16:10:06 +02:00

More effect changes to new format and realtime preview

Main effect host processing extended to support generate
effects and sync locked tracks.

Ladspa updated to utilize new generate support.

I'll address Analyze plugins when I get to the SBSMS ones.

Shared and private config changes are flushed immediately.

Cancel button restored to VST and Ladspa dialogs.  This
also restores the ESC button functionality.

Current parameters saved with Apply or Ok clicked...not
when Audacity ends.

Ladspa and VST effects with that reports no input and
no outputs are now ignored.

Ladspa effects providing a latency value is now handled.

Ladspa generator effects now use the TimeTextCtrl for
specifying duration.
This commit is contained in:
lllucius 2014-11-07 09:54:04 +00:00
parent ed1338cdec
commit 7bb4b7c941
8 changed files with 471 additions and 214 deletions

View File

@ -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;
};

View File

@ -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;

View File

@ -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;
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();
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,6 +980,8 @@ bool Effect::ProcessTrack(int count,
}
// Get the current number of delayed samples and accumulate
if (!isGenerator)
{
sampleCount delay = mClient->GetLatency();
curDelay += delay;
delayRemaining += delay;
@ -933,6 +1005,7 @@ bool Effect::ProcessTrack(int count,
}
curDelay = 0;
}
}
//
outputBufferCnt += curBlockSize;
@ -948,6 +1021,8 @@ bool Effect::ProcessTrack(int count,
}
// Output buffers have filled
else
{
if (!isGenerator)
{
// Write them out
left->Set((samplePtr) mOutBuffer[0], floatSample, outLeftPos, outputBufferCnt);
@ -955,6 +1030,15 @@ bool Effect::ProcessTrack(int count,
{
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
for (int i = 0; i < mNumChannels; i++)
@ -995,6 +1079,8 @@ bool Effect::ProcessTrack(int count,
// Put any remaining output
if (outputBufferCnt)
{
if (!isGenerator)
{
left->Set((samplePtr) mOutBuffer[0], floatSample, outLeftPos, outputBufferCnt);
if (right)
@ -1002,6 +1088,31 @@ bool Effect::ProcessTrack(int count,
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;
}
}
// Allow the plugin to cleanup
mClient->ProcessFinalize();
@ -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);

View File

@ -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;

View File

@ -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);
}
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;

View File

@ -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)
if (IsRealtimeCapable())
{
mDlg->Show(!mDlg->IsShown());
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;
@ -1047,13 +1089,11 @@ 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_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)
@ -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; p<mData->PortCount; 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))
{
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;
}

View File

@ -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;
};

View File

@ -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();