1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-16 08:09:32 +02:00
audacity/src/effects/ladspa/LadspaEffect.cpp
2013-11-01 19:45:00 +00:00

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