diff --git a/src/effects/NoiseRemoval.cpp b/src/effects/NoiseRemoval.cpp index a8cfda09f..4afcfdf26 100644 --- a/src/effects/NoiseRemoval.cpp +++ b/src/effects/NoiseRemoval.cpp @@ -60,6 +60,8 @@ #include #include #include +#include +#include #include #include #include @@ -69,6 +71,7 @@ #include #include + #include "../AudacityApp.h" #include "../PlatformCompatibility.h" @@ -77,12 +80,18 @@ EffectNoiseRemoval::EffectNoiseRemoval() mWindowSize = 2048; mSpectrumSize = 1 + mWindowSize / 2; + gPrefs->Read(wxT("/CsPresets/NoiseSensitivity"), + &mSensitivity, 0.0); gPrefs->Read(wxT("/CsPresets/NoiseGain"), &mNoiseGain, -24.0); gPrefs->Read(wxT("/CsPresets/NoiseFreqSmoothing"), &mFreqSmoothingHz, 150.0); gPrefs->Read(wxT("/CsPresets/NoiseAttackDecayTime"), &mAttackDecayTime, 0.15); + gPrefs->Read(wxT("/CsPresets/NoiseLeaveNoise"), + &mbLeaveNoise, false); +// mbLeaveNoise = false; + mMinSignalTime = 0.05f; mHasProfile = false; @@ -213,9 +222,13 @@ bool EffectNoiseRemoval::CheckWhetherSkipEffect() bool EffectNoiseRemoval::PromptUser() { NoiseRemovalDialog dlog(this, mParent); + dlog.mSensitivity = mSensitivity; dlog.mGain = -mNoiseGain; dlog.mFreq = mFreqSmoothingHz; dlog.mTime = mAttackDecayTime; + dlog.mbLeaveNoise = mbLeaveNoise; + dlog.mKeepSignal->SetValue(!mbLeaveNoise); + dlog.mKeepNoise->SetValue(mbLeaveNoise); if( !mHasProfile ) { @@ -238,6 +251,7 @@ bool EffectNoiseRemoval::PromptUser() } dlog.TransferDataToWindow(); + dlog.mKeepNoise->SetValue(dlog.mbLeaveNoise); dlog.CentreOnParent(); dlog.ShowModal(); @@ -245,12 +259,16 @@ bool EffectNoiseRemoval::PromptUser() return false; } + mSensitivity = dlog.mSensitivity; mNoiseGain = -dlog.mGain; mFreqSmoothingHz = dlog.mFreq; mAttackDecayTime = dlog.mTime; + mbLeaveNoise = dlog.mbLeaveNoise; + gPrefs->Write(wxT("/CsPresets/NoiseSensitivity"), mSensitivity); gPrefs->Write(wxT("/CsPresets/NoiseGain"), mNoiseGain); gPrefs->Write(wxT("/CsPresets/NoiseFreqSmoothing"), mFreqSmoothingHz); gPrefs->Write(wxT("/CsPresets/NoiseAttackDecayTime"), mAttackDecayTime); + gPrefs->Write(wxT("/CsPresets/NoiseLeaveNoise"), mbLeaveNoise); mDoProfile = (dlog.GetReturnCode() == 1); return true; @@ -350,7 +368,8 @@ void EffectNoiseRemoval::Initialize() mAttackDecayBlocks = 1 + (int)(mAttackDecayTime * mSampleRate / (mWindowSize / 2)); mNoiseAttenFactor = pow(10.0, mNoiseGain/20.0); - mOneBlockAttackDecay = (int)(mNoiseGain / (mAttackDecayBlocks - 1)); + mOneBlockAttackDecay = pow(10.0, (mNoiseGain / (10.0 * mAttackDecayBlocks))); + mSensitivityFactor = pow(10.0, mSensitivity/10.0); mMinSignalBlocks = (int)(mMinSignalTime * mSampleRate / (mWindowSize / 2)); if( mMinSignalBlocks < 1 ) @@ -571,13 +590,17 @@ void EffectNoiseRemoval::RemoveNoise() if (mSpectrums[i][j] < min) min = mSpectrums[i][j]; } - if (min > mNoiseThreshold[j] && mGains[center][j] < 1.0) - mGains[center][j] = 1.0; + 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 - // dB of attenuation per block + // of linear attenuation per block for (j = 0; j < mSpectrumSize; j++) { for (i = center + 1; i < mHistoryLen; i++) { if (mGains[i][j] < mGains[i - 1][j] * mOneBlockAttackDecay) @@ -593,6 +616,7 @@ void EffectNoiseRemoval::RemoveNoise() } } + // Apply frequency smoothing to output gain int out = mHistoryLen - 1; // end of the queue @@ -704,14 +728,22 @@ bool EffectNoiseRemoval::ProcessOne(int count, WaveTrack * track, enum { ID_BUTTON_GETPROFILE = 10001, + ID_BUTTON_LEAVENOISE, + ID_RADIOBUTTON_KEEPSIGNAL, + ID_RADIOBUTTON_KEEPNOISE, + ID_SENSIVITY_SLIDER, ID_GAIN_SLIDER, ID_FREQ_SLIDER, ID_TIME_SLIDER, + ID_SENSIVITY_TEXT, ID_GAIN_TEXT, ID_FREQ_TEXT, ID_TIME_TEXT, }; +#define SENSIVITY_MIN 0 // Corresponds to -20 dB +#define SENSIVITY_MAX 4000 // Corresponds to 20 dB + #define GAIN_MIN 0 #define GAIN_MAX 48 // Corresponds to -48 dB @@ -719,7 +751,7 @@ enum { #define FREQ_MAX 100 // Corresponds to 1000 Hz #define TIME_MIN 0 -#define TIME_MAX 100 // Corresponds to 1.00 seconds +#define TIME_MAX 1000 // Corresponds to 1.000 seconds BEGIN_EVENT_TABLE(NoiseRemovalDialog,wxDialog) @@ -727,9 +759,13 @@ BEGIN_EVENT_TABLE(NoiseRemovalDialog,wxDialog) 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_SENSIVITY_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_SENSIVITY_TEXT, NoiseRemovalDialog::OnSensitivityText) EVT_TEXT(ID_GAIN_TEXT, NoiseRemovalDialog::OnGainText) EVT_TEXT(ID_FREQ_TEXT, NoiseRemovalDialog::OnFreqText) EVT_TEXT(ID_TIME_TEXT, NoiseRemovalDialog::OnTimeText) @@ -759,10 +795,17 @@ void NoiseRemovalDialog::OnGetProfile( wxCommandEvent &event ) EndModal(1); } +void NoiseRemovalDialog::OnKeepNoise( wxCommandEvent &event ) +{ + mbLeaveNoise = mKeepNoise->GetValue(); +} + void NoiseRemovalDialog::OnPreview(wxCommandEvent &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; @@ -770,20 +813,25 @@ void NoiseRemovalDialog::OnPreview(wxCommandEvent &event) 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; m_pEffect->Preview(); + m_pEffect->mSensitivity = oldSensitivity; m_pEffect->mNoiseGain = oldGain; m_pEffect->mFreqSmoothingHz = oldFreq; m_pEffect->mAttackDecayTime = oldTime; + m_pEffect->mbLeaveNoise = oldLeaveNoise; m_pEffect->mDoProfile = oldDoProfile; } void NoiseRemovalDialog::OnRemoveNoise( wxCommandEvent &event ) { + mbLeaveNoise = mKeepNoise->GetValue(); EndModal(2); } @@ -862,6 +910,15 @@ void NoiseRemovalDialog::PopulateOrExchange(ShuttleGui & S) mGainS->SetRange(GAIN_MIN, GAIN_MAX); mGainS->SetSizeHints(150, -1); + mSensitivityT = S.Id(ID_SENSIVITY_TEXT).AddTextBox(_("Sensitivity (dB):"), + wxT(""), + 0); + S.SetStyle(wxSL_HORIZONTAL); + mSensitivityS = S.Id(ID_SENSIVITY_SLIDER).AddSlider(wxT(""), 0, SENSIVITY_MAX); + mSensitivityS->SetName(_("Sensitivity")); + mSensitivityS->SetRange(SENSIVITY_MIN, SENSIVITY_MAX); + mSensitivityS->SetSizeHints(150, -1); + mFreqT = S.Id(ID_FREQ_TEXT).AddTextBox(_("Frequency smoothing (Hz):"), wxT(""), 0); @@ -876,9 +933,15 @@ void NoiseRemovalDialog::PopulateOrExchange(ShuttleGui & S) 0); S.SetStyle(wxSL_HORIZONTAL); mTimeS = S.Id(ID_TIME_SLIDER).AddSlider(wxT(""), 0, TIME_MAX); - mTimeS->SetName(_("Attach/decay time")); + mTimeS->SetName(_("Attack/decay time")); mTimeS->SetRange(TIME_MIN, TIME_MAX); mTimeS->SetSizeHints(150, -1); + + S.AddPrompt(_("Noise:")); + mKeepSignal = S.Id(ID_RADIOBUTTON_KEEPSIGNAL) + .AddRadioButtonToGroup(_("Remove")); + mKeepNoise = S.Id(ID_RADIOBUTTON_KEEPNOISE) + .AddRadioButtonToGroup(_("Isolate")); } S.EndMultiColumn(); } @@ -887,13 +950,17 @@ void NoiseRemovalDialog::PopulateOrExchange(ShuttleGui & S) 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 + 2000.0, SENSIVITY_MIN, SENSIVITY_MAX)); mGainS->SetValue(TrapLong(mGain, GAIN_MIN, GAIN_MAX)); mFreqS->SetValue(TrapLong(mFreq / 10, FREQ_MIN, FREQ_MAX)); - mTimeS->SetValue(TrapLong(mTime / 0.01, TIME_MIN, TIME_MAX)); + mTimeS->SetValue(TrapLong(mTime * 1000, TIME_MIN, TIME_MAX)); return true; } @@ -904,6 +971,12 @@ bool NoiseRemovalDialog::TransferDataFromWindow() return true; } +void NoiseRemovalDialog::OnSensitivityText(wxCommandEvent & event) +{ + mSensitivityT->GetValue().ToDouble(&mSensitivity); + mSensitivityS->SetValue(TrapLong(mSensitivity*100.0 + 2000.0, SENSIVITY_MIN, SENSIVITY_MAX)); +} + void NoiseRemovalDialog::OnGainText(wxCommandEvent & event) { mGainT->GetValue().ToDouble(&mGain); @@ -919,7 +992,13 @@ void NoiseRemovalDialog::OnFreqText(wxCommandEvent & event) void NoiseRemovalDialog::OnTimeText(wxCommandEvent & event) { mTimeT->GetValue().ToDouble(&mTime); - mTimeS->SetValue(TrapLong(mTime / 0.01, TIME_MIN, TIME_MAX)); + mTimeS->SetValue(TrapLong(mTime * 1000, TIME_MIN, TIME_MAX)); +} + +void NoiseRemovalDialog::OnSensitivitySlider(wxCommandEvent & event) +{ + mSensitivity = mSensitivityS->GetValue()/100.0 - 20.0; + mSensitivityT->SetValue(wxString::Format(wxT("%.2f"), mSensitivity)); } void NoiseRemovalDialog::OnGainSlider(wxCommandEvent & event) @@ -936,7 +1015,7 @@ void NoiseRemovalDialog::OnFreqSlider(wxCommandEvent & event) void NoiseRemovalDialog::OnTimeSlider(wxCommandEvent & event) { - mTime = mTimeS->GetValue() * 0.01; + mTime = mTimeS->GetValue() / 1000.0; mTimeT->SetValue(wxString::Format(wxT("%.2f"), mTime)); } diff --git a/src/effects/NoiseRemoval.h b/src/effects/NoiseRemoval.h index 77016edbb..5be2204ac 100644 --- a/src/effects/NoiseRemoval.h +++ b/src/effects/NoiseRemoval.h @@ -81,9 +81,11 @@ private: // Parameters that affect the noise removal, regardless of how the // noise profile was extracted + double mSensitivity; double mFreqSmoothingHz; double mNoiseGain; // in dB, should be negative double mAttackDecayTime; // in secs + bool mbLeaveNoise; bool ProcessOne(int count, WaveTrack * track, sampleCount start, sampleCount len); @@ -113,6 +115,7 @@ private: int mAttackDecayBlocks; float mOneBlockAttackDecay; float mNoiseAttenFactor; + float mSensitivityFactor; int mMinSignalBlocks; int mHistoryLen; float *mInWaveBuffer; // mWindowSize @@ -151,13 +154,16 @@ public: private: // handlers void OnGetProfile( wxCommandEvent &event ); + void OnKeepNoise( wxCommandEvent &event ); void OnPreview(wxCommandEvent &event); void OnRemoveNoise( wxCommandEvent &event ); void OnCancel( wxCommandEvent &event ); - + + void OnSensitivityText(wxCommandEvent & event); void OnGainText(wxCommandEvent & event); void OnFreqText(wxCommandEvent & event); void OnTimeText(wxCommandEvent & event); + void OnSensitivitySlider(wxCommandEvent & event); void OnGainSlider(wxCommandEvent & event); void OnFreqSlider(wxCommandEvent & event); void OnTimeSlider(wxCommandEvent & event); @@ -170,18 +176,26 @@ private: wxButton * m_pButton_Preview; wxButton * m_pButton_RemoveNoise; + wxRadioButton *mKeepSignal; + wxRadioButton *mKeepNoise; + + wxSlider *mSensitivityS; wxSlider *mGainS; wxSlider *mFreqS; wxSlider *mTimeS; + wxTextCtrl *mSensitivityT; wxTextCtrl *mGainT; wxTextCtrl *mFreqT; wxTextCtrl *mTimeT; + double mSensitivity; double mGain; double mFreq; double mTime; + bool mbLeaveNoise; + private: DECLARE_EVENT_TABLE()