mirror of
https://github.com/cookiengineer/audacity
synced 2025-06-17 00:20:06 +02:00
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.
358 lines
8.1 KiB
C++
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();
|
|
}
|