1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-06-17 00:20:06 +02:00
audacity/src/effects/Amplify.cpp
Steve Daulton d9f3c432d4 Fix for bugs 943, 942, 941, 843 and 775.
Non-linear effects now process tracks before mixing.
This will be slower when multiple tracks are selected
but the preview should now match the applied effect.
SetLinearEffectFlag(true) allows linear effects to
preview more quickly when multiple tracks selected, by
pre-mixing selected tracks.
Simple generators like Tone and Noise may be marked as
'linear' so that they only preview a few seconds.
Generators that vary over time (such as Chirp) must use
the full duration that is set. As this currently
requires calculating the full duration, preview for
'non-linear' generators are not limited to the preview
length.
2015-05-15 12:51:51 +01:00

358 lines
8.1 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
Amplify.cpp
Dominic Mazzoni
Vaughan Johnson (Preview)
*******************************************************************//**
\class EffectAmplify
\brief An Effect that makes a sound louder or softer.
This rewritten class supports a smart Amplify effect - it calculates
the maximum amount of gain that can be applied to all tracks without
causing clipping and selects this as the default parameter.
*//*******************************************************************/
#include "../Audacity.h"
#include <math.h>
#include <float.h>
#include <wx/button.h>
#include <wx/checkbox.h>
#include <wx/intl.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/valtext.h>
#include "../WaveTrack.h"
#include "../widgets/valnum.h"
#include "Amplify.h"
enum
{
ID_Amp = 10000,
ID_Peak,
ID_Clip
};
// Define keys, defaults, minimums, and maximums for the effect parameters
//
// Name Type Key Def Min Max Scale
Param( Ratio, float, XO("Ratio"), 0.9f, 0.003162f, 316.227766f, 1.0f );
Param( Amp, float, wxT(""), -0.91515f, -50.0f, 50.0f, 10.0f );
//
// EffectAmplify
//
BEGIN_EVENT_TABLE(EffectAmplify, wxEvtHandler)
EVT_SLIDER(ID_Amp, EffectAmplify::OnAmpSlider)
EVT_TEXT(ID_Amp, EffectAmplify::OnAmpText)
EVT_TEXT(ID_Peak, EffectAmplify::OnPeakText)
EVT_CHECKBOX(ID_Clip, EffectAmplify::OnClipCheckBox)
END_EVENT_TABLE()
EffectAmplify::EffectAmplify()
{
mAmp = DEF_Amp;
mRatio = powf(10.0f, mAmp / 20.0f);
mCanClip = false;
mPeak = 0.0f;
SetLinearEffectFlag(true);
}
EffectAmplify::~EffectAmplify()
{
}
// IdentInterface implementation
wxString EffectAmplify::GetSymbol()
{
return AMPLIFY_PLUGIN_SYMBOL;
}
wxString EffectAmplify::GetDescription()
{
// Note: This is useful only after ratio has been set.
return XO("Increases or decreases the volume of the audio you have selected");
}
// EffectIdentInterface implementation
EffectType EffectAmplify::GetType()
{
return EffectTypeProcess;
}
// EffectClientInterface implementation
int EffectAmplify::GetAudioInCount()
{
return 1;
}
int EffectAmplify::GetAudioOutCount()
{
return 1;
}
sampleCount EffectAmplify::ProcessBlock(float **inBlock, float **outBlock, sampleCount blockLen)
{
for (sampleCount i = 0; i < blockLen; i++)
{
outBlock[0][i] = inBlock[0][i] * mRatio;
}
return blockLen;
}
bool EffectAmplify::GetAutomationParameters(EffectAutomationParameters & parms)
{
parms.WriteFloat(KEY_Ratio, mRatio);
return true;
}
bool EffectAmplify::SetAutomationParameters(EffectAutomationParameters & parms)
{
ReadAndVerifyFloat(Ratio);
mRatio = Ratio;
return true;
}
// Effect implementation
bool EffectAmplify::Init()
{
mPeak = 0.0f;
SelectedTrackListOfKindIterator iter(Track::Wave, mTracks);
for (Track *t = iter.First(); t; t = iter.Next())
{
float min, max;
((WaveTrack *)t)->GetMinMax(&min, &max, mT0, mT1);
float newpeak = (fabs(min) > fabs(max) ? fabs(min) : fabs(max));
if (newpeak > mPeak)
{
mPeak = newpeak;
}
}
return true;
}
void EffectAmplify::Preview(bool dryOnly)
{
float ratio = mRatio;
float peak = mPeak;
Effect::Preview(dryOnly);
mRatio = ratio;
mPeak = peak;
}
void EffectAmplify::PopulateOrExchange(ShuttleGui & S)
{
if (IsBatchProcessing())
{
mPeak = 1.0f;
}
else
{
if (mPeak > 0.0)
{
mRatio = 1.0f / mPeak;
}
else
{
mRatio = 1.0;
}
}
S.AddSpace(0, 5);
S.StartVerticalLay(0);
{
// Amplitude
S.StartMultiColumn(2, wxCENTER);
{
FloatingPointValidator<float> vldAmp(1, &mAmp);
vldAmp.SetRange(MIN_Amp, MAX_Amp);
mAmpT = S.Id(ID_Amp).AddTextBox(_("Amplification (dB):"), wxT(""), 12);
mAmpT->SetValidator(vldAmp);
}
S.EndMultiColumn();
// Amplitude
S.StartHorizontalLay(wxEXPAND);
{
S.SetStyle(wxSL_HORIZONTAL);
mAmpS = S.Id(ID_Amp).AddSlider(wxT(""), 0, MAX_Amp * SCL_Amp, MIN_Amp * SCL_Amp);
mAmpS->SetName(_("Amplification dB"));
}
S.EndHorizontalLay();
// Peek
S.StartMultiColumn(2, wxCENTER);
{
FloatingPointValidator<float> vldNewPeak(1, &mNewPeak);
vldNewPeak.SetRange(20.0f * log10f(powf(10.0f, MIN_Amp / 20.0f) * mPeak),
20.0f * log10f(powf(10.0f, MAX_Amp / 20.0f) * mPeak));
mNewPeakT = S.Id(ID_Peak).AddTextBox(_("New Peak Amplitude (dB):"), wxT(""), 12);
mNewPeakT->SetValidator(vldNewPeak);
}
S.EndMultiColumn();
// Clipping
S.StartHorizontalLay(wxCENTER);
{
mClip = S.Id(ID_Clip).AddCheckBox(_("Allow clipping"), wxT("false"));
if (IsBatchProcessing())
{
mClip->Enable(false);
mCanClip = true;
}
}
S.EndHorizontalLay();
}
S.EndVerticalLay();
return;
}
bool EffectAmplify::TransferDataToWindow()
{
// limit range of gain
float dBInit = 20.0f*log10f(mRatio);
float dB = TrapFloat(dBInit, MIN_Amp, MAX_Amp);
if (dB != dBInit)
mRatio = powf(10.0f, dB / 20.0f);
mAmp = 20.0f * log10f(mRatio);
mAmpT->GetValidator()->TransferToWindow();
mAmpS->SetValue((int) (mAmp * SCL_Amp + 0.5f));
mNewPeak = 20.0f * log10f(mRatio * mPeak);
mNewPeakT->GetValidator()->TransferToWindow();
mClip->SetValue(mCanClip);
CheckClip();
return true;
}
bool EffectAmplify::TransferDataFromWindow()
{
if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
{
return false;
}
mRatio = powf(10.0f, TrapFloat(mAmp * SCL_Amp, MIN_Amp * SCL_Amp, MAX_Amp * SCL_Amp) / (20.0f * SCL_Amp));
mCanClip = mClip->GetValue();
if (!mCanClip && mRatio * mPeak > 1.0f)
{
mRatio = 1.0f / mPeak;
}
return true;
}
// EffectAmplify implementation
void EffectAmplify::CheckClip()
{
// On Linux (not tested other platforms), 1.0f/mPeak is calculated at higher precision
// than the (float) value of mRatio, that is, the value is rounded in mRatio = 1/mPeak,
// so there is no guarantee that mRatio == 1/mPeak. To test for equality, assign the value
// of 1/mPeak to a float rather than directly comparing mRatio <= 1.0f/mPeak
float peakInv = 1.0f/mPeak;
EnableApply(mClip->GetValue() || (mPeak > 0.0f && mRatio <= peakInv));
}
void EffectAmplify::OnAmpText(wxCommandEvent & WXUNUSED(evt))
{
if (!mAmpT->GetValidator()->TransferFromWindow())
{
EnableApply(false);
return;
}
mRatio = powf(10.0f, TrapFloat(mAmp * SCL_Amp, MIN_Amp * SCL_Amp, MAX_Amp * SCL_Amp) / (20.0f * SCL_Amp));
mAmpS->SetValue((int) (20.0f * log10f(mRatio) * SCL_Amp + 0.5f));
mNewPeak = 20.0f * log10f(mRatio * mPeak);
mNewPeakT->GetValidator()->TransferToWindow();
CheckClip();
}
void EffectAmplify::OnPeakText(wxCommandEvent & WXUNUSED(evt))
{
if (!mNewPeakT->GetValidator()->TransferFromWindow())
{
EnableApply(false);
return;
}
mRatio = powf(10.0f, mNewPeak / 20.0f) / mPeak;
float ampInit = 20.0f * log10f(mRatio);
mAmp = TrapFloat(ampInit, MIN_Amp, MAX_Amp);
if (mAmp != ampInit)
mRatio = powf(10.0f, mAmp / 20.0f);
mAmpT->GetValidator()->TransferToWindow();
mAmpS->SetValue((int) (mAmp * SCL_Amp + 0.5f));
CheckClip();
}
void EffectAmplify::OnAmpSlider(wxCommandEvent & evt)
{
float dB = evt.GetInt() / SCL_Amp;
mRatio = powf(10.0f, TrapFloat(dB, MIN_Amp, MAX_Amp) / 20.0f);
float dB2 = (evt.GetInt() - 1) / SCL_Amp;
float ratio2 = powf(10.0f, TrapFloat(dB2, MIN_Amp, MAX_Amp) / 20.0f);
if (!mClip->GetValue() && mRatio * mPeak > 1.0f && ratio2 * mPeak < 1.0f)
{
mRatio = 1.0 / mPeak;
}
mAmp = 20.0f * log10f(mRatio);
mAmpT->GetValidator()->TransferToWindow();
mNewPeak = 20.0f * log10f(mRatio * mPeak);
mNewPeakT->GetValidator()->TransferToWindow();
CheckClip();
}
void EffectAmplify::OnClipCheckBox(wxCommandEvent & WXUNUSED(evt))
{
CheckClip();
}