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:
@@ -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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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();
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user