mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-16 08:09:32 +02:00
889 lines
24 KiB
C++
889 lines
24 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
LadspaEffect.cpp
|
|
|
|
Dominic Mazzoni
|
|
|
|
This class implements a Ladspa Plug-in effect.
|
|
|
|
*******************************************************************//**
|
|
|
|
\class LadspaEffect
|
|
\brief An Effect that calls up a LADSPA plug in, i.e. many possible
|
|
effects from this one class.
|
|
|
|
*//****************************************************************//**
|
|
|
|
\class LadspaEffectDialog
|
|
\brief Dialog used with Effect
|
|
|
|
*//*******************************************************************/
|
|
|
|
|
|
#include "../../Audacity.h"
|
|
|
|
#include "ladspa.h"
|
|
|
|
#include <wx/wxprec.h>
|
|
#include <wx/button.h>
|
|
#include <wx/checkbox.h>
|
|
#include <wx/msgdlg.h>
|
|
#include <wx/sizer.h>
|
|
#include <wx/slider.h>
|
|
#include <wx/statbox.h>
|
|
#include <wx/stattext.h>
|
|
#include <wx/textctrl.h>
|
|
#include <wx/tokenzr.h>
|
|
#include <wx/intl.h>
|
|
#include <wx/scrolwin.h>
|
|
|
|
#include "../Effect.h" // Audacity Effect base class
|
|
#include "LadspaEffect.h" // This class's header file
|
|
#include "../../Internat.h"
|
|
|
|
LadspaEffect::LadspaEffect(const LADSPA_Descriptor *data,
|
|
const std::set<wxString>& categories)
|
|
: mCategories(categories) {
|
|
|
|
mData = data;
|
|
pluginName = LAT1CTOWX(mData->Name);
|
|
|
|
fInBuffer = NULL;
|
|
fOutBuffer = NULL;
|
|
|
|
inputs = 0;
|
|
outputs = 0;
|
|
numInputControls = 0;
|
|
mLength = 0;
|
|
|
|
unsigned long p;
|
|
|
|
inputPorts = new unsigned long [mData->PortCount];
|
|
outputPorts = new unsigned long [mData->PortCount];
|
|
inputControls = new float [mData->PortCount];
|
|
outputControls = new float [mData->PortCount];
|
|
|
|
for(p=0; p<mData->PortCount; p++) {
|
|
LADSPA_PortDescriptor d = mData->PortDescriptors[p];
|
|
if (LADSPA_IS_PORT_AUDIO(d)) {
|
|
if (LADSPA_IS_PORT_INPUT(d)) {
|
|
inputPorts[inputs] = p;
|
|
inputs++;
|
|
}
|
|
else if (LADSPA_IS_PORT_OUTPUT(d)) {
|
|
outputPorts[outputs] = p;
|
|
outputs++;
|
|
}
|
|
}
|
|
if (LADSPA_IS_PORT_CONTROL(d) &&
|
|
LADSPA_IS_PORT_INPUT(d)) {
|
|
numInputControls++;
|
|
|
|
float val = float(1.0);
|
|
LADSPA_PortRangeHint hint = mData->PortRangeHints[p];
|
|
|
|
if (LADSPA_IS_HINT_BOUNDED_BELOW(hint.HintDescriptor) &&
|
|
val < hint.LowerBound)
|
|
val = hint.LowerBound;
|
|
|
|
if (LADSPA_IS_HINT_BOUNDED_ABOVE(hint.HintDescriptor) &&
|
|
val > hint.UpperBound)
|
|
val = hint.UpperBound;
|
|
|
|
if (LADSPA_IS_HINT_DEFAULT_MINIMUM(hint.HintDescriptor))
|
|
val = hint.LowerBound;
|
|
|
|
if (LADSPA_IS_HINT_DEFAULT_LOW(hint.HintDescriptor))
|
|
val = hint.LowerBound * 0.75f + hint.UpperBound * 0.25f;
|
|
|
|
if (LADSPA_IS_HINT_DEFAULT_MIDDLE(hint.HintDescriptor))
|
|
val = hint.LowerBound * 0.5f + hint.UpperBound * 0.5f;
|
|
|
|
if (LADSPA_IS_HINT_DEFAULT_HIGH(hint.HintDescriptor))
|
|
val = hint.LowerBound * 0.25f + hint.UpperBound * 0.75f;
|
|
|
|
if (LADSPA_IS_HINT_DEFAULT_MAXIMUM(hint.HintDescriptor))
|
|
val = hint.UpperBound;
|
|
|
|
if (LADSPA_IS_HINT_SAMPLE_RATE(hint.HintDescriptor))
|
|
val *= mProjectRate;
|
|
|
|
if (LADSPA_IS_HINT_DEFAULT_0(hint.HintDescriptor))
|
|
val = 0.0f;
|
|
|
|
if (LADSPA_IS_HINT_DEFAULT_1(hint.HintDescriptor))
|
|
val = 1.0f;
|
|
|
|
if (LADSPA_IS_HINT_DEFAULT_100(hint.HintDescriptor))
|
|
val = 100.0f;
|
|
|
|
if (LADSPA_IS_HINT_DEFAULT_440(hint.HintDescriptor))
|
|
val = 440.0f;
|
|
|
|
inputControls[p] = val;
|
|
}
|
|
}
|
|
|
|
int flags = PLUGIN_EFFECT;
|
|
if (inputs == 0)
|
|
flags |= INSERT_EFFECT;
|
|
else if (outputs == 0)
|
|
flags |= ANALYZE_EFFECT;
|
|
else
|
|
flags |= PROCESS_EFFECT;
|
|
|
|
SetEffectFlags(flags);
|
|
}
|
|
|
|
LadspaEffect::~LadspaEffect()
|
|
{
|
|
delete[] inputPorts;
|
|
delete[] outputPorts;
|
|
delete[] inputControls;
|
|
delete[] outputControls;
|
|
}
|
|
|
|
wxString LadspaEffect::GetEffectName()
|
|
{
|
|
if (numInputControls > 0)
|
|
return pluginName + wxT("...");
|
|
else
|
|
return pluginName;
|
|
}
|
|
|
|
std::set<wxString> LadspaEffect::GetEffectCategories()
|
|
{
|
|
return mCategories;
|
|
}
|
|
|
|
wxString LadspaEffect::GetEffectIdentifier()
|
|
{
|
|
wxStringTokenizer st(pluginName, wxT(" "));
|
|
wxString id;
|
|
|
|
// CamelCase the name
|
|
while (st.HasMoreTokens()) {
|
|
wxString tok = st.GetNextToken();
|
|
|
|
id += tok.Left(1).MakeUpper() + tok.Mid(1);
|
|
}
|
|
|
|
return id;
|
|
}
|
|
|
|
wxString LadspaEffect::GetEffectAction()
|
|
{
|
|
return wxString::Format(_("Performing Effect: %s"),
|
|
pluginName.c_str());
|
|
}
|
|
|
|
bool LadspaEffect::Init()
|
|
{
|
|
mBlockSize = 0;
|
|
mainRate = 0;
|
|
|
|
TrackListOfKindIterator iter(Track::Wave, mTracks);
|
|
Track *left = iter.First();
|
|
while(left) {
|
|
if (mainRate == 0)
|
|
mainRate = (int)(((WaveTrack *)left)->GetRate() + 0.5);
|
|
|
|
if (left->GetLinked()) {
|
|
Track *right = iter.Next();
|
|
|
|
if (((WaveTrack *)left)->GetRate() !=
|
|
((WaveTrack *)right)->GetRate()) {
|
|
wxMessageBox(_("Sorry, Plug-in Effects cannot be performed on stereo tracks where the individual channels of the track do not match."));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
left = iter.Next();
|
|
}
|
|
|
|
if (mainRate<=0)
|
|
mainRate = (int)(mProjectRate + 0.5);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool LadspaEffect::PromptUser()
|
|
{
|
|
if (numInputControls > 0) {
|
|
double length = mT1 > mT0 ? mT1 - mT0 : sDefaultGenerateLen;
|
|
|
|
LadspaEffectDialog dlog(this, mParent, mData, inputControls, mainRate, length);
|
|
dlog.CentreOnParent();
|
|
dlog.ShowModal();
|
|
|
|
if (!dlog.GetReturnCode())
|
|
return false;
|
|
|
|
mLength = dlog.GetLength();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool LadspaEffect::Process()
|
|
{
|
|
this->CopyInputTracks(); // Set up mOutputTracks.
|
|
bool bGoodResult = true;
|
|
|
|
TrackListIterator iter(mOutputTracks);
|
|
int count = 0;
|
|
Track *left = iter.First();
|
|
Track *right;
|
|
while(left) {
|
|
sampleCount lstart = 0, rstart = 0;
|
|
sampleCount len;
|
|
GetSamples((WaveTrack *)left, &lstart, &len);
|
|
|
|
right = NULL;
|
|
if (left->GetLinked() && inputs>1) {
|
|
right = iter.Next();
|
|
GetSamples((WaveTrack *)right, &rstart, &len);
|
|
}
|
|
|
|
if (inputs < 2 && right) {
|
|
// If the effect is mono, apply to each channel separately
|
|
|
|
bGoodResult = ProcessStereo(count, (WaveTrack *)left, NULL,
|
|
lstart, 0, len) &&
|
|
ProcessStereo(count, (WaveTrack *)right, NULL,
|
|
rstart, 0, len);
|
|
}
|
|
else bGoodResult = ProcessStereo(count,
|
|
(WaveTrack *)left, (WaveTrack *)right,
|
|
lstart, rstart, len);
|
|
if (!bGoodResult)
|
|
break;
|
|
|
|
left = iter.Next();
|
|
count++;
|
|
}
|
|
|
|
this->ReplaceProcessedTracks(bGoodResult);
|
|
return bGoodResult;
|
|
}
|
|
|
|
bool LadspaEffect::ProcessStereo(int count, WaveTrack *left, WaveTrack *right,
|
|
sampleCount lstart,
|
|
sampleCount rstart,
|
|
sampleCount len)
|
|
{
|
|
/* Allocate buffers */
|
|
if (mBlockSize == 0) {
|
|
mBlockSize = left->GetMaxBlockSize() * 2;
|
|
|
|
fInBuffer = new float *[inputs];
|
|
unsigned long i;
|
|
for (i = 0; i < inputs; i++)
|
|
fInBuffer[i] = new float[mBlockSize];
|
|
fOutBuffer = new float *[outputs];
|
|
for (i = 0; i < outputs; i++)
|
|
fOutBuffer[i] = new float[mBlockSize];
|
|
}
|
|
|
|
/* Instantiate the plugin */
|
|
|
|
unsigned long rate = (unsigned long)(left->GetRate() + 0.5);
|
|
LADSPA_Handle handle = mData->instantiate(mData, rate);
|
|
|
|
unsigned long p;
|
|
for(p=0; p<inputs; p++) {
|
|
mData->connect_port(handle, inputPorts[p], fInBuffer[p]);
|
|
}
|
|
for(p=0; p<outputs; p++) {
|
|
mData->connect_port(handle, outputPorts[p], fOutBuffer[p]);
|
|
}
|
|
|
|
for(p=0; p<mData->PortCount; p++) {
|
|
LADSPA_PortDescriptor d = mData->PortDescriptors[p];
|
|
if (LADSPA_IS_PORT_CONTROL(d)) {
|
|
if (LADSPA_IS_PORT_INPUT(d)) {
|
|
mData->connect_port(handle, p, &inputControls[p]);
|
|
}
|
|
else
|
|
mData->connect_port(handle, p, &outputControls[p]);
|
|
}
|
|
}
|
|
|
|
if (mData->activate)
|
|
mData->activate(handle);
|
|
|
|
// Actually perform the effect here
|
|
|
|
sampleCount originalLen = len;
|
|
sampleCount ls = lstart;
|
|
sampleCount rs = rstart;
|
|
while (len) {
|
|
int block = mBlockSize;
|
|
if (block > len)
|
|
block = len;
|
|
|
|
if (left && inputs > 0) {
|
|
left->Get((samplePtr)fInBuffer[0], floatSample, ls, block);
|
|
}
|
|
if (right && inputs > 1) {
|
|
right->Get((samplePtr)fInBuffer[1], floatSample, rs, block);
|
|
}
|
|
|
|
mData->run(handle, block);
|
|
|
|
if (left && outputs > 0) {
|
|
left->Set((samplePtr)fOutBuffer[0], floatSample, ls, block);
|
|
}
|
|
|
|
if (right && outputs > 1) {
|
|
right->Set((samplePtr)fOutBuffer[1], floatSample, rs, block);
|
|
}
|
|
|
|
len -= block;
|
|
ls += block;
|
|
rs += block;
|
|
|
|
if (inputs > 1) {
|
|
if (TrackGroupProgress(count, (ls-lstart)/(double)originalLen))
|
|
return false;
|
|
}
|
|
else {
|
|
if (TrackProgress(count, (ls-lstart)/(double)originalLen))
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (mData->deactivate)
|
|
mData->deactivate(handle);
|
|
|
|
if (mData->cleanup)
|
|
mData->cleanup(handle);
|
|
|
|
return true;
|
|
}
|
|
|
|
void LadspaEffect::End()
|
|
{
|
|
unsigned long i;
|
|
|
|
if (fInBuffer) {
|
|
for (i = 0; i < inputs; i++) {
|
|
if (fInBuffer[i]) {
|
|
delete [] fInBuffer[i];
|
|
}
|
|
}
|
|
delete [] fInBuffer;
|
|
fInBuffer = NULL;
|
|
}
|
|
|
|
if (fOutBuffer) {
|
|
for (i = 0; i < outputs; i++) {
|
|
if (fOutBuffer[i]) {
|
|
delete [] fOutBuffer[i];
|
|
}
|
|
}
|
|
delete [] fOutBuffer;
|
|
fOutBuffer = NULL;
|
|
}
|
|
}
|
|
|
|
class Slider:public wxSlider
|
|
{
|
|
public:
|
|
Slider(wxWindow *parent, wxWindowID id,
|
|
int value, int minValue, int maxValue,
|
|
const wxPoint& pos = wxDefaultPosition,
|
|
const wxSize& size = wxDefaultSize,
|
|
long style = wxSL_HORIZONTAL,
|
|
const wxValidator& validator = wxDefaultValidator,
|
|
const wxString& name = wxSliderNameStr)
|
|
: wxSlider(parent, id, value, minValue, maxValue,
|
|
pos, size, style, validator, name)
|
|
{
|
|
};
|
|
|
|
void OnSetFocus(wxFocusEvent &event)
|
|
{
|
|
wxScrolledWindow *p = (wxScrolledWindow *) GetParent();
|
|
wxRect r = GetRect();
|
|
wxRect rv = p->GetRect();
|
|
rv.y = 0;
|
|
|
|
event.Skip();
|
|
|
|
int y;
|
|
int yppu;
|
|
p->GetScrollPixelsPerUnit(NULL, &yppu);
|
|
|
|
if (r.y >= rv.y && r.GetBottom() <= rv.GetBottom()) {
|
|
return;
|
|
}
|
|
|
|
if (r.y < rv.y) {
|
|
p->CalcUnscrolledPosition(0, r.y, NULL, &r.y);
|
|
y = r.y / yppu;
|
|
}
|
|
else {
|
|
p->CalcUnscrolledPosition(0, r.y, NULL, &r.y);
|
|
y = (r.GetBottom() - rv.GetBottom() + yppu) / yppu;
|
|
}
|
|
|
|
p->Scroll(-1, y);
|
|
};
|
|
|
|
DECLARE_EVENT_TABLE();
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(Slider, wxSlider)
|
|
EVT_SET_FOCUS(Slider::OnSetFocus)
|
|
END_EVENT_TABLE()
|
|
|
|
class TextCtrl:public wxTextCtrl
|
|
{
|
|
public:
|
|
TextCtrl(wxWindow *parent, wxWindowID id,
|
|
const wxString& value = wxEmptyString,
|
|
const wxPoint& pos = wxDefaultPosition,
|
|
const wxSize& size = wxDefaultSize, long style = 0,
|
|
const wxValidator& validator = wxDefaultValidator,
|
|
const wxString& name = wxTextCtrlNameStr)
|
|
: wxTextCtrl(parent, id, value,
|
|
pos, size, style, validator, name)
|
|
{
|
|
};
|
|
|
|
void OnSetFocus(wxFocusEvent &event)
|
|
{
|
|
wxScrolledWindow *p = (wxScrolledWindow *) GetParent();
|
|
wxRect r = GetRect();
|
|
wxRect rv = p->GetRect();
|
|
rv.y = 0;
|
|
|
|
event.Skip();
|
|
|
|
int y;
|
|
int yppu;
|
|
p->GetScrollPixelsPerUnit(NULL, &yppu);
|
|
|
|
if (r.y >= rv.y && r.GetBottom() <= rv.GetBottom()) {
|
|
return;
|
|
}
|
|
|
|
if (r.y < rv.y) {
|
|
p->CalcUnscrolledPosition(0, r.y, NULL, &r.y);
|
|
y = r.y / yppu;
|
|
}
|
|
else {
|
|
p->CalcUnscrolledPosition(0, r.y, NULL, &r.y);
|
|
y = (r.GetBottom() - rv.GetBottom() + yppu) / yppu;
|
|
}
|
|
|
|
p->Scroll(-1, y);
|
|
};
|
|
|
|
DECLARE_EVENT_TABLE();
|
|
};
|
|
|
|
BEGIN_EVENT_TABLE(TextCtrl, wxTextCtrl)
|
|
EVT_SET_FOCUS(TextCtrl::OnSetFocus)
|
|
END_EVENT_TABLE()
|
|
|
|
const int LADSPA_SECONDS_ID = 13101;
|
|
|
|
BEGIN_EVENT_TABLE(LadspaEffectDialog, wxDialog)
|
|
EVT_BUTTON(wxID_OK, LadspaEffectDialog::OnOK)
|
|
EVT_BUTTON(wxID_CANCEL, LadspaEffectDialog::OnCancel)
|
|
EVT_BUTTON(ID_EFFECT_PREVIEW, LadspaEffectDialog::OnPreview)
|
|
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)
|
|
|
|
LadspaEffectDialog::LadspaEffectDialog(LadspaEffect *eff,
|
|
wxWindow * parent,
|
|
const LADSPA_Descriptor *data,
|
|
float *inputControls,
|
|
int sampleRate,
|
|
double length)
|
|
:wxDialog(parent, -1, LAT1CTOWX(data->Name),
|
|
wxDefaultPosition, wxDefaultSize,
|
|
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
|
|
effect(eff)
|
|
{
|
|
mLength = length;
|
|
numParams = 0;
|
|
this->mData = data;
|
|
this->inputControls = inputControls;
|
|
this->sampleRate = 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.
|
|
// This can give us a bad pointer crash, so manipulate inSlider to
|
|
// no-op HandleText during creation.
|
|
inSlider = true;
|
|
#else
|
|
inSlider = false;
|
|
#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];
|
|
|
|
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++;
|
|
}
|
|
}
|
|
|
|
wxControl *item;
|
|
|
|
wxBoxSizer *vSizer = new wxBoxSizer(wxVERTICAL);
|
|
|
|
if (mData->Maker &&
|
|
mData->Maker[0] &&
|
|
LAT1CTOWX(mData->Maker) != wxString(_("None"))) {
|
|
item = new wxStaticText(this, 0,
|
|
wxString(_("Author: "))+LAT1CTOWX(mData->Maker));
|
|
vSizer->Add(item, 0, wxALL, 5);
|
|
}
|
|
|
|
if (mData->Copyright &&
|
|
mData->Copyright[0] &&
|
|
LAT1CTOWX(mData->Copyright) != wxString(_("None"))) {
|
|
|
|
item = new wxStaticText(this, 0,
|
|
LAT1CTOWX(mData->Copyright));
|
|
vSizer->Add(item, 0, wxALL, 5);
|
|
}
|
|
|
|
wxScrolledWindow *w = new wxScrolledWindow(this,
|
|
wxID_ANY,
|
|
wxDefaultPosition,
|
|
wxDefaultSize,
|
|
wxVSCROLL | wxTAB_TRAVERSAL);
|
|
|
|
// Try to give the window a sensible default/minimum size
|
|
w->SetMinSize(wxSize(
|
|
wxMax(600, parent->GetSize().GetWidth() * 2/3),
|
|
parent->GetSize().GetHeight() / 2));
|
|
|
|
w->SetScrollRate(0, 20);
|
|
vSizer->Add(w, 1, wxEXPAND|wxALL, 5);
|
|
|
|
// Preview, OK, & Cancel buttons
|
|
vSizer->Add(CreateStdButtonSizer(this, ePreviewButton|eCancelButton|eOkButton), 0, wxEXPAND);
|
|
|
|
SetSizer(vSizer);
|
|
|
|
wxSizer *paramSizer =
|
|
new wxStaticBoxSizer(wxVERTICAL, w, _("Effect Settings"));
|
|
|
|
wxFlexGridSizer *gridSizer =
|
|
new wxFlexGridSizer(5, 0, 0);
|
|
gridSizer->AddGrowableCol(3);
|
|
|
|
for (p = 0; p < numParams; p++) {
|
|
wxString labelText = LAT1CTOWX(mData->PortNames[ports[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]];
|
|
|
|
if (LADSPA_IS_HINT_TOGGLED(hint.HintDescriptor)) {
|
|
toggles[p] = new wxCheckBox(w, p, wxT(""));
|
|
toggles[p]->SetName(labelText);
|
|
toggles[p]->SetValue(inputControls[ports[p]] > 0);
|
|
gridSizer->Add(toggles[p], 0, wxALL, 5);
|
|
ConnectFocus(toggles[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)(inputControls[ports[p]] + 0.5));
|
|
else
|
|
fieldText = Internat::ToDisplayString(inputControls[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;
|
|
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 *= sampleRate * 1000;
|
|
upper *= sampleRate;
|
|
forceint = true;
|
|
}
|
|
|
|
wxString str;
|
|
if (haslo) {
|
|
if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint)
|
|
str.Printf(wxT("%d"), (int)(lower + 0.5));
|
|
else
|
|
str = Internat::ToDisplayString(lower);
|
|
item = new wxStaticText(w, 0, str);
|
|
gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, 5);
|
|
}
|
|
else {
|
|
gridSizer->Add(1, 1, 0);
|
|
}
|
|
|
|
sliders[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]);
|
|
|
|
if (hashi) {
|
|
if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint)
|
|
str.Printf(wxT("%d"), (int)(upper + 0.5));
|
|
else
|
|
str = Internat::ToDisplayString(upper);
|
|
item = new wxStaticText(w, 0, str);
|
|
gridSizer->Add(item, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, 5);
|
|
}
|
|
else {
|
|
gridSizer->Add(1, 1, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Now add the length control
|
|
if (effect->GetEffectFlags() & INSERT_EFFECT) {
|
|
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)"));
|
|
gridSizer->Add(mSeconds, 0, wxALIGN_CENTER_VERTICAL | wxALL, 5);
|
|
gridSizer->Add(1, 1, 0);
|
|
gridSizer->Add(1, 1, 0);
|
|
gridSizer->Add(1, 1, 0);
|
|
ConnectFocus(mSeconds);
|
|
}
|
|
|
|
// Set all of the sliders based on the value in the
|
|
// text fields
|
|
inSlider = false; // Now we're ready for HandleText to actually do something.
|
|
HandleText();
|
|
|
|
paramSizer->Add(gridSizer, 1, wxEXPAND | wxALL, 5);
|
|
w->SetSizer(paramSizer);
|
|
|
|
Layout();
|
|
Fit();
|
|
SetSizeHints(GetSize());
|
|
}
|
|
|
|
LadspaEffectDialog::~LadspaEffectDialog()
|
|
{
|
|
delete[]toggles;
|
|
delete[]sliders;
|
|
delete[]fields;
|
|
delete[]labels;
|
|
delete[]ports;
|
|
}
|
|
|
|
void LadspaEffectDialog::OnCheckBox(wxCommandEvent &event)
|
|
{
|
|
int p = event.GetId();
|
|
|
|
inputControls[ports[p]] = toggles[p]->GetValue();
|
|
}
|
|
|
|
void LadspaEffectDialog::OnSlider(wxCommandEvent &event)
|
|
{
|
|
int p = event.GetId();
|
|
|
|
// if we don't add the following three lines, changing
|
|
// the value of the slider will change the text, which
|
|
// will change the slider, and so on. This gets rid of
|
|
// the implicit loop.
|
|
if (inText)
|
|
return;
|
|
inSlider = true;
|
|
|
|
float val;
|
|
float lower = float(0.0);
|
|
float upper = float(10.0);
|
|
float range;
|
|
bool forceint = false;
|
|
|
|
LADSPA_PortRangeHint hint = mData->PortRangeHints[ports[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;
|
|
forceint = true;
|
|
}
|
|
|
|
range = upper - lower;
|
|
|
|
val = (sliders[p]->GetValue() / 1000.0) * range + lower;
|
|
|
|
wxString str;
|
|
if (LADSPA_IS_HINT_INTEGER(hint.HintDescriptor) || forceint)
|
|
str.Printf(wxT("%d"), (int)(val + 0.5));
|
|
else
|
|
str = Internat::ToDisplayString(val);
|
|
|
|
fields[p]->SetValue(str);
|
|
|
|
inputControls[ports[p]] = val;
|
|
|
|
inSlider = false;
|
|
}
|
|
|
|
void LadspaEffectDialog::OnTextCtrl(wxCommandEvent & WXUNUSED(event))
|
|
{
|
|
HandleText();
|
|
}
|
|
|
|
void LadspaEffectDialog::HandleText()
|
|
{
|
|
// if we don't add the following three lines, changing
|
|
// the value of the slider will change the text, which
|
|
// will change the slider, and so on. This gets rid of
|
|
// the implicit loop.
|
|
|
|
if (inSlider)
|
|
return;
|
|
inText = true;
|
|
for (unsigned long p = 0; p < numParams; p++) {
|
|
double dval;
|
|
float val;
|
|
float lower = float(0.0);
|
|
float upper = float(10.0);
|
|
float range;
|
|
|
|
LADSPA_PortRangeHint hint = mData->PortRangeHints[ports[p]];
|
|
if (LADSPA_IS_HINT_TOGGLED(hint.HintDescriptor)) {
|
|
continue;
|
|
}
|
|
|
|
dval = Internat::CompatibleToDouble(fields[p]->GetValue());
|
|
val = dval;
|
|
|
|
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;
|
|
}
|
|
range = upper - lower;
|
|
|
|
if (val < lower)
|
|
val = lower;
|
|
if (val > upper)
|
|
val = upper;
|
|
|
|
inputControls[ports[p]] = val;
|
|
|
|
sliders[p]->SetValue((int)(((val-lower)/range) * 1000.0 + 0.5));
|
|
}
|
|
|
|
inText = false;
|
|
}
|
|
|
|
void LadspaEffectDialog::OnOK(wxCommandEvent & WXUNUSED(event))
|
|
{
|
|
EndModal(TRUE);
|
|
}
|
|
|
|
void LadspaEffectDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
|
|
{
|
|
EndModal(FALSE);
|
|
}
|
|
|
|
void LadspaEffectDialog::OnPreview(wxCommandEvent & WXUNUSED(event))
|
|
{
|
|
effect->Preview();
|
|
}
|
|
|
|
void LadspaEffectDialog::ConnectFocus(wxControl *c)
|
|
{
|
|
c->GetEventHandler()->Connect(wxEVT_SET_FOCUS,
|
|
wxFocusEventHandler(LadspaEffectDialog::ControlSetFocus));
|
|
}
|
|
|
|
void LadspaEffectDialog::DisconnectFocus(wxControl *c)
|
|
{
|
|
c->GetEventHandler()->Disconnect(wxEVT_SET_FOCUS,
|
|
wxFocusEventHandler(LadspaEffectDialog::ControlSetFocus));
|
|
}
|
|
|
|
void LadspaEffectDialog::ControlSetFocus(wxFocusEvent &event)
|
|
{
|
|
wxControl *c = (wxControl *) event.GetEventObject();
|
|
wxScrolledWindow *p = (wxScrolledWindow *) c->GetParent();
|
|
wxRect r = c->GetRect();
|
|
wxRect rv = p->GetRect();
|
|
rv.y = 0;
|
|
|
|
event.Skip();
|
|
|
|
int y;
|
|
int yppu;
|
|
p->GetScrollPixelsPerUnit(NULL, &yppu);
|
|
|
|
if (r.y >= rv.y && r.GetBottom() <= rv.GetBottom()) {
|
|
return;
|
|
}
|
|
|
|
if (r.y < rv.y) {
|
|
p->CalcUnscrolledPosition(0, r.y, NULL, &r.y);
|
|
y = r.y / yppu;
|
|
}
|
|
else {
|
|
p->CalcUnscrolledPosition(0, r.y, NULL, &r.y);
|
|
y = (r.GetBottom() - rv.GetBottom() + yppu) / yppu;
|
|
}
|
|
|
|
p->Scroll(-1, y);
|
|
};
|
|
|
|
double LadspaEffectDialog::GetLength()
|
|
{
|
|
if (effect->GetEffectFlags() & INSERT_EFFECT) {
|
|
mLength = Internat::CompatibleToDouble(mSeconds->GetValue());
|
|
}
|
|
|
|
return mLength;
|
|
}
|