1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-12-05 00:00:21 +01:00
Files
audacity/src/effects/Leveller.cpp
Leland Lucius 8fbfa460c4 Migrating the remaining effects
This brings the builtin, LV2, and VAMP effects inline with the
Audio Units, LADSPA, and VST effects.  All effects now share
a common UI.

This gives all effects (though not implemented for all):

User and factory preset capability
Preset import/export capability
Shared or private configuration options

Builtin effects can now be migrated to RTP, depending on algorithm.
LV2 effects now support graphical interfaces if the plugin supplies one.
Nyquist prompt enhanced to provide some features of the Nyquist Workbench.

It may not look like it, but this was a LOT of work, so trust me, there
WILL be problems and everything effect related should be suspect.  Keep
a sharp eye (or two) open.
2015-04-16 23:36:28 -05:00

318 lines
7.2 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
Leveller.cpp
Lynn Allan
******************************************************************//**
\class EffectLeveller
\brief An Effect
*//*******************************************************************/
#include "../Audacity.h"
#include <math.h>
#include <wx/choice.h>
#include <wx/intl.h>
#include <wx/valgen.h>
#include "../Prefs.h"
#include "Leveller.h"
enum kPasses
{
kLight,
kModerate,
kHeavy,
kHeavier,
kHeaviest,
kNumPasses
};
static const wxString kPassStrings[kNumPasses] =
{
/* i18n-hint: Of strength of an effect. Not strongly.*/
wxTRANSLATE("Light"),
wxTRANSLATE("Moderate"),
/* i18n-hint: Of strength of an effect. Strongly.*/
wxTRANSLATE("Heavy"),
wxTRANSLATE("Heavier"),
wxTRANSLATE("Heaviest"),
};
// Define keys, defaults, minimums, and maximums for the effect parameters
//
// Name Type Key Def Min Max Scale
Param( Level, int, wxTRANSLATE("dB"), 10, 0, Enums::NumDbChoices - 1, 1 );
Param( Passes, int, wxTRANSLATE("Passes"), kModerate, 0, kNumPasses - 1, 1 );
//
// EffectLeveller
//
EffectLeveller::EffectLeveller()
{
mPassIndex = DEF_Passes;
mDbIndex = DEF_Level;
mNumPasses = mPassIndex + 1;
mDbSilenceThreshold = Enums::Db2Signal[mDbIndex];
CalcLevellerFactors();
}
EffectLeveller::~EffectLeveller()
{
}
// IdentInterface implementation
wxString EffectLeveller::GetSymbol()
{
return LEVELLER_PLUGIN_SYMBOL;
}
wxString EffectLeveller::GetDescription()
{
return wxTRANSLATE("Leveler is a simple, combined compressor and limiter effect for reducing the dynamic range of audio");
}
// EffectIdentInterface implementation
EffectType EffectLeveller::GetType()
{
return EffectTypeProcess;
}
// EffectClientInterface implementation
int EffectLeveller::GetAudioInCount()
{
return 1;
}
int EffectLeveller::GetAudioOutCount()
{
return 1;
}
sampleCount EffectLeveller::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
{
float *ibuf = inBlock[0];
float *obuf = outBlock[0];
for (sampleCount i = 0; i < blockLen; i++)
{
float frame = ibuf[i];
for (int pass = 0; pass < mNumPasses; pass++)
{
frame = LevelOneFrame(frame);
}
obuf[i] = frame;
}
return blockLen;
}
bool EffectLeveller::GetAutomationParameters(EffectAutomationParameters & parms)
{
parms.Write(KEY_Level, Enums::DbChoices[mDbIndex]);
parms.Write(KEY_Passes, kPassStrings[mPassIndex]);
return true;
}
bool EffectLeveller::SetAutomationParameters(EffectAutomationParameters & parms)
{
// Allow for 2.1.0 and before
wxArrayString passChoices(kNumPasses, kPassStrings);
passChoices.Insert(wxT("1"), 0);
passChoices.Insert(wxT("2"), 1);
passChoices.Insert(wxT("3"), 2);
passChoices.Insert(wxT("4"), 3);
passChoices.Insert(wxT("5"), 4);
ReadAndVerifyEnum(Level, wxArrayString(Enums::NumDbChoices,Enums::GetDbChoices()));
ReadAndVerifyEnum(Passes, passChoices);
mDbIndex = Level;
mPassIndex = Passes;
// Readjust for 2.1.0 or before
if (mPassIndex >= kNumPasses)
{
mPassIndex -= kNumPasses;
}
mNumPasses = mPassIndex + 1;
mDbSilenceThreshold = Enums::Db2Signal[mDbIndex];
CalcLevellerFactors();
return true;
}
// Effect implementation
bool EffectLeveller::Startup()
{
wxString base = wxT("/Effects/Leveller/");
// 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))
{
mNumPasses = gPrefs->Read(base + wxT("LevellerNumPasses"), 2L);
if ((mNumPasses <= 0) || (mNumPasses > kNumPasses))
{ // corrupted Pr
mNumPasses = 1;
}
mDbIndex = gPrefs->Read(base + wxT("LevellerDbChoiceIndex"), 10L);
if ((mDbIndex < 0) || (mDbIndex >= Enums::NumDbChoices))
{ // cor
mDbIndex = 0; //Least dB
}
SaveUserPreset(GetCurrentSettingsGroup());
// Do not migrate again
gPrefs->Write(base + wxT("Migrated"), true);
gPrefs->Flush();
}
return true;
}
void EffectLeveller::PopulateOrExchange(ShuttleGui & S)
{
wxASSERT(kNumPasses == WXSIZEOF(kPassStrings));
wxArrayString passChoices;
for (int i = 0; i < kNumPasses; i++)
{
passChoices.Add(wxGetTranslation(kPassStrings[i]));
}
wxArrayString dBChoices(Enums::NumDbChoices,Enums::GetDbChoices());
S.SetBorder(5);
S.StartVerticalLay();
{
S.AddSpace(5);
S.StartMultiColumn(2, wxALIGN_CENTER);
{
S.AddChoice(_("Degree of Leveling:"),
wxT(""),
&passChoices)->SetValidator(wxGenericValidator(&mPassIndex));
S.AddChoice(_("Noise Threshold:"),
wxT(""),
&dBChoices)->SetValidator(wxGenericValidator(&mDbIndex));
}
S.EndMultiColumn();
}
S.EndVerticalLay();
return;
}
bool EffectLeveller::TransferDataToWindow()
{
if (!mUIParent->TransferDataToWindow())
{
return false;
}
return true;
}
bool EffectLeveller::TransferDataFromWindow()
{
if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
{
return false;
}
mNumPasses = mPassIndex + 1;
mDbSilenceThreshold = Enums::Db2Signal[mDbIndex];
CalcLevellerFactors();
return true;
}
// EffectLeveller implementation
#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()
{
gLimit[1] = mDbSilenceThreshold;
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;
}
}
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);
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;
}