audacia/src/effects/NoiseRemoval.cpp

846 lines
26 KiB
C++

/**********************************************************************
Audacity: A Digital Audio Editor
NoiseRemoval.cpp
Dominic Mazzoni
*******************************************************************//**
\class EffectNoiseRemoval
\brief A two-pass effect to remove background noise.
The first pass is done over just noise. For each windowed sample
of the sound, we take a FFT and then statistics are tabulated for
each frequency band - specifically the maximum level achieved by
at least (n) sampling windows in a row, for various values of (n).
During the noise removal phase, we start by setting a gain control
for each frequency band such that if the sound has exceeded the
previously-determined threshold, the gain is set to 0, otherwise
the gain is set lower (e.g. -18 dB), to suppress the noise.
Then frequency-smoothing is applied so that a single frequency is
never suppressed or boosted in isolation, and then time-smoothing
is applied so that the gain for each frequency band moves slowly.
Lookahead is employed; this effect is not designed for real-time
but if it were, there would be a significant delay.
The gain controls are applied to the complex FFT of the signal,
and then the inverse FFT is applied, followed by a Hann window;
the output signal is then pieced together using overlap/add of
half the window size.
*//****************************************************************//**
\class NoiseRemovalDialog
\brief Dialog used with EffectNoiseRemoval
*//*******************************************************************/
#include "NoiseRemoval.h"
#if !defined(EXPERIMENTAL_NOISE_REDUCTION)
#include "LoadEffects.h"
#include "../WaveTrack.h"
#include "../Prefs.h"
#include "../FileNames.h"
#include "../ShuttleGui.h"
#include <math.h>
#if defined(__WXMSW__) && !defined(__CYGWIN__)
#include <float.h>
#define finite(x) _finite(x)
#endif
#include <wx/file.h>
#include <wx/ffile.h>
#include <wx/bitmap.h>
#include <wx/brush.h>
#include <wx/button.h>
#include <wx/choice.h>
#include <wx/radiobut.h>
#include <wx/image.h>
#include <wx/intl.h>
#include <wx/sizer.h>
#include <wx/statbox.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/valtext.h>
#include "../PlatformCompatibility.h"
const ComponentInterfaceSymbol EffectNoiseRemoval::Symbol
{ XO("Noise Removal") };
namespace{ BuiltinEffectsModule::Registration< EffectNoiseRemoval > reg; }
EffectNoiseRemoval::EffectNoiseRemoval()
{
mWindowSize = 2048;
mSpectrumSize = 1 + mWindowSize / 2;
gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseSensitivity"),
&mSensitivity, 0.0);
gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseGain"),
&mNoiseGain, -24.0);
gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseFreqSmoothing"),
&mFreqSmoothingHz, 150.0);
gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseAttackDecayTime"),
&mAttackDecayTime, 0.15);
gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseLeaveNoise"),
&mbLeaveNoise, false);
// mbLeaveNoise = false;
mMinSignalTime = 0.05f;
mHasProfile = false;
mDoProfile = true;
mNoiseThreshold.reinit(mSpectrumSize);
Init();
}
EffectNoiseRemoval::~EffectNoiseRemoval()
{
}
// ComponentInterface implementation
ComponentInterfaceSymbol EffectNoiseRemoval::GetSymbol()
{
return Symbol;
}
TranslatableString EffectNoiseRemoval::GetDescription()
{
return XO("Removes constant background noise such as fans, tape noise, or hums");
}
// EffectDefinitionInterface implementation
EffectType EffectNoiseRemoval::GetType()
{
return EffectTypeProcess;
}
bool EffectNoiseRemoval::SupportsAutomation()
{
return false;
}
// Effect implementation
#define MAX_NOISE_LEVEL 30
bool EffectNoiseRemoval::Init()
{
mLevel = gPrefs->Read(wxT("/Effects/NoiseRemoval/Noise_Level"), 3L);
if ((mLevel < 0) || (mLevel > MAX_NOISE_LEVEL)) { // corrupted Prefs?
mLevel = 0; //Off-skip
gPrefs->Write(wxT("/Effects/NoiseRemoval/Noise_Level"), mLevel);
}
return gPrefs->Flush();
}
bool EffectNoiseRemoval::CheckWhetherSkipEffect()
{
return (mLevel == 0);
}
bool EffectNoiseRemoval::ShowInterface(
wxWindow &parent, const EffectDialogFactory &, bool forceModal )
{
// to do: use forceModal correctly
NoiseRemovalDialog dlog(this, &parent);
dlog.mSensitivity = mSensitivity;
dlog.mGain = -mNoiseGain;
dlog.mFreq = mFreqSmoothingHz;
dlog.mTime = mAttackDecayTime;
dlog.mbLeaveNoise = mbLeaveNoise;
dlog.mKeepSignal->SetValue(!mbLeaveNoise);
dlog.mKeepNoise->SetValue(mbLeaveNoise);
// We may want to twiddle the levels if we are setting
// from an automation dialog
bool bAllowTwiddleSettings = forceModal;
if (mHasProfile || bAllowTwiddleSettings) {
dlog.m_pButton_Preview->Enable(GetNumWaveTracks() != 0);
dlog.m_pButton_RemoveNoise->SetDefault();
} else {
dlog.m_pButton_Preview->Enable(false);
dlog.m_pButton_RemoveNoise->Enable(false);
}
dlog.TransferDataToWindow();
dlog.mKeepNoise->SetValue(dlog.mbLeaveNoise);
dlog.CentreOnParent();
dlog.ShowModal();
if (dlog.GetReturnCode() == 0) {
return false;
}
mSensitivity = dlog.mSensitivity;
mNoiseGain = -dlog.mGain;
mFreqSmoothingHz = dlog.mFreq;
mAttackDecayTime = dlog.mTime;
mbLeaveNoise = dlog.mbLeaveNoise;
gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseSensitivity"), mSensitivity);
gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseGain"), mNoiseGain);
gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseFreqSmoothing"), mFreqSmoothingHz);
gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseAttackDecayTime"), mAttackDecayTime);
gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseLeaveNoise"), mbLeaveNoise);
mDoProfile = (dlog.GetReturnCode() == 1);
return gPrefs->Flush();
}
bool EffectNoiseRemoval::Process()
{
Initialize();
// This same code will both remove noise and profile it,
// depending on 'mDoProfile'
this->CopyInputTracks(); // Set up mOutputTracks.
bool bGoodResult = true;
int count = 0;
for( auto track : mOutputTracks->Selected< WaveTrack >() ) {
double trackStart = track->GetStartTime();
double trackEnd = track->GetEndTime();
double t0 = mT0 < trackStart? trackStart: mT0;
double t1 = mT1 > trackEnd? trackEnd: mT1;
if (t1 > t0) {
auto start = track->TimeToLongSamples(t0);
auto end = track->TimeToLongSamples(t1);
auto len = end - start;
if (!ProcessOne(count, track, start, len)) {
bGoodResult = false;
break;
}
}
count++;
}
if (bGoodResult && mDoProfile) {
mHasProfile = true;
mDoProfile = false;
}
this->ReplaceProcessedTracks(bGoodResult);
return bGoodResult;
}
void EffectNoiseRemoval::ApplyFreqSmoothing(float *spec)
{
Floats tmp{ mSpectrumSize };
int j, j0, j1;
for(int i = 0; i < mSpectrumSize; i++) {
j0 = wxMax(0, i - mFreqSmoothingBins);
j1 = wxMin(mSpectrumSize-1, i + mFreqSmoothingBins);
tmp[i] = 0.0;
for(j = j0; j <= j1; j++) {
tmp[i] += spec[j];
}
tmp[i] /= (j1 - j0 + 1);
}
for(size_t i = 0; i < mSpectrumSize; i++)
spec[i] = tmp[i];
}
void EffectNoiseRemoval::Initialize()
{
mSampleRate = mProjectRate;
mFreqSmoothingBins = (int)(mFreqSmoothingHz * mWindowSize / mSampleRate);
mAttackDecayBlocks = 1 +
(int)(mAttackDecayTime * mSampleRate / (mWindowSize / 2));
mNoiseAttenFactor = DB_TO_LINEAR(mNoiseGain);
mOneBlockAttackDecay = DB_TO_LINEAR(mNoiseGain / mAttackDecayBlocks);
// Applies to power, divide by 10:
mSensitivityFactor = pow(10.0, mSensitivity/10.0);
mMinSignalBlocks =
(int)(mMinSignalTime * mSampleRate / (mWindowSize / 2));
if( mMinSignalBlocks < 1 )
mMinSignalBlocks = 1;
mHistoryLen = (2 * mAttackDecayBlocks) - 1;
if (mHistoryLen < mMinSignalBlocks)
mHistoryLen = mMinSignalBlocks;
mSpectrums.reinit(mHistoryLen, mSpectrumSize);
mGains.reinit(mHistoryLen, mSpectrumSize);
mRealFFTs.reinit(mHistoryLen, mSpectrumSize);
mImagFFTs.reinit(mHistoryLen, mSpectrumSize);
// Initialize the FFT
hFFT = GetFFT(mWindowSize);
mFFTBuffer.reinit(mWindowSize);
mInWaveBuffer.reinit(mWindowSize);
mWindow.reinit(mWindowSize);
mOutOverlapBuffer.reinit(mWindowSize);
// Create a Hann window function
for(size_t i=0; i<mWindowSize; i++)
mWindow[i] = 0.5 - 0.5 * cos((2.0*M_PI*i) / mWindowSize);
if (mDoProfile) {
for (size_t i = 0; i < mSpectrumSize; i++)
mNoiseThreshold[i] = float(0);
}
}
void EffectNoiseRemoval::End()
{
hFFT.reset();
if (mDoProfile) {
ApplyFreqSmoothing(mNoiseThreshold.get());
}
mSpectrums.reset();
mGains.reset();
mRealFFTs.reset();
mImagFFTs.reset();
mFFTBuffer.reset();
mInWaveBuffer.reset();
mWindow.reset();
mOutOverlapBuffer.reset();
mOutputTrack.reset();
}
void EffectNoiseRemoval::StartNewTrack()
{
for(size_t i = 0; i < mHistoryLen; i++) {
for(size_t j = 0; j < mSpectrumSize; j++) {
mSpectrums[i][j] = 0;
mGains[i][j] = mNoiseAttenFactor;
mRealFFTs[i][j] = 0.0;
mImagFFTs[i][j] = 0.0;
}
}
for(size_t j = 0; j < mWindowSize; j++)
mOutOverlapBuffer[j] = 0.0;
mInputPos = 0;
mInSampleCount = 0;
mOutSampleCount = -(int)((mWindowSize / 2) * (mHistoryLen - 1));
}
void EffectNoiseRemoval::ProcessSamples(size_t len, float *buffer)
{
while(len && mOutSampleCount < mInSampleCount) {
size_t avail = wxMin(len, mWindowSize - mInputPos);
for(size_t i = 0; i < avail; i++)
mInWaveBuffer[mInputPos + i] = buffer[i];
buffer += avail;
len -= avail;
mInputPos += avail;
if (mInputPos == int(mWindowSize)) {
FillFirstHistoryWindow();
if (mDoProfile)
GetProfile();
else
RemoveNoise();
RotateHistoryWindows();
// Rotate halfway for overlap-add
for(size_t i = 0; i < mWindowSize / 2; i++) {
mInWaveBuffer[i] = mInWaveBuffer[i + mWindowSize / 2];
}
mInputPos = mWindowSize / 2;
}
}
}
void EffectNoiseRemoval::FillFirstHistoryWindow()
{
for(size_t i = 0; i < mWindowSize; i++)
mFFTBuffer[i] = mInWaveBuffer[i];
RealFFTf(mFFTBuffer.get(), hFFT.get());
for(size_t i = 1; i + 1 < mSpectrumSize; i++) {
mRealFFTs[0][i] = mFFTBuffer[hFFT->BitReversed[i] ];
mImagFFTs[0][i] = mFFTBuffer[hFFT->BitReversed[i]+1];
mSpectrums[0][i] = mRealFFTs[0][i]*mRealFFTs[0][i] + mImagFFTs[0][i]*mImagFFTs[0][i];
mGains[0][i] = mNoiseAttenFactor;
}
// DC and Fs/2 bins need to be handled specially
mSpectrums[0][0] = mFFTBuffer[0]*mFFTBuffer[0];
mSpectrums[0][mSpectrumSize-1] = mFFTBuffer[1]*mFFTBuffer[1];
mGains[0][0] = mNoiseAttenFactor;
mGains[0][mSpectrumSize-1] = mNoiseAttenFactor;
}
namespace {
inline void Rotate(ArraysOf<float> &arrays, size_t historyLen)
{
Floats temp = std::move( arrays[ historyLen - 1 ] );
for ( size_t nn = historyLen - 1; nn--; )
arrays[ nn + 1 ] = std::move( arrays[ nn ] );
arrays[0] = std::move( temp );
}
}
void EffectNoiseRemoval::RotateHistoryWindows()
{
// Remember the last window so we can reuse it
Rotate(mSpectrums, mHistoryLen);
Rotate(mGains, mHistoryLen);
Rotate(mRealFFTs, mHistoryLen);
Rotate(mImagFFTs, mHistoryLen);
}
void EffectNoiseRemoval::FinishTrack()
{
// Keep flushing empty input buffers through the history
// windows until we've output exactly as many samples as
// were input.
// Well, not exactly, but not more than mWindowSize/2 extra samples at the end.
// We'll DELETE them later in ProcessOne.
Floats empty{ mWindowSize / 2 };
for(size_t i = 0; i < mWindowSize / 2; i++)
empty[i] = 0.0;
while (mOutSampleCount < mInSampleCount) {
ProcessSamples(mWindowSize / 2, empty.get());
}
}
void EffectNoiseRemoval::GetProfile()
{
// The noise threshold for each frequency is the maximum
// level achieved at that frequency for a minimum of
// mMinSignalBlocks blocks in a row - the max of a min.
int start = mHistoryLen - mMinSignalBlocks;
int finish = mHistoryLen;
int i;
for (size_t j = 0; j < mSpectrumSize; j++) {
float min = mSpectrums[start][j];
for (i = start+1; i < finish; i++) {
if (mSpectrums[i][j] < min)
min = mSpectrums[i][j];
}
if (min > mNoiseThreshold[j])
mNoiseThreshold[j] = min;
}
mOutSampleCount += mWindowSize / 2; // what is this for? Not used when we are getting the profile?
}
void EffectNoiseRemoval::RemoveNoise()
{
size_t center = mHistoryLen / 2;
size_t start = center - mMinSignalBlocks/2;
size_t finish = start + mMinSignalBlocks;
// Raise the gain for elements in the center of the sliding history
for (size_t j = 0; j < mSpectrumSize; j++) {
float min = mSpectrums[start][j];
for (size_t i = start+1; i < finish; i++) {
if (mSpectrums[i][j] < min)
min = mSpectrums[i][j];
}
if (min > mSensitivityFactor * mNoiseThreshold[j] && mGains[center][j] < 1.0) {
if (mbLeaveNoise) mGains[center][j] = 0.0;
else mGains[center][j] = 1.0;
} else {
if (mbLeaveNoise) mGains[center][j] = 1.0;
}
}
// Decay the gain in both directions;
// note that mOneBlockAttackDecay is less than 1.0
// of linear attenuation per block
for (size_t j = 0; j < mSpectrumSize; j++) {
for (size_t i = center + 1; i < mHistoryLen; i++) {
if (mGains[i][j] < mGains[i - 1][j] * mOneBlockAttackDecay)
mGains[i][j] = mGains[i - 1][j] * mOneBlockAttackDecay;
if (mGains[i][j] < mNoiseAttenFactor)
mGains[i][j] = mNoiseAttenFactor;
}
for (size_t i = center; i--;) {
if (mGains[i][j] < mGains[i + 1][j] * mOneBlockAttackDecay)
mGains[i][j] = mGains[i + 1][j] * mOneBlockAttackDecay;
if (mGains[i][j] < mNoiseAttenFactor)
mGains[i][j] = mNoiseAttenFactor;
}
}
// Apply frequency smoothing to output gain
int out = mHistoryLen - 1; // end of the queue
ApplyFreqSmoothing(mGains[out].get());
// Apply gain to FFT
for (size_t j = 0; j < (mSpectrumSize-1); j++) {
mFFTBuffer[j*2 ] = mRealFFTs[out][j] * mGains[out][j];
mFFTBuffer[j*2+1] = mImagFFTs[out][j] * mGains[out][j];
}
// The Fs/2 component is stored as the imaginary part of the DC component
mFFTBuffer[1] = mRealFFTs[out][mSpectrumSize-1] * mGains[out][mSpectrumSize-1];
// Invert the FFT into the output buffer
InverseRealFFTf(mFFTBuffer.get(), hFFT.get());
// Overlap-add
for(size_t j = 0; j < (mSpectrumSize-1); j++) {
mOutOverlapBuffer[j*2 ] += mFFTBuffer[hFFT->BitReversed[j] ] * mWindow[j*2 ];
mOutOverlapBuffer[j*2+1] += mFFTBuffer[hFFT->BitReversed[j]+1] * mWindow[j*2+1];
}
// Output the first half of the overlap buffer, they're done -
// and then shift the next half over.
if (mOutSampleCount >= 0) { // ...but not if it's the first half-window
mOutputTrack->Append((samplePtr)mOutOverlapBuffer.get(), floatSample,
mWindowSize / 2);
}
mOutSampleCount += mWindowSize / 2;
for(size_t j = 0; j < mWindowSize / 2; j++) {
mOutOverlapBuffer[j] = mOutOverlapBuffer[j + (mWindowSize / 2)];
mOutOverlapBuffer[j + (mWindowSize / 2)] = 0.0;
}
}
bool EffectNoiseRemoval::ProcessOne(int count, WaveTrack * track,
sampleCount start, sampleCount len)
{
if (track == NULL)
return false;
StartNewTrack();
if (!mDoProfile)
mOutputTrack = track->EmptyCopy();
auto bufferSize = track->GetMaxBlockSize();
Floats buffer{ bufferSize };
bool bLoopSuccess = true;
auto samplePos = start;
while (samplePos < start + len) {
//Get a blockSize of samples (smaller than the size of the buffer)
//Adjust the block size if it is the final block in the track
const auto blockSize = limitSampleBufferSize(
track->GetBestBlockSize(samplePos),
start + len - samplePos
);
//Get the samples from the track and put them in the buffer
track->Get((samplePtr)buffer.get(), floatSample, samplePos, blockSize);
mInSampleCount += blockSize;
ProcessSamples(blockSize, buffer.get());
samplePos += blockSize;
// Update the Progress meter
if (TrackProgress(count, (samplePos - start).as_double() / len.as_double())) {
bLoopSuccess = false;
break;
}
}
FinishTrack();
if (!mDoProfile) {
// Flush the output WaveTrack (since it's buffered)
mOutputTrack->Flush();
// Take the output track and insert it in place of the original
// sample data (as operated on -- this may not match mT0/mT1)
if (bLoopSuccess) {
double t0 = mOutputTrack->LongSamplesToTime(start);
double tLen = mOutputTrack->LongSamplesToTime(len);
// Filtering effects always end up with more data than they started with. Delete this 'tail'.
mOutputTrack->HandleClear(tLen, mOutputTrack->GetEndTime(), false, false);
track->ClearAndPaste(t0, t0 + tLen, mOutputTrack.get(), true, false);
}
}
return bLoopSuccess;
}
// WDR: class implementations
//----------------------------------------------------------------------------
// NoiseRemovalDialog
//----------------------------------------------------------------------------
// WDR: event table for NoiseRemovalDialog
enum {
ID_BUTTON_GETPROFILE = 10001,
ID_BUTTON_LEAVENOISE,
ID_RADIOBUTTON_KEEPSIGNAL,
ID_RADIOBUTTON_KEEPNOISE,
ID_SENSITIVITY_SLIDER,
ID_GAIN_SLIDER,
ID_FREQ_SLIDER,
ID_TIME_SLIDER,
ID_SENSITIVITY_TEXT,
ID_GAIN_TEXT,
ID_FREQ_TEXT,
ID_TIME_TEXT,
};
#define SENSITIVITY_MIN 0 // Corresponds to -20 dB
#define SENSITIVITY_MAX 4000 // Corresponds to 20 dB
#define GAIN_MIN 0
#define GAIN_MAX 48 // Corresponds to -48 dB
#define FREQ_MIN 0
#define FREQ_MAX 100 // Corresponds to 1000 Hz
#define TIME_MIN 0
#define TIME_MAX 100 // Corresponds to 1.000 seconds
BEGIN_EVENT_TABLE(NoiseRemovalDialog,wxDialogWrapper)
EVT_BUTTON(wxID_OK, NoiseRemovalDialog::OnRemoveNoise)
EVT_BUTTON(wxID_CANCEL, NoiseRemovalDialog::OnCancel)
EVT_BUTTON(ID_EFFECT_PREVIEW, NoiseRemovalDialog::OnPreview)
EVT_BUTTON(ID_BUTTON_GETPROFILE, NoiseRemovalDialog::OnGetProfile)
EVT_RADIOBUTTON(ID_RADIOBUTTON_KEEPNOISE, NoiseRemovalDialog::OnKeepNoise)
EVT_RADIOBUTTON(ID_RADIOBUTTON_KEEPSIGNAL, NoiseRemovalDialog::OnKeepNoise)
EVT_SLIDER(ID_SENSITIVITY_SLIDER, NoiseRemovalDialog::OnSensitivitySlider)
EVT_SLIDER(ID_GAIN_SLIDER, NoiseRemovalDialog::OnGainSlider)
EVT_SLIDER(ID_FREQ_SLIDER, NoiseRemovalDialog::OnFreqSlider)
EVT_SLIDER(ID_TIME_SLIDER, NoiseRemovalDialog::OnTimeSlider)
EVT_TEXT(ID_SENSITIVITY_TEXT, NoiseRemovalDialog::OnSensitivityText)
EVT_TEXT(ID_GAIN_TEXT, NoiseRemovalDialog::OnGainText)
EVT_TEXT(ID_FREQ_TEXT, NoiseRemovalDialog::OnFreqText)
EVT_TEXT(ID_TIME_TEXT, NoiseRemovalDialog::OnTimeText)
END_EVENT_TABLE()
NoiseRemovalDialog::NoiseRemovalDialog(EffectNoiseRemoval * effect,
wxWindow *parent)
: EffectDialog( parent, XO("Noise Removal"), EffectTypeProcess)
{
m_pEffect = effect;
// NULL out the control members until the controls are created.
m_pButton_GetProfile = NULL;
m_pButton_Preview = NULL;
m_pButton_RemoveNoise = NULL;
Init();
m_pButton_Preview =
(wxButton *)wxWindow::FindWindowById(ID_EFFECT_PREVIEW, this);
m_pButton_RemoveNoise =
(wxButton *)wxWindow::FindWindowById(wxID_OK, this);
}
void NoiseRemovalDialog::OnGetProfile( wxCommandEvent & WXUNUSED(event))
{
EndModal(1);
}
void NoiseRemovalDialog::OnKeepNoise( wxCommandEvent & WXUNUSED(event))
{
mbLeaveNoise = mKeepNoise->GetValue();
}
void NoiseRemovalDialog::OnPreview(wxCommandEvent & WXUNUSED(event))
{
// Save & restore parameters around Preview, because we didn't do OK.
bool oldDoProfile = m_pEffect->mDoProfile;
bool oldLeaveNoise = m_pEffect->mbLeaveNoise;
double oldSensitivity = m_pEffect->mSensitivity;
double oldGain = m_pEffect->mNoiseGain;
double oldFreq = m_pEffect->mFreqSmoothingHz;
double oldTime = m_pEffect->mAttackDecayTime;
TransferDataFromWindow();
m_pEffect->mDoProfile = false;
m_pEffect->mbLeaveNoise = mbLeaveNoise;
m_pEffect->mSensitivity = mSensitivity;
m_pEffect->mNoiseGain = -mGain;
m_pEffect->mFreqSmoothingHz = mFreq;
m_pEffect->mAttackDecayTime = mTime;
auto cleanup = finally( [&] {
m_pEffect->mSensitivity = oldSensitivity;
m_pEffect->mNoiseGain = oldGain;
m_pEffect->mFreqSmoothingHz = oldFreq;
m_pEffect->mAttackDecayTime = oldTime;
m_pEffect->mbLeaveNoise = oldLeaveNoise;
m_pEffect->mDoProfile = oldDoProfile;
} );
m_pEffect->Preview( false );
}
void NoiseRemovalDialog::OnRemoveNoise( wxCommandEvent & WXUNUSED(event))
{
mbLeaveNoise = mKeepNoise->GetValue();
EndModal(2);
}
void NoiseRemovalDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
{
EndModal(0);
}
void NoiseRemovalDialog::PopulateOrExchange(ShuttleGui & S)
{
S.StartStatic(XO("Step 1"));
{
S.AddVariableText(XO(
"Select a few seconds of just noise so Audacity knows what to filter out,\nthen click Get Noise Profile:"));
m_pButton_GetProfile = S.Id(ID_BUTTON_GETPROFILE).AddButton(XXO("&Get Noise Profile"));
}
S.EndStatic();
S.StartStatic(XO("Step 2"));
{
S.AddVariableText(XO(
"Select all of the audio you want filtered, choose how much noise you want\nfiltered out, and then click 'OK' to remove noise.\n"));
S.StartMultiColumn(3, wxEXPAND);
S.SetStretchyCol(2);
{
mGainT = S.Id(ID_GAIN_TEXT)
.Validator<wxTextValidator>(wxFILTER_NUMERIC)
.AddTextBox(XXO("Noise re&duction (dB):"), wxT(""), 0);
mGainS = S.Id(ID_GAIN_SLIDER)
.Name(XO("Noise reduction"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 150, -1 } )
.AddSlider( {}, 0, GAIN_MAX, GAIN_MIN);
mSensitivityT = S.Id(ID_SENSITIVITY_TEXT)
.Validator<wxTextValidator>(wxFILTER_NUMERIC)
.AddTextBox(XXO("&Sensitivity (dB):"), wxT(""), 0);
mSensitivityS = S.Id(ID_SENSITIVITY_SLIDER)
.Name(XO("Sensitivity"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 150, -1 } )
.AddSlider( {}, 0, SENSITIVITY_MAX, SENSITIVITY_MIN);
mFreqT = S.Id(ID_FREQ_TEXT)
.Validator<wxTextValidator>(wxFILTER_NUMERIC)
.AddTextBox(XXO("Fr&equency smoothing (Hz):"), wxT(""), 0);
mFreqS = S.Id(ID_FREQ_SLIDER)
.Name(XO("Frequency smoothing"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 150, -1 } )
.AddSlider( {}, 0, FREQ_MAX, FREQ_MIN);
mTimeT = S.Id(ID_TIME_TEXT)
.Validator<wxTextValidator>(wxFILTER_NUMERIC)
.AddTextBox(XXO("Attac&k/decay time (secs):"), wxT(""), 0);
mTimeS = S.Id(ID_TIME_SLIDER)
.Name(XO("Attack/decay time"))
.Style(wxSL_HORIZONTAL)
.MinSize( { 150, -1 } )
.AddSlider( {}, 0, TIME_MAX, TIME_MIN);
S.AddPrompt(XXO("Noise:"));
mKeepSignal = S.Id(ID_RADIOBUTTON_KEEPSIGNAL)
.AddRadioButton(XXO("Re&move"));
mKeepNoise = S.Id(ID_RADIOBUTTON_KEEPNOISE)
.AddRadioButtonToGroup(XXO("&Isolate"));
}
S.EndMultiColumn();
}
S.EndStatic();
}
bool NoiseRemovalDialog::TransferDataToWindow()
{
mSensitivityT->SetValue(wxString::Format(wxT("%.2f"), mSensitivity));
mGainT->SetValue(wxString::Format(wxT("%d"), (int)mGain));
mFreqT->SetValue(wxString::Format(wxT("%d"), (int)mFreq));
mTimeT->SetValue(wxString::Format(wxT("%.2f"), mTime));
mKeepNoise->SetValue(mbLeaveNoise);
mKeepSignal->SetValue(!mbLeaveNoise);
mSensitivityS->SetValue(TrapLong(mSensitivity*100.0 + (SENSITIVITY_MAX-SENSITIVITY_MIN+1)/2.0, SENSITIVITY_MIN, SENSITIVITY_MAX));
mGainS->SetValue(TrapLong(mGain, GAIN_MIN, GAIN_MAX));
mFreqS->SetValue(TrapLong(mFreq / 10, FREQ_MIN, FREQ_MAX));
mTimeS->SetValue(TrapLong(mTime * TIME_MAX + 0.5, TIME_MIN, TIME_MAX));
return true;
}
bool NoiseRemovalDialog::TransferDataFromWindow()
{
// Nothing to do here
return true;
}
void NoiseRemovalDialog::OnSensitivityText(wxCommandEvent & WXUNUSED(event))
{
mSensitivityT->GetValue().ToDouble(&mSensitivity);
mSensitivityS->SetValue(TrapLong(mSensitivity*100.0 + (SENSITIVITY_MAX-SENSITIVITY_MIN+1)/2.0, SENSITIVITY_MIN, SENSITIVITY_MAX));
}
void NoiseRemovalDialog::OnGainText(wxCommandEvent & WXUNUSED(event))
{
mGainT->GetValue().ToDouble(&mGain);
mGainS->SetValue(TrapLong(mGain, GAIN_MIN, GAIN_MAX));
}
void NoiseRemovalDialog::OnFreqText(wxCommandEvent & WXUNUSED(event))
{
mFreqT->GetValue().ToDouble(&mFreq);
mFreqS->SetValue(TrapLong(mFreq / 10, FREQ_MIN, FREQ_MAX));
}
void NoiseRemovalDialog::OnTimeText(wxCommandEvent & WXUNUSED(event))
{
mTimeT->GetValue().ToDouble(&mTime);
mTimeS->SetValue(TrapLong(mTime * TIME_MAX + 0.5, TIME_MIN, TIME_MAX));
}
void NoiseRemovalDialog::OnSensitivitySlider(wxCommandEvent & WXUNUSED(event))
{
mSensitivity = mSensitivityS->GetValue()/100.0 - 20.0;
mSensitivityT->SetValue(wxString::Format(wxT("%.2f"), mSensitivity));
}
void NoiseRemovalDialog::OnGainSlider(wxCommandEvent & WXUNUSED(event))
{
mGain = mGainS->GetValue();
mGainT->SetValue(wxString::Format(wxT("%d"), (int)mGain));
}
void NoiseRemovalDialog::OnFreqSlider(wxCommandEvent & WXUNUSED(event))
{
mFreq = mFreqS->GetValue() * 10;
mFreqT->SetValue(wxString::Format(wxT("%d"), (int)mFreq));
}
void NoiseRemovalDialog::OnTimeSlider(wxCommandEvent & WXUNUSED(event))
{
mTime = mTimeS->GetValue() / (TIME_MAX*1.0);
mTimeT->SetValue(wxString::Format(wxT("%.2f"), mTime));
}
#endif