audacia/src/effects/Leveller.cpp

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