1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-11-08 14:13:57 +01:00

Add real time preview to Bass and Treble

This commit is contained in:
Steve Daulton
2016-06-06 18:29:10 +01:00
parent ea8b53386c
commit e9dcf3e5e2
2 changed files with 337 additions and 289 deletions

View File

@@ -1,7 +1,7 @@
/********************************************************************** /**********************************************************************
Audacity: A Digital Audio Editor Audacity: A Digital Audio Editor
Audacity(R) is copyright (c) 1999-2013 Audacity Team. Audacity(R) is copyright (c) 1999-2016 Audacity Team.
License: GPL v2. See License.txt. License: GPL v2. See License.txt.
BassTreble.cpp BassTreble.cpp
@@ -12,10 +12,6 @@
\class EffectBassTreble \class EffectBassTreble
\brief A high shelf and low shelf filter. \brief A high shelf and low shelf filter.
The first pass applies the equalization and calculates the
peak value. The second pass, if enabled, normalizes to the
level set by the level control.
*//*******************************************************************/ *//*******************************************************************/
#include "../Audacity.h" #include "../Audacity.h"
@@ -37,21 +33,20 @@ enum
{ {
ID_Bass = 10000, ID_Bass = 10000,
ID_Treble, ID_Treble,
ID_Level, ID_Gain,
ID_Normalize, ID_Link
}; };
// Define keys, defaults, minimums, and maximums for the effect parameters // Define keys, defaults, minimums, and maximums for the effect parameters
// //
// Name Type Key Def Min Max Scale // Name Type Key Def Min Max Scale
Param( Bass, double, XO("Bass"), 0.0, -30.0, 30.0, 1 ); Param( Bass, double, XO("Bass"), 0.0, -30.0, 30.0, 1 );
Param( Treble, double, XO("Treble"), 0.0, -30.0, 30.0, 1 ); Param( Treble, double, XO("Treble"), 0.0, -30.0, 30.0, 1 );
Param( Level, double, XO("Level"), -1.0, -30.0, 0.0, 1 ); Param( Gain, double, XO("Gain"), 0.0, -30.0, 30.0, 1 );
Param( Normalize, bool, XO("Normalize"), true, false, true, 1 ); Param( Link, bool, XO("Link Sliders"), false, false, true, 1 );
// Sliders are integer, so range is x 10 #include <wx/arrimpl.cpp>
// to allow 1 decimal place resolution WX_DEFINE_OBJARRAY(EffectBassTrebleStateArray);
static const int kSliderScale = 10;
// Used to communicate the type of the filter. // Used to communicate the type of the filter.
enum kShelfType enum kShelfType
@@ -61,23 +56,23 @@ enum kShelfType
}; };
BEGIN_EVENT_TABLE(EffectBassTreble, wxEvtHandler) BEGIN_EVENT_TABLE(EffectBassTreble, wxEvtHandler)
EVT_SLIDER(ID_Bass, EffectBassTreble::OnBassSlider) EVT_SLIDER(ID_Bass, EffectBassTreble::OnBassSlider)
EVT_SLIDER(ID_Treble, EffectBassTreble::OnTrebleSlider) EVT_SLIDER(ID_Treble, EffectBassTreble::OnTrebleSlider)
EVT_SLIDER(ID_Level, EffectBassTreble::OnLevelSlider) EVT_SLIDER(ID_Gain, EffectBassTreble::OnGainSlider)
EVT_TEXT(ID_Bass, EffectBassTreble::OnBassText) EVT_TEXT(ID_Bass, EffectBassTreble::OnBassText)
EVT_TEXT(ID_Treble, EffectBassTreble::OnTrebleText) EVT_TEXT(ID_Treble, EffectBassTreble::OnTrebleText)
EVT_TEXT(ID_Level, EffectBassTreble::OnLevelText) EVT_TEXT(ID_Gain, EffectBassTreble::OnGainText)
EVT_CHECKBOX(ID_Normalize, EffectBassTreble::OnNormalize) EVT_CHECKBOX(ID_Link, EffectBassTreble::OnLinkCheckbox)
END_EVENT_TABLE() END_EVENT_TABLE()
EffectBassTreble::EffectBassTreble() EffectBassTreble::EffectBassTreble()
{ {
dB_bass = DEF_Bass; mBass = DEF_Bass;
dB_treble = DEF_Treble; mTreble = DEF_Treble;
dB_level = DEF_Level; mGain = DEF_Gain;
mbNormalize = DEF_Normalize; mLink = DEF_Link;
SetLinearEffectFlag(false); SetLinearEffectFlag(true);
} }
EffectBassTreble::~EffectBassTreble() EffectBassTreble::~EffectBassTreble()
@@ -93,7 +88,7 @@ wxString EffectBassTreble::GetSymbol()
wxString EffectBassTreble::GetDescription() wxString EffectBassTreble::GetDescription()
{ {
return XO("Increases or decreases the lower frequencies and higher frequencies of your audio independently"); return XO("Simple tone control effect");
} }
// EffectIdentInterface implementation // EffectIdentInterface implementation
@@ -103,6 +98,16 @@ EffectType EffectBassTreble::GetType()
return EffectTypeProcess; return EffectTypeProcess;
} }
bool EffectBassTreble::SupportsRealtime()
{
#if defined(EXPERIMENTAL_REALTIME_AUDACITY_EFFECTS)
return true;
#else
return false;
#endif
}
// EffectClientInterface implementation // EffectClientInterface implementation
int EffectBassTreble::GetAudioInCount() int EffectBassTreble::GetAudioInCount()
@@ -117,61 +122,57 @@ int EffectBassTreble::GetAudioOutCount()
bool EffectBassTreble::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap)) bool EffectBassTreble::ProcessInitialize(sampleCount WXUNUSED(totalLen), ChannelNames WXUNUSED(chanMap))
{ {
if (GetPass() == 1) InstanceInit(mMaster, mSampleRate);
{
const float slope = 0.4f; // same slope for both filters
const double hzBass = 250.0f;
const double hzTreble = 4000.0f;
//(re)initialise filter parameters
xn1Bass=xn2Bass=yn1Bass=yn2Bass=0;
xn1Treble=xn2Treble=yn1Treble=yn2Treble=0;
// Compute coefficents of the low shelf biquand IIR filter
Coefficents(hzBass, slope, dB_bass, kBass,
a0Bass, a1Bass, a2Bass,
b0Bass, b1Bass, b2Bass);
// Compute coefficents of the high shelf biquand IIR filter
Coefficents(hzTreble, slope, dB_treble, kTreble,
a0Treble, a1Treble, a2Treble,
b0Treble, b1Treble, b2Treble);
}
return true; return true;
} }
sampleCount EffectBassTreble::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen) sampleCount EffectBassTreble::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
{ {
float *ibuf = inBlock[0]; return InstanceProcess(mMaster, inBlock, outBlock, blockLen);
float *obuf = outBlock[0]; }
if (GetPass() == 1) bool EffectBassTreble::RealtimeInitialize()
{ {
for (sampleCount i = 0; i < blockLen; i++) SetBlockSize(512);
{
obuf[i] = DoFilter(ibuf[i]) / mPreGain;
}
}
else
{
float gain = DB_TO_LINEAR(dB_level) / mMax;
for (sampleCount i = 0; i < blockLen; i++)
{
// Normalize to specified level
obuf[i] = ibuf[i] * (mPreGain * gain);
}
}
return blockLen; mSlaves.Clear();
return true;
}
bool EffectBassTreble::RealtimeAddProcessor(int WXUNUSED(numChannels), float sampleRate)
{
EffectBassTrebleState slave;
InstanceInit(slave, sampleRate);
mSlaves.Add(slave);
return true;
}
bool EffectBassTreble::RealtimeFinalize()
{
mSlaves.Clear();
return true;
}
sampleCount EffectBassTreble::RealtimeProcess(int group,
float **inbuf,
float **outbuf,
sampleCount numSamples)
{
return InstanceProcess(mSlaves[group], inbuf, outbuf, numSamples);
} }
bool EffectBassTreble::GetAutomationParameters(EffectAutomationParameters & parms) bool EffectBassTreble::GetAutomationParameters(EffectAutomationParameters & parms)
{ {
parms.Write(KEY_Bass, dB_bass); parms.Write(KEY_Bass, mBass);
parms.Write(KEY_Treble, dB_treble); parms.Write(KEY_Treble, mTreble);
parms.Write(KEY_Level, dB_level); parms.Write(KEY_Gain, mGain);
parms.Write(KEY_Normalize, mbNormalize); parms.Write(KEY_Link, mLink);
return true; return true;
} }
@@ -180,144 +181,88 @@ bool EffectBassTreble::SetAutomationParameters(EffectAutomationParameters & parm
{ {
ReadAndVerifyDouble(Bass); ReadAndVerifyDouble(Bass);
ReadAndVerifyDouble(Treble); ReadAndVerifyDouble(Treble);
ReadAndVerifyDouble(Level); ReadAndVerifyDouble(Gain);
ReadAndVerifyBool(Normalize); ReadAndVerifyBool(Link);
dB_bass = Bass; mBass = Bass;
dB_treble = Treble; mTreble = Treble;
dB_level = Level; mGain = Gain;
mbNormalize = Normalize; mLink = Link;
return true; return true;
} }
bool EffectBassTreble::CheckWhetherSkipEffect()
{
return (mBass == 0.0 && mTreble == 0.0 && mGain == 0.0);
}
// Effect implementation // Effect implementation
bool EffectBassTreble::Startup()
{
wxString base = wxT("/Effects/BassTreble/");
// Migrate settings from 2.1.0 or before
// Already migrated, so bail
if (gPrefs->Exists(base + wxT("Migrated")))
{
return true;
}
// Load the old "current" settings
if (gPrefs->Exists(base))
{
int readBool;
gPrefs->Read(base + wxT("Bass"), &dB_bass, 0.0);
gPrefs->Read(base + wxT("Treble"), &dB_treble, 0.0);
gPrefs->Read(base + wxT("Level"), &dB_level, -1.0);
gPrefs->Read(base + wxT("Normalize"), &readBool, 1 );
// Validate data
dB_level = (dB_level > 0) ? 0 : dB_level;
mbNormalize = (readBool != 0);
SaveUserPreset(GetCurrentSettingsGroup());
// Do not migrate again
gPrefs->Write(base + wxT("Migrated"), true);
gPrefs->Flush();
}
return true;
}
bool EffectBassTreble::InitPass1()
{
mMax = 0.0;
// Integer format tracks require headroom to avoid clipping
// when saved between passes (bug 619)
if (mbNormalize) // don't need to calculate this if only doing one pass.
{
// Up to (gain + 6dB) headroom required for treble boost (experimental).
mPreGain = (dB_treble > 0) ? (dB_treble + 6.0) : 0.0;
if (dB_bass >= 0)
{
mPreGain = (mPreGain > dB_bass) ? mPreGain : dB_bass;
}
else
{
// Up to 6 dB headroom reaquired for bass cut (experimental)
mPreGain = (mPreGain > 6.0) ? mPreGain : 6.0;
}
mPreGain = (exp(log(10.0) * mPreGain / 20)); // to linear
}
else
{
mPreGain = 1.0; // Unity gain
}
return true;
}
bool EffectBassTreble::InitPass2()
{
return mbNormalize && mMax != 0;
}
void EffectBassTreble::PopulateOrExchange(ShuttleGui & S) void EffectBassTreble::PopulateOrExchange(ShuttleGui & S)
{ {
S.StartVerticalLay(0); S.SetBorder(5);
S.AddSpace(0, 5);
S.StartStatic(_("Tone controls"));
{ {
S.StartMultiColumn(3, wxEXPAND); S.StartMultiColumn(3, wxEXPAND);
S.SetStretchyCol(2);
{ {
S.SetStretchyCol(2);
// Bass control // Bass control
FloatingPointValidator<double> vldBass(1, &dB_bass); FloatingPointValidator<double> vldBass(1, &mBass);
vldBass.SetRange(MIN_Bass, MAX_Bass); vldBass.SetRange(MIN_Bass, MAX_Bass);
mBassT = S.Id(ID_Bass).AddTextBox(_("&Bass (dB):"), wxT(""), 10); mBassT = S.Id(ID_Bass).AddTextBox(_("&Bass (dB):"), wxT(""), 10);
mBassT->SetName(_("Bass (dB):")); mBassT->SetName(_("Bass (dB):"));
mBassT->SetValidator(vldBass); mBassT->SetValidator(vldBass);
S.SetStyle(wxSL_HORIZONTAL); S.SetStyle(wxSL_HORIZONTAL);
mBassS = S.Id(ID_Bass).AddSlider(wxT(""), 0, MAX_Bass * kSliderScale, MIN_Bass * kSliderScale); mBassS = S.Id(ID_Bass).AddSlider(wxT(""), 0, MAX_Bass * SCL_Bass, MIN_Bass * SCL_Bass);
mBassS->SetName(_("Bass")); mBassS->SetName(_("Bass"));
mBassS->SetPageSize(30);
// Treble control // Treble control
FloatingPointValidator<double> vldTreble(1, &dB_treble); FloatingPointValidator<double> vldTreble(1, &mTreble);
vldTreble.SetRange(MIN_Treble, MAX_Treble); vldTreble.SetRange(MIN_Treble, MAX_Treble);
mTrebleT = S.Id(ID_Treble).AddTextBox(_("&Treble (dB):"), wxT(""), 10); mTrebleT = S.Id(ID_Treble).AddTextBox(_("&Treble (dB):"), wxT(""), 10);
mTrebleT->SetValidator(vldTreble); mTrebleT->SetValidator(vldTreble);
S.SetStyle(wxSL_HORIZONTAL); S.SetStyle(wxSL_HORIZONTAL);
mTrebleS = S.Id(ID_Treble).AddSlider(wxT(""), 0, MAX_Treble * kSliderScale, MIN_Treble * kSliderScale); mTrebleS = S.Id(ID_Treble).AddSlider(wxT(""), 0, MAX_Treble * SCL_Treble, MIN_Treble * SCL_Treble);
mTrebleS->SetName(_("Treble")); mTrebleS->SetName(_("Treble"));
mTrebleS->SetPageSize(30); }
S.EndMultiColumn();
}
S.EndStatic();
// Level control S.StartStatic("Output");
FloatingPointValidator<double> vldLevel(1, &dB_level); {
vldLevel.SetRange(MIN_Level, MAX_Level); S.StartMultiColumn(3, wxEXPAND);
mLevelT = S.Id(ID_Level).AddTextBox(_("&Level (dB):"), wxT(""), 10); {
mLevelT->SetValidator(vldLevel); S.SetStretchyCol(2);
// Gain control
FloatingPointValidator<double> vldGain(1, &mGain);
vldGain.SetRange(MIN_Gain, MAX_Gain);
mGainT = S.Id(ID_Gain).AddTextBox(_("&Volume (dB):"), wxT(""), 10);
mGainT->SetValidator(vldGain);
S.SetStyle(wxSL_HORIZONTAL); S.SetStyle(wxSL_HORIZONTAL);
mLevelS = S.Id(ID_Level).AddSlider(wxT(""), 0, MAX_Level * kSliderScale, MIN_Level * kSliderScale); mGainS = S.Id(ID_Gain).AddSlider(wxT(""), 0, MAX_Gain * SCL_Gain, MIN_Gain * SCL_Gain);
mLevelS->SetName(_("Level")); mGainS->SetName(_("Level"));
mLevelS->SetPageSize(30);
} }
S.EndMultiColumn(); S.EndMultiColumn();
// Normalize checkbox S.StartMultiColumn(2, wxCENTER);
S.StartHorizontalLay(wxLEFT, true);
{ {
mNormalizeCheckBox = S.Id(ID_Normalize).AddCheckBox(_("&Enable level control"), // Link checkbox
DEF_Normalize ? wxT("true") : wxT("false")); mLinkCheckBox = S.Id(ID_Link).AddCheckBox(_("Link Volume control to Tone controls"),
mWarning = S.AddVariableText(wxT(""), false, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT); DEF_Link ? wxT("true") : wxT("false"));
} }
S.EndHorizontalLay(); S.EndMultiColumn();
} }
S.EndVerticalLay(); S.EndStatic();
return;
} }
bool EffectBassTreble::TransferDataToWindow() bool EffectBassTreble::TransferDataToWindow()
@@ -327,12 +272,10 @@ bool EffectBassTreble::TransferDataToWindow()
return false; return false;
} }
mBassS->SetValue((int) dB_bass * kSliderScale + 0.5); mBassS->SetValue((int) (mBass * SCL_Bass));
mTrebleS->SetValue((int) dB_treble * kSliderScale + 0.5); mTrebleS->SetValue((int) mTreble *SCL_Treble);
mLevelS->SetValue((int) dB_level * kSliderScale + 0.5); mGainS->SetValue((int) mGain * SCL_Gain);
mNormalizeCheckBox->SetValue(mbNormalize); mLinkCheckBox->SetValue(mLink);
UpdateUI();
return true; return true;
} }
@@ -344,18 +287,96 @@ bool EffectBassTreble::TransferDataFromWindow()
return false; return false;
} }
mbNormalize = mNormalizeCheckBox->GetValue();
return true; return true;
} }
// EffectBassTreble implementation // EffectBassTreble implementation
void EffectBassTreble::Coefficents(double hz, float slope, double gain, int type, void EffectBassTreble::InstanceInit(EffectBassTrebleState & data, float sampleRate)
float& a0, float& a1, float& a2,
float& b0, float& b1, float& b2)
{ {
double w = 2 * M_PI * hz / mSampleRate; data.samplerate = sampleRate;
data.slope = 0.4f; // same slope for both filters
data.hzBass = 250.0f; // could be tunable in a more advanced version
data.hzTreble = 4000.0f; // could be tunable in a more advanced version
data.a0Bass = 1;
data.a1Bass = 0;
data.a2Bass = 0;
data.b0Bass = 0;
data.b1Bass = 0;
data.b2Bass = 0;
data.a0Treble = 1;
data.a1Treble = 0;
data.a2Treble = 0;
data.b0Treble = 0;
data.b1Treble = 0;
data.b2Treble = 0;
data.xn1Bass = 0;
data.xn2Bass = 0;
data.yn1Bass = 0;
data.yn2Bass = 0;
data.xn1Treble = 0;
data.xn2Treble = 0;
data.yn1Treble = 0;
data.yn2Treble = 0;
data.bass = -1;
data.treble = -1;
data.gain = DB_TO_LINEAR(mGain);
}
// EffectClientInterface implementation
sampleCount EffectBassTreble::InstanceProcess(EffectBassTrebleState & data,
float **inBlock,
float **outBlock,
sampleCount blockLen)
{
float *ibuf = inBlock[0];
float *obuf = outBlock[0];
// Set value to ensure correct rounding
double oldBass = DB_TO_LINEAR(mBass);
double oldTreble = DB_TO_LINEAR(mTreble);
data.gain = DB_TO_LINEAR(mGain);
// Compute coefficents of the low shelf biquand IIR filter
if (data.bass != oldBass)
Coefficents(data.hzBass, data.slope, mBass, data.samplerate, kBass,
data.a0Bass, data.a1Bass, data.a2Bass,
data.b0Bass, data.b1Bass, data.b2Bass);
// Compute coefficents of the high shelf biquand IIR filter
if (data.treble != oldTreble)
Coefficents(data.hzTreble, data.slope, mTreble, data.samplerate, kTreble,
data.a0Treble, data.a1Treble, data.a2Treble,
data.b0Treble, data.b1Treble, data.b2Treble);
for (sampleCount i = 0; i < blockLen; i++) {
obuf[i] = DoFilter(data, ibuf[i]) * data.gain;
}
return blockLen;
}
// Effect implementation
void EffectBassTreble::Coefficents(double hz, double slope, double gain, double samplerate, int type,
double& a0, double& a1, double& a2,
double& b0, double& b1, double& b2)
{
double w = 2 * M_PI * hz / samplerate;
double a = exp(log(10.0) * gain / 40); double a = exp(log(10.0) * gain / 40);
double b = sqrt((a * a + 1) / slope - (pow((a - 1), 2))); double b = sqrt((a * a + 1) / slope - (pow((a - 1), 2)));
@@ -379,109 +400,112 @@ void EffectBassTreble::Coefficents(double hz, float slope, double gain, int type
} }
} }
float EffectBassTreble::DoFilter(float in) float EffectBassTreble::DoFilter(EffectBassTrebleState & data, float in)
{ {
// Bass filter // Bass filter
float out = (b0Bass * in + b1Bass * xn1Bass + b2Bass * xn2Bass - float out = (data.b0Bass * in + data.b1Bass * data.xn1Bass + data.b2Bass * data.xn2Bass -
a1Bass * yn1Bass - a2Bass * yn2Bass) / a0Bass; data.a1Bass * data.yn1Bass - data.a2Bass * data.yn2Bass) / data.a0Bass;
xn2Bass = xn1Bass; data.xn2Bass = data.xn1Bass;
xn1Bass = in; data.xn1Bass = in;
yn2Bass = yn1Bass; data.yn2Bass = data.yn1Bass;
yn1Bass = out; data.yn1Bass = out;
// Treble filter // Treble filter
in = out; in = out;
out = (b0Treble * in + b1Treble * xn1Treble + b2Treble * xn2Treble - out = (data.b0Treble * in + data.b1Treble * data.xn1Treble + data.b2Treble * data.xn2Treble -
a1Treble * yn1Treble - a2Treble * yn2Treble) / a0Treble; data.a1Treble * data.yn1Treble - data.a2Treble * data.yn2Treble) / data.a0Treble;
xn2Treble = xn1Treble; data.xn2Treble = data.xn1Treble;
xn1Treble = in; data.xn1Treble = in;
yn2Treble = yn1Treble; data.yn2Treble = data.yn1Treble;
yn1Treble = out; data.yn1Treble = out;
// Retain the maximum value for use in the normalization pass
if(mMax < fabs(out))
mMax = fabs(out);
return out; return out;
} }
void EffectBassTreble::UpdateUI()
{
double bass, treble, level;
mBassT->GetValue().ToDouble(&bass);
mTrebleT->GetValue().ToDouble(&treble);
mLevelT->GetValue().ToDouble(&level);
bool enable = mNormalizeCheckBox->GetValue();
// Disallow level control if disabled
mLevelT->Enable(enable);
mLevelS->Enable(enable);
if (bass == 0 && treble == 0 && !enable)
{
// Disallow Apply if nothing to do
EnableApply(false);
mWarning->SetLabel(_(" No change to apply."));
}
else
{
if (level > 0 && enable)
{
// Disallow Apply if level enabled and > 0
EnableApply(false);
mWarning->SetLabel(_(": Maximum 0 dB."));
}
else
{
// Apply enabled
EnableApply(true);
mWarning->SetLabel(wxT(""));
}
}
}
void EffectBassTreble::OnBassText(wxCommandEvent & WXUNUSED(evt)) void EffectBassTreble::OnBassText(wxCommandEvent & WXUNUSED(evt))
{ {
mBassT->GetValidator()->TransferFromWindow(); double oldBass = mBass;
mBassS->SetValue((int) floor(dB_bass * kSliderScale + 0.5));
UpdateUI(); if (!EnableApply(mUIParent->TransferDataFromWindow()))
{
return;
}
if (mLink) UpdateGain(oldBass, kBass);
mBassS->SetValue((int) (mBass * SCL_Bass));
} }
void EffectBassTreble::OnTrebleText(wxCommandEvent & WXUNUSED(evt)) void EffectBassTreble::OnTrebleText(wxCommandEvent & WXUNUSED(evt))
{ {
mTrebleT->GetValidator()->TransferFromWindow(); double oldTreble = mTreble;
mTrebleS->SetValue((int) floor(dB_treble * kSliderScale + 0.5));
UpdateUI(); if (!EnableApply(mUIParent->TransferDataFromWindow()))
{
return;
}
if (mLink) UpdateGain(oldTreble, kTreble);
mTrebleS->SetValue((int) (mTreble * SCL_Treble));
} }
void EffectBassTreble::OnLevelText(wxCommandEvent & WXUNUSED(evt)) void EffectBassTreble::OnGainText(wxCommandEvent & WXUNUSED(evt))
{ {
mLevelT->GetValidator()->TransferFromWindow(); if (!EnableApply(mUIParent->TransferDataFromWindow()))
mLevelS->SetValue((int) floor(dB_level * kSliderScale + 0.5)); {
UpdateUI(); return;
}
mGainS->SetValue((int) (mGain * SCL_Gain));
} }
void EffectBassTreble::OnBassSlider(wxCommandEvent & evt) void EffectBassTreble::OnBassSlider(wxCommandEvent & evt)
{ {
dB_bass = (double) evt.GetInt() / kSliderScale; double oldBass = mBass;
mBass = (double) evt.GetInt() / SCL_Bass;
mBassT->GetValidator()->TransferToWindow(); mBassT->GetValidator()->TransferToWindow();
UpdateUI();
if (mLink) UpdateGain(oldBass, kBass);
EnableApply(mUIParent->Validate());
} }
void EffectBassTreble::OnTrebleSlider(wxCommandEvent & evt) void EffectBassTreble::OnTrebleSlider(wxCommandEvent & evt)
{ {
dB_treble = (double) evt.GetInt() / kSliderScale; double oldTreble = mTreble;
mTreble = (double) evt.GetInt() / SCL_Treble;
mTrebleT->GetValidator()->TransferToWindow(); mTrebleT->GetValidator()->TransferToWindow();
UpdateUI();
if (mLink) UpdateGain(oldTreble, kTreble);
EnableApply(mUIParent->Validate());
} }
void EffectBassTreble::OnLevelSlider(wxCommandEvent & evt) void EffectBassTreble::OnGainSlider(wxCommandEvent & evt)
{ {
dB_level = (double) evt.GetInt() / kSliderScale; mGain = (double) evt.GetInt() / SCL_Gain;
mLevelT->GetValidator()->TransferToWindow(); mGainT->GetValidator()->TransferToWindow();
UpdateUI();
EnableApply(mUIParent->Validate());
} }
void EffectBassTreble::OnNormalize(wxCommandEvent& WXUNUSED(evt)) void EffectBassTreble::OnLinkCheckbox(wxCommandEvent& evt)
{ {
UpdateUI(); mLink = mLinkCheckBox->GetValue();
}
void EffectBassTreble::UpdateGain(double oldVal, int control)
{
double newVal;
oldVal = (oldVal > 0)? oldVal / 2.0 : oldVal / 4.0;
if (control == kBass)
newVal = (mBass > 0)? mBass / 2.0 : mBass / 4.0;
else
newVal = (mTreble > 0)? mTreble / 2.0 : mTreble / 4.0;
mGain -= newVal - oldVal;
mGain = std::min(MAX_Gain, std::max(MIN_Gain, mGain));
mGainS->SetValue(mGain);
mGainT->GetValidator()->TransferToWindow();
} }

