277 lines
7.4 KiB
C++
277 lines
7.4 KiB
C++
/**********************************************************************
|
|
|
|
Audacity: A Digital Audio Editor
|
|
|
|
Leveller.cpp
|
|
|
|
Lynn Allan
|
|
|
|
******************************************************************//**
|
|
|
|
\class EffectLeveller
|
|
\brief An EffectSimpleMono
|
|
|
|
*//***************************************************************//**
|
|
|
|
\class LevellerDialog
|
|
\brief Dialog for EffectLeveller
|
|
|
|
*//*******************************************************************/
|
|
|
|
|
|
|
|
#include "../Audacity.h"
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include <wx/wxprec.h>
|
|
|
|
#ifdef __BORLANDC__
|
|
#pragma hdrstop
|
|
#endif
|
|
|
|
#ifndef WX_PRECOMP
|
|
// Include your minimal set of headers here, or wx.h
|
|
#include <wx/wx.h>
|
|
#endif
|
|
|
|
#include <math.h>
|
|
#include "../Prefs.h"
|
|
#include "Leveller.h"
|
|
|
|
EffectLeveller::EffectLeveller()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
#define NUM_PASSES_CHOICES 6
|
|
|
|
bool EffectLeveller::Init()
|
|
{
|
|
mLevellerNumPasses = gPrefs->Read(wxT("/CsPresets/LevellerNumPasses"), 2L);
|
|
if ((mLevellerNumPasses < 0) || (mLevellerNumPasses >= NUM_PASSES_CHOICES)) { // corrupted Prefs?
|
|
mLevellerNumPasses = 0;
|
|
gPrefs->Write(wxT("/CsPresets/LevellerNumPasses"), 0);
|
|
}
|
|
mLevellerDbChoiceIndex = gPrefs->Read(wxT("/CsPresets/LevellerDbChoiceIndex"), 10L);
|
|
if ((mLevellerDbChoiceIndex < 0) || (mLevellerDbChoiceIndex >= Enums::NumDbChoices)) { // corrupted Prefs?
|
|
mLevellerDbChoiceIndex = (Enums::NumDbChoices - 1); //Off-skip
|
|
gPrefs->Write(wxT("/CsPresets/LevellerDbChoiceIndex"), mLevellerDbChoiceIndex);
|
|
}
|
|
mLevellerDbSilenceThreshold = Enums::Db2Signal[mLevellerDbChoiceIndex];
|
|
|
|
CalcLevellerFactors();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EffectLeveller::CheckWhetherSkipEffect()
|
|
{
|
|
return ((mLevellerDbChoiceIndex >= (Enums::NumDbChoices - 1)) || (mLevellerNumPasses == 0));
|
|
}
|
|
|
|
void EffectLeveller::End()
|
|
{
|
|
int frameSum = (int)mFrameSum;
|
|
gPrefs->Write(wxT("/Validate/LevellerFrameSum"), frameSum);
|
|
}
|
|
|
|
#define LEVELER_FACTORS 6
|
|
static double gLimit[LEVELER_FACTORS] = { 0.0001, 0.0, 0.1, 0.3, 0.5, 1.0 };
|
|
static double gAdjLimit[LEVELER_FACTORS];
|
|
static double gAddOnValue[LEVELER_FACTORS];
|
|
// static double gAdjFactor[LEVELER_FACTORS] = { 0.9, 1.0, 1.1, 1.1, 1.0, 0.9 };
|
|
static double gAdjFactor[LEVELER_FACTORS] = { 0.80, 1.00, 1.20, 1.20, 1.00, 0.80 };
|
|
|
|
void EffectLeveller::CalcLevellerFactors()
|
|
{
|
|
mFrameSum = 0.0;
|
|
gLimit[1] = mLevellerDbSilenceThreshold;
|
|
int prev = 0;
|
|
double addOnValue = 0.0;
|
|
double prevLimit = 0.0;
|
|
double limit = gLimit[0];
|
|
gAddOnValue[0] = addOnValue;
|
|
double lowerAdjLimit = 0.0;
|
|
double adjFactor = gAdjFactor[0];
|
|
double upperAdjLimit = gLimit[0] * adjFactor;
|
|
double prevAdjLimit = upperAdjLimit;
|
|
gAdjLimit[0] = upperAdjLimit;
|
|
|
|
for (int f = 1; f < LEVELER_FACTORS; ++f) {
|
|
prev = f - 1;
|
|
adjFactor = gAdjFactor[f];
|
|
lowerAdjLimit = gAdjLimit[prev];
|
|
prevLimit = gLimit[prev];
|
|
limit = gLimit[f];
|
|
prevAdjLimit = gAdjLimit[prev];
|
|
addOnValue = prevAdjLimit - (adjFactor * prevLimit);
|
|
upperAdjLimit = (adjFactor * limit) + addOnValue;
|
|
|
|
gAddOnValue[f] = addOnValue;
|
|
gAdjLimit[f] = (adjFactor * limit) + addOnValue;
|
|
}
|
|
}
|
|
|
|
bool EffectLeveller::PromptUser()
|
|
{
|
|
LevellerDialog dlog(this, mParent);
|
|
dlog.mLevellerDbChoiceIndex = mLevellerDbChoiceIndex;
|
|
dlog.mLevellerNumPasses = mLevellerNumPasses;
|
|
dlog.TransferDataToWindow();
|
|
|
|
dlog.CentreOnParent();
|
|
dlog.ShowModal();
|
|
|
|
if (dlog.GetReturnCode() == wxID_CANCEL) {
|
|
return false;
|
|
}
|
|
|
|
mLevellerNumPasses = dlog.mLevellerNumPasses;
|
|
mLevellerDbChoiceIndex = dlog.mLevellerDbChoiceIndex;
|
|
mLevellerDbSilenceThreshold = Enums::Db2Signal[mLevellerDbChoiceIndex];
|
|
gPrefs->Write(wxT("/CsPresets/LevellerDbChoiceIndex"), mLevellerDbChoiceIndex);
|
|
gPrefs->Write(wxT("/CsPresets/LevellerNumPasses"), mLevellerNumPasses);
|
|
|
|
CalcLevellerFactors();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EffectLeveller::TransferParameters( Shuttle & shuttle )
|
|
{
|
|
shuttle.TransferEnum(wxT("dB"),mLevellerDbChoiceIndex,Enums::NumDbChoices,Enums::GetDbChoices());
|
|
shuttle.TransferInt(wxT("Passes"),mLevellerNumPasses,1);
|
|
return true;
|
|
}
|
|
|
|
float EffectLeveller::LevelOneFrame(float frameInBuffer)
|
|
{
|
|
float curFrame;
|
|
float fabsCurFrame;
|
|
float curSign;
|
|
|
|
curFrame = frameInBuffer;
|
|
if (curFrame < 0.0) {
|
|
curSign = -1.0;
|
|
}
|
|
else {
|
|
curSign = 1.0;
|
|
}
|
|
fabsCurFrame = (float)fabs(curFrame);
|
|
mFrameSum += fabsCurFrame;
|
|
|
|
for (int f = 0; f < LEVELER_FACTORS; ++f) {
|
|
if (fabsCurFrame <= gLimit[f]) {
|
|
curFrame *= (float)gAdjFactor[f];
|
|
curFrame += (float)(gAddOnValue[f] * curSign);
|
|
return curFrame;
|
|
}
|
|
}
|
|
return (float)0.99;
|
|
}
|
|
|
|
bool EffectLeveller::ProcessSimpleMono(float *buffer, sampleCount len)
|
|
{
|
|
for (int pass = 0; pass < mLevellerNumPasses; ++pass) {
|
|
for (int i = 0; i < len; ++i) {
|
|
buffer[i] = LevelOneFrame(buffer[i]);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
// LevellerDialog
|
|
//----------------------------------------------------------------------------
|
|
|
|
BEGIN_EVENT_TABLE(LevellerDialog, EffectDialog)
|
|
EVT_BUTTON(ID_EFFECT_PREVIEW, LevellerDialog::OnPreview)
|
|
END_EVENT_TABLE()
|
|
|
|
LevellerDialog::LevellerDialog(EffectLeveller *effect, wxWindow *parent)
|
|
: EffectDialog(parent, _("Leveller"), PROCESS_EFFECT),
|
|
mEffect(effect)
|
|
{
|
|
mLevellerNumPasses = 0;
|
|
mLevellerDbChoiceIndex = 0;
|
|
Init();
|
|
}
|
|
|
|
void LevellerDialog::PopulateOrExchange(ShuttleGui & S)
|
|
{
|
|
wxArrayString db(Enums::NumDbChoices, Enums::GetDbChoices());
|
|
wxArrayString numPasses;
|
|
|
|
numPasses.Add(_("None-Skip"));
|
|
numPasses.Add(_("Light"));
|
|
numPasses.Add(_("Moderate"));
|
|
numPasses.Add(_("Heavy"));
|
|
numPasses.Add(_("Heavier"));
|
|
numPasses.Add(_("Heaviest"));
|
|
|
|
S.StartHorizontalLay(wxCENTER, false);
|
|
{
|
|
S.AddTitle(_("by Lynn Allan"));
|
|
}
|
|
S.EndHorizontalLay();
|
|
|
|
S.StartHorizontalLay(wxCENTER, false);
|
|
{
|
|
// Add a little space
|
|
}
|
|
S.EndHorizontalLay();
|
|
|
|
S.StartStatic(_("Degree of Leveling"));
|
|
{
|
|
S.StartHorizontalLay();
|
|
{
|
|
S.TieChoice(_("Degree of Leveling:"),
|
|
mLevellerNumPasses,
|
|
&numPasses);
|
|
}
|
|
S.EndHorizontalLay();
|
|
}
|
|
S.EndStatic();
|
|
|
|
S.StartStatic(_("Noise Threshold (Hiss/Hum/Ambient Noise)"));
|
|
{
|
|
S.StartHorizontalLay();
|
|
{
|
|
S.TieChoice(_("Threshold for Noise:"),
|
|
mLevellerDbChoiceIndex,
|
|
&db);
|
|
}
|
|
S.EndHorizontalLay();
|
|
}
|
|
S.EndStatic();
|
|
}
|
|
|
|
void LevellerDialog::OnPreview(wxCommandEvent &event)
|
|
{
|
|
TransferDataFromWindow();
|
|
|
|
// Save & restore parameters around Preview
|
|
int oldLevellerDbChoiceIndex = mEffect->mLevellerDbChoiceIndex;
|
|
int oldLevellerNumPasses = mEffect->mLevellerNumPasses;
|
|
|
|
mEffect->mLevellerDbChoiceIndex = mLevellerDbChoiceIndex;
|
|
mEffect->mLevellerNumPasses = mLevellerNumPasses;
|
|
|
|
mEffect->Preview();
|
|
|
|
mEffect->mLevellerDbChoiceIndex = oldLevellerDbChoiceIndex;
|
|
mEffect->mLevellerNumPasses = oldLevellerNumPasses;
|
|
}
|
|
|
|
// Indentation settings for Vim and Emacs and unique identifier for Arch, a
|
|
// version control system. Please do not modify past this point.
|
|
//
|
|
// Local Variables:
|
|
// c-basic-offset: 3
|
|
// indent-tabs-mode: nil
|
|
// End:
|
|
//
|
|
// vim: et sts=3 sw=3
|
|
// arch-tag: 0e9ab1c7-3cb3-4864-8f30-876218bea476
|
|
|