/********************************************************************** 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 #ifdef __BORLANDC__ #pragma hdrstop #endif #ifndef WX_PRECOMP // Include your minimal set of headers here, or wx.h #include #endif #include #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