View File

@@ -1,7 +1,7 @@
/********************************************************************** /**********************************************************************
Audacity: A Digital Audio Editor Audacity: A Digital Audio Editor
Audacity(R) is copyright (c) 1999-2013 Audacity Team. Audacity(R) is copyright (c) 1999-2016 Audacity Team.
License: GPL v2. See License.txt. License: GPL v2. See License.txt.
BassTreble.h (two shelf filters) BassTreble.h (two shelf filters)
@@ -12,12 +12,12 @@
#ifndef __AUDACITY_EFFECT_BASS_TREBLE__ #ifndef __AUDACITY_EFFECT_BASS_TREBLE__
#define __AUDACITY_EFFECT_BASS_TREBLE__ #define __AUDACITY_EFFECT_BASS_TREBLE__
#include <wx/checkbox.h>
#include <wx/event.h> #include <wx/event.h>
#include <wx/slider.h> #include <wx/slider.h>
#include <wx/stattext.h> #include <wx/stattext.h>
#include <wx/string.h> #include <wx/string.h>
#include <wx/textctrl.h> #include <wx/textctrl.h>
#include <wx/checkbox.h>
#include "Effect.h" #include "Effect.h"
@@ -25,7 +25,23 @@ class ShuttleGui;
#define BASSTREBLE_PLUGIN_SYMBOL XO("Bass and Treble") #define BASSTREBLE_PLUGIN_SYMBOL XO("Bass and Treble")
class EffectBassTreble final : public Effect class EffectBassTrebleState
{
public:
float samplerate;
double treble;
double bass;
double gain;
double slope, hzBass, hzTreble;
double a0Bass, a1Bass, a2Bass, b0Bass, b1Bass, b2Bass;
double a0Treble, a1Treble, a2Treble, b0Treble, b1Treble, b2Treble;
double xn1Bass, xn2Bass, yn1Bass, yn2Bass;
double xn1Treble, xn2Treble, yn1Treble, yn2Treble;
};
WX_DECLARE_OBJARRAY(EffectBassTrebleState, EffectBassTrebleStateArray);
class EffectBassTreble : public Effect
{ {
public: public:
EffectBassTreble(); EffectBassTreble();
@@ -33,12 +49,13 @@ public:
// IdentInterface implementation // IdentInterface implementation
wxString GetSymbol() override; virtual wxString GetSymbol();
wxString GetDescription() override; virtual wxString GetDescription();
// EffectIdentInterface implementation // EffectIdentInterface implementation
EffectType GetType() override; EffectType GetType() override;
bool SupportsRealtime() override;
// EffectClientInterface implementation // EffectClientInterface implementation
@@ -46,57 +63,64 @@ public:
int GetAudioOutCount() override; int GetAudioOutCount() override;
bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL) override; bool ProcessInitialize(sampleCount totalLen, ChannelNames chanMap = NULL) override;
sampleCount ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen) override; sampleCount ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen) override;
bool RealtimeInitialize() override;
bool RealtimeAddProcessor(int numChannels, float sampleRate) override;
bool RealtimeFinalize() override;
sampleCount RealtimeProcess(int group,
float **inbuf,
float **outbuf,
sampleCount numSamples) override;
bool GetAutomationParameters(EffectAutomationParameters & parms) override; bool GetAutomationParameters(EffectAutomationParameters & parms) override;
bool SetAutomationParameters(EffectAutomationParameters & parms) override; bool SetAutomationParameters(EffectAutomationParameters & parms) override;
// Effect Implementation
bool Startup() override; // Effect Implementation
bool InitPass1() override;
bool InitPass2() override;
void PopulateOrExchange(ShuttleGui & S) override; void PopulateOrExchange(ShuttleGui & S) override;
bool TransferDataToWindow() override; bool TransferDataToWindow() override;
bool TransferDataFromWindow() override; bool TransferDataFromWindow() override;
bool CheckWhetherSkipEffect() override;
private: private:
// EffectBassTreble implementation // EffectBassTreble implementation
void Coefficents(double hz, float slope, double gain, int type, void InstanceInit(EffectBassTrebleState & data, float sampleRate);
float& a0, float& a1, float& a2, float& b0, float& b1, float& b2); sampleCount InstanceProcess(EffectBassTrebleState & data, float **inBlock, float **outBlock, sampleCount blockLen);
float DoFilter(float in);
void UpdateUI(); void Coefficents(double hz, double slope, double gain, double samplerate, int type,
double& a0, double& a1, double& a2, double& b0, double& b1, double& b2);
float DoFilter(EffectBassTrebleState & data, float in);
void OnBassText(wxCommandEvent & evt); void OnBassText(wxCommandEvent & evt);
void OnTrebleText(wxCommandEvent & evt); void OnTrebleText(wxCommandEvent & evt);
void OnLevelText(wxCommandEvent & evt); void OnGainText(wxCommandEvent & evt);
void OnBassSlider(wxCommandEvent & evt); void OnBassSlider(wxCommandEvent & evt);
void OnTrebleSlider(wxCommandEvent & evt); void OnTrebleSlider(wxCommandEvent & evt);
void OnLevelSlider(wxCommandEvent & evt); void OnGainSlider(wxCommandEvent & evt);
void OnNormalize(wxCommandEvent & evt); void OnLinkCheckbox(wxCommandEvent & evt);
// Auto-adjust gain to reduce variation in peak level
void UpdateGain(double oldVal, int control );
private: private:
float xn1Bass, xn2Bass, yn1Bass, yn2Bass, EffectBassTrebleState mMaster;
wBass, swBass, cwBass, aBass, bBass, EffectBassTrebleStateArray mSlaves;
a0Bass, a1Bass, a2Bass, b0Bass, b1Bass, b2Bass;
// High shelf
float xn1Treble, xn2Treble, yn1Treble, yn2Treble,
wTreble, swTreble, cwTreble, aTreble, bTreble,
b0Treble, b1Treble, b2Treble, a0Treble, a1Treble, a2Treble;
double dB_bass, dB_treble, dB_level; double mBass;
double mMax; double mTreble;
bool mbNormalize; double mGain;
double mPreGain; bool mLink;
wxSlider *mBassS; wxSlider *mBassS;
wxSlider *mTrebleS; wxSlider *mTrebleS;
wxSlider *mLevelS; wxSlider *mGainS;
wxTextCtrl *mBassT;
wxTextCtrl *mTrebleT; wxTextCtrl *mBassT;
wxTextCtrl *mLevelT; wxTextCtrl *mTrebleT;
wxCheckBox *mNormalizeCheckBox; wxTextCtrl *mGainT;
wxStaticText *mWarning;
wxCheckBox *mLinkCheckBox;
DECLARE_EVENT_TABLE(); DECLARE_EVENT_TABLE();
}; };