mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-30 15:18:42 +02:00
247 lines
6.8 KiB
C++
247 lines
6.8 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 5
|
|
|
|
bool EffectLeveller::Init()
|
|
{
|
|
mLevellerNumPasses = gPrefs->Read(wxT("/Effects/Leveller/LevellerNumPasses"), 2L) ;
|
|
if ((mLevellerNumPasses <= 0) || (mLevellerNumPasses > NUM_PASSES_CHOICES)) { // corrupted Prefs?
|
|
mLevellerNumPasses = 1;
|
|
gPrefs->Write(wxT("/Effects/Leveller/LevellerNumPasses"), 1);
|
|
}
|
|
mLevellerDbChoiceIndex = gPrefs->Read(wxT("/Effects/Leveller/LevellerDbChoiceIndex"), 10L);
|
|
if ((mLevellerDbChoiceIndex < 0) || (mLevellerDbChoiceIndex >= Enums::NumDbChoices)) { // corrupted Prefs?
|
|
mLevellerDbChoiceIndex = 0; //Least dB
|
|
gPrefs->Write(wxT("/Effects/Leveller/LevellerDbChoiceIndex"), mLevellerDbChoiceIndex);
|
|
}
|
|
gPrefs->Flush();
|
|
|
|
mLevellerDbSilenceThreshold = Enums::Db2Signal[mLevellerDbChoiceIndex];
|
|
|
|
CalcLevellerFactors();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool EffectLeveller::CheckWhetherSkipEffect()
|
|
{
|
|
return mLevellerNumPasses == 0;
|
|
}
|
|
|
|
void EffectLeveller::End()
|
|
{
|
|
int frameSum = (int)mFrameSum;
|
|
gPrefs->Write(wxT("/Validate/LevellerFrameSum"), frameSum);
|
|
gPrefs->Flush();
|
|
}
|
|
|
|
#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 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];
|
|
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.mLevellerNumPassesChoiceIndex = mLevellerNumPasses-1;
|
|
dlog.TransferDataToWindow();
|
|
|
|
dlog.CentreOnParent();
|
|
dlog.ShowModal();
|
|
|
|
if (dlog.GetReturnCode() == wxID_CANCEL) {
|
|
return false;
|
|
}
|
|
|
|
mLevellerNumPasses = dlog.mLevellerNumPassesChoiceIndex+1;
|
|
mLevellerDbChoiceIndex = dlog.mLevellerDbChoiceIndex;
|
|
mLevellerDbSilenceThreshold = Enums::Db2Signal[mLevellerDbChoiceIndex];
|
|
|
|
gPrefs->Write(wxT("/Effects/Leveller/LevellerDbChoiceIndex"), mLevellerDbChoiceIndex);
|
|
gPrefs->Write(wxT("/Effects/Leveller/LevellerNumPasses"), mLevellerNumPasses);
|
|
gPrefs->Flush();
|
|
|
|
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, _("Leveler"), PROCESS_EFFECT), // Lynn called it "Leveller", but preferred spelling is "Leveler".
|
|
mEffect(effect)
|
|
{
|
|
mLevellerNumPassesChoiceIndex = 0;//
|
|
mLevellerDbChoiceIndex = 0;
|
|
Init();
|
|
}
|
|
|
|
void LevellerDialog::PopulateOrExchange(ShuttleGui & S)
|
|
{
|
|
wxArrayString db(Enums::NumDbChoices, Enums::GetDbChoices());
|
|
wxArrayString numPasses;
|
|
|
|
/* i18n-hint: Of strength of an effect. Not strongly.*/
|
|
numPasses.Add(_("Light"));
|
|
numPasses.Add(_("Moderate"));
|
|
/* i18n-hint: Of strength of an effect. Strongly.*/
|
|
numPasses.Add(_("Heavy"));
|
|
numPasses.Add(_("Heavier"));
|
|
numPasses.Add(_("Heaviest"));
|
|
|
|
S.SetBorder(5);
|
|
S.AddSpace(5);
|
|
|
|
S.StartMultiColumn(2);
|
|
{
|
|
S.TieChoice(_("Degree of Leveling:"),
|
|
mLevellerNumPassesChoiceIndex,
|
|
&numPasses);
|
|
S.TieChoice(_("Noise Threshold:"),
|
|
mLevellerDbChoiceIndex,
|
|
&db);
|
|
}
|
|
S.EndMultiColumn();
|
|
}
|
|
|
|
void LevellerDialog::OnPreview(wxCommandEvent & WXUNUSED(event))
|
|
{
|
|
TransferDataFromWindow();
|
|
|
|
// Save & restore parameters around Preview.
|
|
int oldLevellerDbChoiceIndex = mEffect->mLevellerDbChoiceIndex;
|
|
int oldLevellerNumPasses = mEffect->mLevellerNumPasses;
|
|
|
|
mEffect->mLevellerDbChoiceIndex = mLevellerDbChoiceIndex;
|
|
mEffect->mLevellerNumPasses = mLevellerNumPassesChoiceIndex+1;
|
|
|
|
mEffect->Preview();
|
|
|
|
mEffect->mLevellerDbChoiceIndex = oldLevellerDbChoiceIndex;
|
|
mEffect->mLevellerNumPasses = oldLevellerNumPasses;
|
|
}
|