Audacity 3.2.0
NoiseReduction.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 NoiseReduction.cpp
6
7 Dominic Mazzoni
8
9 detailed rewriting by
10 Paul Licameli
11
12*******************************************************************//****************************************************************/
40#include "NoiseReduction.h"
41
42#include "LoadEffects.h"
43#include "EffectManager.h"
44#include "EffectUI.h"
45
46#include "ShuttleGui.h"
47#include "HelpSystem.h"
48#include "FFT.h"
49#include "Prefs.h"
50#include "RealFFTf.h"
51#include "../SpectrumTransformer.h"
52
53#include "WaveTrack.h"
54#include "AudacityMessageBox.h"
55#include "../widgets/valnum.h"
56
57#include <algorithm>
58#include <vector>
59#include <math.h>
60
61#if defined(__WXMSW__) && !defined(__CYGWIN__)
62#include <float.h>
63#define finite(x) _finite(x)
64#endif
65
66#include <wx/button.h>
67#include <wx/choice.h>
68#include <wx/radiobut.h>
69#include <wx/slider.h>
70#include <wx/valtext.h>
71#include <wx/textctrl.h>
72
73// SPECTRAL_SELECTION not to affect this effect for now, as there might be no indication that it does.
74// [Discussed and agreed for v2.1 by Steve, Paul, Bill].
75#undef EXPERIMENTAL_SPECTRAL_EDITING
76
77typedef std::vector<float> FloatVector;
78
79// Define both of these to make the radio button three-way
80#define RESIDUE_CHOICE
81//#define ISOLATE_CHOICE
82
83// Define for Attack and release controls.
84// #define ATTACK_AND_RELEASE
85
86// Define to expose other advanced, experimental dialog controls
87//#define ADVANCED_SETTINGS
88
89// Define to make the old statistical methods an available choice
90//#define OLD_METHOD_AVAILABLE
91
92namespace {
93
94enum DiscriminationMethod : size_t {
98
101};
102
106 // Experimental only, don't need translations
107 { XO("Median") },
108 { XO("Second greatest") },
109 { XO("Old") },
111
112// magic number used only in the old statistics
113// and the old discrimination
114const float minSignalTime = 0.05f;
115
116enum WindowTypes : unsigned {
117 WT_RECTANGULAR_HANN = 0, // 2.0.6 behavior, requires 1/2 step
118 WT_HANN_RECTANGULAR, // requires 1/2 step
119 WT_HANN_HANN, // requires 1/4 step
120 WT_BLACKMAN_HANN, // requires 1/4 step
121 WT_HAMMING_RECTANGULAR, // requires 1/2 step
122 WT_HAMMING_HANN, // requires 1/4 step
123 // WT_HAMMING_INV_HAMMING, // requires 1/2 step
124
128
129const struct WindowTypesInfo {
131 unsigned minSteps;
133
134 // Experimental only, don't need translations
135 { Verbatim("none, Hann (2.0.6 behavior)"), 2 },
136 /* i18n-hint: Hann is a proper name */
137 { Verbatim("Hann, none"), 2 },
138 /* i18n-hint: Hann is a proper name */
139 { Verbatim("Hann, Hann (default)"), 4 },
140 /* i18n-hint: Hann and Blackman are proper names */
141 { Verbatim("Blackman, Hann"), 4 },
142 /* i18n-hint: Hamming is a proper name */
143 { Verbatim("Hamming, none"), 2 },
144 /* i18n-hint: Hamming and Hann area proper names */
145 { Verbatim("Hamming, Hann"), 4 },
146 /* i18n-hint: Hamming is a proper name */
147 // { XO("Hamming, Reciprocal Hamming"), 2, }, // output window is special
149
150enum {
151 DEFAULT_WINDOW_SIZE_CHOICE = 8, // corresponds to 2048
152 DEFAULT_STEPS_PER_WINDOW_CHOICE = 1 // corresponds to 4, minimum for WT_HANN_HANN
154
159};
160
161} // namespace
162
163//----------------------------------------------------------------------------
164// EffectNoiseReduction::Statistics
165//----------------------------------------------------------------------------
166
168{
169public:
170 Statistics(size_t spectrumSize, double rate, int windowTypes)
171 : mRate{ rate }
172 , mWindowSize{ (spectrumSize - 1) * 2 }
173 , mWindowTypes{ windowTypes }
174 , mTotalWindows{ 0 }
175 , mTrackWindows{ 0 }
176 , mSums( spectrumSize )
177 , mMeans (spectrumSize )
178#ifdef OLD_METHOD_AVAILABLE
179 , mNoiseThreshold( spectrumSize )
180#endif
181 {}
182
183 // Noise profile statistics follow
184
185 double mRate; // Rate of profile track(s) -- processed tracks must match
188
193
194#ifdef OLD_METHOD_AVAILABLE
195 // Old statistics:
196 FloatVector mNoiseThreshold;
197#endif
198};
199
200//----------------------------------------------------------------------------
201// EffectNoiseReduction::Settings
202//----------------------------------------------------------------------------
203
204// This object is the memory of the effect between uses
205// (other than noise profile statistics)
207{
208public:
209 Settings();
211
213 wxWindow &parent, bool bHasProfile, bool bAllowTwiddleSettings);
214 bool PrefsIO(bool read);
215 bool Validate(EffectNoiseReduction *effect) const;
216
217 size_t WindowSize() const { return 1u << (3 + mWindowSizeChoice); }
218 unsigned StepsPerWindow() const { return 1u << (1 + mStepsPerWindowChoice); }
219
221
222 // Stored in preferences:
223
224 // Basic:
225 double mNewSensitivity; // - log10 of a probability... yeah.
226 double mFreqSmoothingBands; // really an integer
227 double mNoiseGain; // in dB, positive
228 double mAttackTime; // in secs
229 double mReleaseTime; // in secs
230
231 // Advanced:
232 double mOldSensitivity; // in dB, plus or minus
233
234 // Basic:
236
237 // Advanced:
242};
243
245 : mDoProfile{ true }
246{
247 PrefsIO(true);
248}
249
250//----------------------------------------------------------------------------
251// EffectNoiseReduction::Worker
252//----------------------------------------------------------------------------
253
254// This object holds information needed only during effect calculation
257{
258public:
261
262 Worker(eWindowFunctions inWindowType, eWindowFunctions outWindowType,
263 EffectNoiseReduction &effect, const Settings &settings,
264 Statistics &statistics
265#ifdef EXPERIMENTAL_SPECTRAL_EDITING
266 , double f0, double f1
267#endif
268 );
269 ~Worker();
270
271 struct MyWindow : public Window
272 {
273 explicit MyWindow(size_t windowSize)
274 : Window{ windowSize }
275 , mSpectrums(windowSize / 2 + 1)
276 , mGains(windowSize / 2 + 1)
277 {}
278 ~MyWindow() override;
279
282 };
283
284 bool Process(TrackList &tracks, double mT0, double mT1);
285
286protected:
287 MyWindow &NthWindow(int nn) { return static_cast<MyWindow&>(Nth(nn)); }
288 std::unique_ptr<Window> NewWindow(size_t windowSize) override;
289 bool DoStart() override;
290 static bool Processor(SpectrumTransformer &transformer);
291 bool DoFinish() override;
292
293private:
294 void ApplyFreqSmoothing(FloatVector &gains);
295 void GatherStatistics();
296 inline bool Classify(unsigned nWindows, int band);
297 void ReduceNoise();
299
300private:
301
302 const bool mDoProfile;
303
306
308 const size_t mFreqSmoothingBins;
309 // When spectral selection limits the affected band:
310 size_t mBinLow; // inclusive lower bound
311 size_t mBinHigh; // exclusive upper bound
312
314 const int mMethod;
315 const double mNewSensitivity;
316
321
323 unsigned mCenter;
324 unsigned mHistoryLen;
325
326 // Following are for progress indicator only:
330};
331
332/****************************************************************//*****************************************************************/
338
339//----------------------------------------------------------------------------
340// EffectNoiseReduction::Dialog
341//----------------------------------------------------------------------------
342
344{
345public:
346 // constructors and destructors
349 wxWindow *parent, bool bHasProfile,
350 bool bAllowTwiddleSettings);
351
352 void PopulateOrExchange(ShuttleGui & S) override;
353 bool TransferDataToWindow() override;
354 bool TransferDataFromWindow() override;
355
357 { return mTempSettings; }
358
359private:
361
362#ifdef ADVANCED_SETTINGS
363 void EnableDisableSensitivityControls();
364#endif
365
366 // handlers
367 void OnGetProfile( wxCommandEvent &event );
368 void OnNoiseReductionChoice( wxCommandEvent &event );
369#ifdef ADVANCED_SETTINGS
370 void OnMethodChoice(wxCommandEvent &);
371#endif
372 void OnPreview(wxCommandEvent &event) override;
373 void OnReduceNoise( wxCommandEvent &event );
374 void OnCancel( wxCommandEvent &event );
375 void OnHelp( wxCommandEvent &event );
376
377 void OnText(wxCommandEvent &event);
378 void OnSlider(wxCommandEvent &event);
379
380 // data members
381
387
390
391
392 wxRadioButton *mKeepSignal;
393#ifdef ISOLATE_CHOICE
394 wxRadioButton *mKeepNoise;
395#endif
396#ifdef RESIDUE_CHOICE
397 wxRadioButton *mResidue;
398#endif
399
400private:
401 DECLARE_EVENT_TABLE()
402};
403
405{ XO("Noise Reduction") };
406
408
410: mSettings(std::make_unique<EffectNoiseReduction::Settings>())
411{
412}
413
415{
416}
417
418// ComponentInterface implementation
419
421{
422 return Symbol;
423}
424
426{
427 return XO("Removes background noise such as fans, tape noise, or hums");
428}
429
430// EffectDefinitionInterface implementation
431
433{
434 return EffectTypeProcess;
435}
436
439
444 wxWindow &parent, const EffectDialogFactory &,
445 std::shared_ptr<EffectInstance> &pInstance, EffectSettingsAccess &access,
446 bool forceModal)
447{
448 // Assign the out parameter
449 pInstance = MakeInstance();
450
451 // to do: use forceModal correctly
452
453 // Doesn't use the factory but substitutes its own dialog
454
455 // We may want to twiddle the levels if we are setting
456 // from a macro editing dialog
457 return mSettings->PromptUser(this, access, parent,
459}
460
462 EffectSettingsAccess &access, wxWindow &parent,
463 bool bHasProfile, bool bAllowTwiddleSettings)
464{
465 EffectNoiseReduction::Dialog dlog(effect, access,
466 this, &parent, bHasProfile, bAllowTwiddleSettings);
467
468 dlog.CentreOnParent();
469 dlog.ShowModal();
470
471 const auto returnCode = dlog.GetReturnCode();
472 if (!returnCode)
473 return 0;
474
475 *this = dlog.GetTempSettings();
476 mDoProfile = (returnCode == 1);
477
478 if (!PrefsIO(false))
479 return 0;
480 return returnCode;
481}
482
483namespace {
484 template <typename StructureType, typename FieldType>
486 typedef FieldType (StructureType::*MemberPointer);
487
489 const wxChar *name;
490 FieldType defaultValue;
491 };
492
493 template <typename StructureType, typename FieldType>
495 StructureType *structure, const wxString &prefix,
496 const PrefsTableEntry<StructureType, FieldType> *fields, size_t numFields)
497 {
498 for (size_t ii = 0; ii < numFields; ++ii) {
500 gPrefs->Read(prefix + entry.name, &(structure->*(entry.field)),
501 entry.defaultValue);
502 }
503 }
504
505 template <typename StructureType, typename FieldType>
507 const StructureType *structure, const wxString &prefix,
508 const PrefsTableEntry<StructureType, FieldType> *fields, size_t numFields)
509 {
510 for (size_t ii = 0; ii < numFields; ++ii) {
512 gPrefs->Write(prefix + entry.name, structure->*(entry.field));
513 }
514 }
515}
516
518{
519 static const double DEFAULT_OLD_SENSITIVITY = 0.0;
520
521 static const PrefsTableEntry<Settings, double> doubleTable[] = {
522 { &Settings::mNewSensitivity, wxT("Sensitivity"), 6.0 },
523 { &Settings::mNoiseGain, wxT("Gain"), 6.0 },
524 { &Settings::mAttackTime, wxT("AttackTime"), 0.02 },
525 { &Settings::mReleaseTime, wxT("ReleaseTime"), 0.10 },
526 { &Settings::mFreqSmoothingBands, wxT("FreqSmoothing"), 6.0 },
527
528 // Advanced settings
529 { &Settings::mOldSensitivity, wxT("OldSensitivity"), DEFAULT_OLD_SENSITIVITY },
530 };
531 static auto doubleTableSize = sizeof(doubleTable) / sizeof(doubleTable[0]);
532
533 static const PrefsTableEntry<Settings, int> intTable[] = {
534 { &Settings::mNoiseReductionChoice, wxT("ReductionChoice"), NRC_REDUCE_NOISE },
535
536 // Advanced settings
540 { &Settings::mMethod, wxT("Method"), DM_DEFAULT_METHOD },
541 };
542 static auto intTableSize = sizeof(intTable) / sizeof(intTable[0]);
543
544 static const wxString prefix(wxT("/Effects/NoiseReduction/"));
545
546 if (read) {
547 readPrefs(this, prefix, doubleTable, doubleTableSize);
548 readPrefs(this, prefix, intTable, intTableSize);
549
550 // Ignore preferences for unavailable options.
551#if !(defined(RESIDUE_CHOICE) || defined (ISOLATE_CHOICE))
552 mNoiseReductionChoice == NRC_REDUCE_NOISE;
553#elif !(defined(RESIDUE_CHOICE))
554 if (mNoiseReductionChoice == NRC_LEAVE_RESIDUE)
555 mNoiseReductionChoice = NRC_ISOLATE_NOISE;
556#elif !(defined(ISOLATE_CHOICE))
557 if (mNoiseReductionChoice == NRC_ISOLATE_NOISE)
558 mNoiseReductionChoice = NRC_LEAVE_RESIDUE;
559#endif
560
561#ifndef ADVANCED_SETTINGS
562 // Initialize all hidden advanced settings to defaults.
563 mWindowTypes = WT_DEFAULT_WINDOW_TYPES;
564 mWindowSizeChoice = DEFAULT_WINDOW_SIZE_CHOICE;
565 mStepsPerWindowChoice = DEFAULT_STEPS_PER_WINDOW_CHOICE;
566 mMethod = DM_DEFAULT_METHOD;
567 mOldSensitivity = DEFAULT_OLD_SENSITIVITY;
568#endif
569
570#ifndef OLD_METHOD_AVAILABLE
571 if (mMethod == DM_OLD_METHOD)
572 mMethod = DM_DEFAULT_METHOD;
573#endif
574
575 return true;
576 }
577 else {
578 writePrefs(this, prefix, doubleTable, doubleTableSize);
579 writePrefs(this, prefix, intTable, intTableSize);
580 return gPrefs->Flush();
581 }
582}
583
585{
586 if (StepsPerWindow() < windowTypesInfo[mWindowTypes].minSteps) {
588 XO("Steps per block are too few for the window types.") );
589 return false;
590 }
591
592 if (StepsPerWindow() > WindowSize()) {
594 XO("Steps per block cannot exceed the window size.") );
595 return false;
596 }
597
598 if (mMethod == DM_MEDIAN && StepsPerWindow() > 4) {
600 XO(
601"Median method is not implemented for more than four steps per window.") );
602 return false;
603 }
604
605 return true;
606}
607
609 -> std::unique_ptr<Window>
610{
611 return std::make_unique<MyWindow>(windowSize);
612}
613
615{
616}
617
619{
620 // This same code will either reduce noise or profile it
621
622 this->CopyInputTracks(); // Set up mOutputTracks.
623
624 auto track = * (mOutputTracks->Selected< const WaveTrack >()).begin();
625 if (!track)
626 return false;
627
628 // Initialize statistics if gathering them, or check for mismatched (advanced)
629 // settings if reducing noise.
630 if (mSettings->mDoProfile) {
631 size_t spectrumSize = 1 + mSettings->WindowSize() / 2;
632 mStatistics = std::make_unique<Statistics>
633 (spectrumSize, track->GetRate(), mSettings->mWindowTypes);
634 }
635 else if (mStatistics->mWindowSize != mSettings->WindowSize()) {
636 // possible only with advanced settings
638 XO("You must specify the same window size for steps 1 and 2.") );
639 return false;
640 }
641 else if (mStatistics->mWindowTypes != mSettings->mWindowTypes) {
642 // A warning only
644 XO("Warning: window types are not the same as for profiling.") );
645 }
646
647 eWindowFunctions inWindowType, outWindowType;
648 switch (mSettings->mWindowTypes) {
650 inWindowType = eWinFuncRectangular;
651 outWindowType = eWinFuncHann;
652 break;
654 inWindowType = eWinFuncHann;
655 outWindowType = eWinFuncRectangular;
656 break;
657 case WT_BLACKMAN_HANN:
658 inWindowType = eWinFuncBlackman;
659 outWindowType = eWinFuncHann;
660 break;
662 inWindowType = eWinFuncHamming;
663 outWindowType = eWinFuncRectangular;
664 break;
665 case WT_HAMMING_HANN:
666 inWindowType = eWinFuncHamming;
667 outWindowType = eWinFuncHann;
668 break;
669 default:
670 wxASSERT(false);
671 [[fallthrough]] ;
672 case WT_HANN_HANN:
673 inWindowType = outWindowType = eWinFuncHann;
674 break;
675 }
676 Worker worker{ inWindowType, outWindowType,
677 *this, *mSettings, *mStatistics
678#ifdef EXPERIMENTAL_SPECTRAL_EDITING
679 , mF0, mF1
680#endif
681 };
682 bool bGoodResult = worker.Process(*mOutputTracks, mT0, mT1);
683 if (mSettings->mDoProfile) {
684 if (bGoodResult)
685 mSettings->mDoProfile = false; // So that "repeat last effect" will reduce noise
686 else
687 mStatistics.reset(); // So that profiling must be done again before noise reduction
688 }
689 this->ReplaceProcessedTracks(bGoodResult);
690 return bGoodResult;
691}
692
694{
695}
696
698 TrackList &tracks, double inT0, double inT1)
699{
700 mProgressTrackCount = 0;
701 for ( auto track : tracks.Selected< WaveTrack >() ) {
702 mProgressWindowCount = 0;
703 if (track->GetRate() != mStatistics.mRate) {
704 if (mDoProfile)
706 XO("All noise profile data must have the same sample rate.") );
707 else
709 XO(
710"The sample rate of the noise profile must match that of the sound to be processed.") );
711 return false;
712 }
713
714 double trackStart = track->GetStartTime();
715 double trackEnd = track->GetEndTime();
716 double t0 = std::max(trackStart, inT0);
717 double t1 = std::min(trackEnd, inT1);
718
719 if (t1 > t0) {
720 auto start = track->TimeToLongSamples(t0);
721 auto end = track->TimeToLongSamples(t1);
722 const auto len = end - start;
723 mLen = len;
724 const auto extra = (mStepsPerWindow - 1) * mStepSize;
725 // Adjust denominator for presence or absence of padding,
726 // which makes the number of windows visited either more or less
727 // than the number of window steps in the data.
728 if (mDoProfile)
729 mLen -= extra;
730 else
731 mLen += extra;
732
734 Processor, track, mHistoryLen, start, len ))
735 return false;
736 }
737 ++mProgressTrackCount;
738 }
739
740 if (mDoProfile) {
741 if (mStatistics.mTotalWindows == 0) {
743 XO("Selected noise profile is too short."));
744 return false;
745 }
746 }
747
748 return true;
749}
750
752{
753 // Given an array of gain mutipliers, average them
754 // GEOMETRICALLY. Don't multiply and take nth root --
755 // that may quickly cause underflows. Instead, average the logs.
756
757 if (mFreqSmoothingBins == 0)
758 return;
759
760 {
761 auto pScratch = mFreqSmoothingScratch.data();
762 std::fill(pScratch, pScratch + mSpectrumSize, 0.0f);
763 }
764
765 for (size_t ii = 0; ii < mSpectrumSize; ++ii)
766 gains[ii] = log(gains[ii]);
767
768 // ii must be signed
769 for (int ii = 0; ii < (int)mSpectrumSize; ++ii) {
770 const int j0 = std::max(0, ii - (int)mFreqSmoothingBins);
771 const int j1 = std::min(mSpectrumSize - 1, ii + mFreqSmoothingBins);
772 for(int jj = j0; jj <= j1; ++jj) {
773 mFreqSmoothingScratch[ii] += gains[jj];
774 }
775 mFreqSmoothingScratch[ii] /= (j1 - j0 + 1);
776 }
777
778 for (size_t ii = 0; ii < mSpectrumSize; ++ii)
779 gains[ii] = exp(mFreqSmoothingScratch[ii]);
780}
781
783 eWindowFunctions outWindowType,
784 EffectNoiseReduction &effect,
785 const Settings &settings, Statistics &statistics
786#ifdef EXPERIMENTAL_SPECTRAL_EDITING
787 , double f0, double f1
788#endif
789)
790: TrackSpectrumTransformer{ !settings.mDoProfile, inWindowType, outWindowType,
791 settings.WindowSize(), settings.StepsPerWindow(),
792 !settings.mDoProfile, !settings.mDoProfile
793}
794, mDoProfile{ settings.mDoProfile }
795
796, mEffect{ effect }
797, mStatistics{ statistics }
798
799, mFreqSmoothingScratch( mSpectrumSize )
800, mFreqSmoothingBins{ size_t(std::max(0.0, settings.mFreqSmoothingBands)) }
801, mBinLow{ 0 }
802, mBinHigh{ mSpectrumSize }
803
804, mNoiseReductionChoice{ settings.mNoiseReductionChoice }
805, mMethod{ settings.mMethod }
806
807// Sensitivity setting is a base 10 log, turn it into a natural log
808, mNewSensitivity{ settings.mNewSensitivity * log(10.0) }
809{
810 const auto sampleRate = mStatistics.mRate;
811
812#ifdef EXPERIMENTAL_SPECTRAL_EDITING
813 {
814 // mBinLow is inclusive, mBinHigh is exclusive, of
815 // the range of frequencies to affect. Include any
816 // bin that partly overlaps the selected range of frequencies.
817 const double bin = sampleRate / mWindowSize;
818 if (f0 >= 0.0 )
819 mBinLow = floor(f0 / bin);
820 if (f1 >= 0.0)
821 mBinHigh = ceil(f1 / bin);
822 }
823#endif
824
825 const double noiseGain = -settings.mNoiseGain;
826 const unsigned nAttackBlocks = 1 + (int)(settings.mAttackTime * sampleRate / mStepSize);
827 const unsigned nReleaseBlocks = 1 + (int)(settings.mReleaseTime * sampleRate / mStepSize);
828 // Applies to amplitudes, divide by 20:
829 mNoiseAttenFactor = DB_TO_LINEAR(noiseGain);
830 // Apply to gain factors which apply to amplitudes, divide by 20:
831 mOneBlockAttack = DB_TO_LINEAR(noiseGain / nAttackBlocks);
832 mOneBlockRelease = DB_TO_LINEAR(noiseGain / nReleaseBlocks);
833 // Applies to power, divide by 10:
835
837 ? std::max(2, (int)(minSignalTime * sampleRate / mStepSize))
838 : 1 + mStepsPerWindow;
839
841 wxASSERT(mCenter >= 1); // release depends on this assumption
842
843 if (mDoProfile)
844#ifdef OLD_METHOD_AVAILABLE
846#else
847 mHistoryLen = 1;
848#endif
849 else {
850 // Allow long enough queue for sufficient inspection of the middle
851 // and for attack processing
852 // See ReduceNoise()
853 mHistoryLen = std::max(mNWindowsToExamine, mCenter + nAttackBlocks);
854 }
855}
856
858{
859 for (size_t ii = 0, nn = TotalQueueSize(); ii < nn; ++ii) {
860 MyWindow &record = NthWindow(ii);
861 std::fill(record.mSpectrums.begin(), record.mSpectrums.end(), 0.0);
862 std::fill(record.mGains.begin(), record.mGains.end(), mNoiseAttenFactor);
863 }
865}
866
868{
869 auto &worker = static_cast<Worker &>(transformer);
870 // Compute power spectrum in the newest window
871 {
872 MyWindow &record = worker.NthWindow(0);
873 float *pSpectrum = &record.mSpectrums[0];
874 const double dc = record.mRealFFTs[0];
875 *pSpectrum++ = dc * dc;
876 float *pReal = &record.mRealFFTs[1], *pImag = &record.mImagFFTs[1];
877 for (size_t nn = worker.mSpectrumSize - 2; nn--;) {
878 const double re = *pReal++, im = *pImag++;
879 *pSpectrum++ = re * re + im * im;
880 }
881 const double nyquist = record.mImagFFTs[0];
882 *pSpectrum = nyquist * nyquist;
883 }
884
885 if (worker.mDoProfile)
886 worker.GatherStatistics();
887 else
888 worker.ReduceNoise();
889
890 // Update the Progress meter, let user cancel
891 return !worker.mEffect.TrackProgress(worker.mProgressTrackCount,
892 std::min(1.0,
893 ((++worker.mProgressWindowCount).as_double() * worker.mStepSize)
894 / worker.mLen.as_double()));
895}
896
898{
899 const auto windows = mStatistics.mTrackWindows;
900
901 // Combine averages in case of multiple profile tracks.
902 if (windows) {
903 const auto multiplier = mStatistics.mTotalWindows;
904 const auto denom = windows + multiplier;
905 for (size_t ii = 0, nn = mStatistics.mMeans.size(); ii < nn; ++ii) {
906 auto &mean = mStatistics.mMeans[ii];
907 auto &sum = mStatistics.mSums[ii];
908 mean = (mean * multiplier + sum) / denom;
909 // Reset for next track
910 sum = 0;
911 }
912 // Reset for next track
913 mStatistics.mTrackWindows = 0;
914 mStatistics.mTotalWindows = denom;
915 }
916}
917
919{
920 ++mStatistics.mTrackWindows;
921
922 {
923 // NEW statistics
924 auto pPower = NthWindow(0).mSpectrums.data();
925 auto pSum = mStatistics.mSums.data();
926 for (size_t jj = 0; jj < mSpectrumSize; ++jj) {
927 *pSum++ += *pPower++;
928 }
929 }
930
931#ifdef OLD_METHOD_AVAILABLE
932 // The noise threshold for each frequency is the maximum
933 // level achieved at that frequency for a minimum of
934 // mMinSignalBlocks blocks in a row - the max of a min.
935
936 auto finish = mHistoryLen;
937
938 {
939 // old statistics
940 auto pPower = NthWindow(0).mSpectrums.data();
941 auto pThreshold = mStatistics.mNoiseThreshold.data();
942 for (size_t jj = 0; jj < mSpectrumSize; ++jj) {
943 float min = *pPower++;
944 for (unsigned ii = 1; ii < finish; ++ii)
945 min = std::min(min, NthWindow(ii).mSpectrums[jj]);
946 *pThreshold = std::max(*pThreshold, min);
947 ++pThreshold;
948 }
949 }
950#endif
951}
952
953// Return true iff the given band of the "center" window looks like noise.
954// Examine the band in a few neighboring windows to decide.
955inline
956bool EffectNoiseReduction::Worker::Classify(unsigned nWindows, int band)
957{
958 switch (mMethod) {
959#ifdef OLD_METHOD_AVAILABLE
960 case DM_OLD_METHOD:
961 {
962 float min = NthWindow(0).mSpectrums[band];
963 for (unsigned ii = 1; ii < nWindows; ++ii)
964 min = std::min(min, NthWindow(ii).mSpectrums[band]);
965 return
966 min <= mOldSensitivityFactor * mStatistics.mNoiseThreshold[band];
967 }
968#endif
969 // New methods suppose an exponential distribution of power values
970 // in the noise; NEW sensitivity (which is nonnegative) is meant to be
971 // the negative of a log of probability (so the log is nonpositive)
972 // that noise strays above the threshold. Call that probability
973 // 1 - F. The quantile function of an exponential distribution is
974 // - log (1 - F) * mean. Thus simply multiply mean by sensitivity
975 // to get the threshold.
976 case DM_MEDIAN:
977 // This method examines the window and all other windows
978 // whose centers lie on or between its boundaries, and takes a median, to
979 // avoid being fooled by up and down excursions into
980 // either the mistake of classifying noise as not noise
981 // (leaving a musical noise chime), or the opposite
982 // (distorting the signal with a drop out).
983 if (nWindows <= 3)
984 // No different from second greatest.
985 goto secondGreatest;
986 else if (nWindows <= 5)
987 {
988 float greatest = 0.0, second = 0.0, third = 0.0;
989 for (unsigned ii = 0; ii < nWindows; ++ii) {
990 const float power = NthWindow(ii).mSpectrums[band];
991 if (power >= greatest)
992 third = second, second = greatest, greatest = power;
993 else if (power >= second)
994 third = second, second = power;
995 else if (power >= third)
996 third = power;
997 }
998 return third <= mNewSensitivity * mStatistics.mMeans[band];
999 }
1000 else {
1001 // not implemented
1002 wxASSERT(false);
1003 return true;
1004 }
1005 secondGreatest:
1006 case DM_SECOND_GREATEST:
1007 {
1008 // This method just throws out the high outlier. It
1009 // should be less prone to distortions and more prone to
1010 // chimes.
1011 float greatest = 0.0, second = 0.0;
1012 for (unsigned ii = 0; ii < nWindows; ++ii) {
1013 const float power = NthWindow(ii).mSpectrums[band];
1014 if (power >= greatest)
1015 second = greatest, greatest = power;
1016 else if (power >= second)
1017 second = power;
1018 }
1019 return second <= mNewSensitivity * mStatistics.mMeans[band];
1020 }
1021 default:
1022 wxASSERT(false);
1023 return true;
1024 }
1025}
1026
1028{
1029 auto historyLen = CurrentQueueSize();
1030 auto nWindows = std::min<unsigned>(mNWindowsToExamine, historyLen);
1031
1032 if (mNoiseReductionChoice != NRC_ISOLATE_NOISE)
1033 {
1034 MyWindow &record = NthWindow(0);
1035 // Default all gains to the reduction factor,
1036 // until we decide to raise some of them later
1037 float *pGain = &record.mGains[0];
1038 std::fill(pGain, pGain + mSpectrumSize, mNoiseAttenFactor);
1039 }
1040
1041 // Raise the gain for elements in the center of the sliding history
1042 // or, if isolating noise, zero out the non-noise
1043 if (nWindows > mCenter)
1044 {
1045 auto pGain = NthWindow(mCenter).mGains.data();
1046 if (mNoiseReductionChoice == NRC_ISOLATE_NOISE) {
1047 // All above or below the selected frequency range is non-noise
1048 std::fill(pGain, pGain + mBinLow, 0.0f);
1049 std::fill(pGain + mBinHigh, pGain + mSpectrumSize, 0.0f);
1050 pGain += mBinLow;
1051 for (size_t jj = mBinLow; jj < mBinHigh; ++jj) {
1052 const bool isNoise = Classify(nWindows, jj);
1053 *pGain++ = isNoise ? 1.0 : 0.0;
1054 }
1055 }
1056 else {
1057 // All above or below the selected frequency range is non-noise
1058 std::fill(pGain, pGain + mBinLow, 1.0f);
1059 std::fill(pGain + mBinHigh, pGain + mSpectrumSize, 1.0f);
1060 pGain += mBinLow;
1061 for (size_t jj = mBinLow; jj < mBinHigh; ++jj) {
1062 const bool isNoise = Classify(nWindows, jj);
1063 if (!isNoise)
1064 *pGain = 1.0;
1065 ++pGain;
1066 }
1067 }
1068 }
1069
1070 if (mNoiseReductionChoice != NRC_ISOLATE_NOISE)
1071 {
1072 // In each direction, define an exponential decay of gain from the
1073 // center; make actual gains the maximum of mNoiseAttenFactor, and
1074 // the decay curve, and their prior values.
1075
1076 // First, the attack, which goes backward in time, which is,
1077 // toward higher indices in the queue.
1078 for (size_t jj = 0; jj < mSpectrumSize; ++jj) {
1079 for (unsigned ii = mCenter + 1; ii < historyLen; ++ii) {
1080 const float minimum =
1081 std::max(mNoiseAttenFactor,
1082 NthWindow(ii - 1).mGains[jj] * mOneBlockAttack);
1083 float &gain = NthWindow(ii).mGains[jj];
1084 if (gain < minimum)
1085 gain = minimum;
1086 else
1087 // We can stop now, our attack curve is intersecting
1088 // the release curve of some window previously processed.
1089 break;
1090 }
1091 }
1092
1093 // Now, release. We need only look one window ahead. This part will
1094 // be visited again when we examine the next window, and
1095 // carry the decay further.
1096 {
1097 auto pNextGain = NthWindow(mCenter - 1).mGains.data();
1098 auto pThisGain = NthWindow(mCenter).mGains.data();
1099 for (auto nn = mSpectrumSize; nn--;) {
1100 *pNextGain =
1101 std::max(*pNextGain,
1102 std::max(mNoiseAttenFactor,
1103 *pThisGain++ * mOneBlockRelease));
1104 ++pNextGain;
1105 }
1106 }
1107 }
1108
1109
1110 if (QueueIsFull()) {
1111 auto &record = NthWindow(historyLen - 1); // end of the queue
1112 const auto last = mSpectrumSize - 1;
1113
1114 if (mNoiseReductionChoice != NRC_ISOLATE_NOISE)
1115 // Apply frequency smoothing to output gain
1116 // Gains are not less than mNoiseAttenFactor
1117 ApplyFreqSmoothing(record.mGains);
1118
1119 // Apply gain to FFT
1120 {
1121 const float *pGain = &record.mGains[1];
1122 float *pReal = &record.mRealFFTs[1];
1123 float *pImag = &record.mImagFFTs[1];
1124 auto nn = mSpectrumSize - 2;
1125 if (mNoiseReductionChoice == NRC_LEAVE_RESIDUE) {
1126 for (; nn--;) {
1127 // Subtract the gain we would otherwise apply from 1, and
1128 // negate that to flip the phase.
1129 const double gain = *pGain++ - 1.0;
1130 *pReal++ *= gain;
1131 *pImag++ *= gain;
1132 }
1133 record.mRealFFTs[0] *= (record.mGains[0] - 1.0);
1134 // The Fs/2 component is stored as the imaginary part of the DC component
1135 record.mImagFFTs[0] *= (record.mGains[last] - 1.0);
1136 }
1137 else {
1138 for (; nn--;) {
1139 const double gain = *pGain++;
1140 *pReal++ *= gain;
1141 *pImag++ *= gain;
1142 }
1143 record.mRealFFTs[0] *= record.mGains[0];
1144 // The Fs/2 component is stored as the imaginary part of the DC component
1145 record.mImagFFTs[0] *= record.mGains[last];
1146 }
1147 }
1148 }
1149}
1150
1152{
1153 if (mDoProfile)
1154 FinishTrackStatistics();
1156}
1157
1158//----------------------------------------------------------------------------
1159// EffectNoiseReduction::Dialog
1160//----------------------------------------------------------------------------
1161
1162enum {
1165#ifdef ISOLATE_CHOICE
1167#endif
1168#ifdef RESIDUE_CHOICE
1170#endif
1171
1172#ifdef ADVANCED_SETTINGS
1173 ID_CHOICE_METHOD,
1174#endif
1175
1176 // Slider/text pairs
1179
1182
1183#ifdef ATTACK_AND_RELEASE
1184 ID_ATTACK_TIME_SLIDER,
1185 ID_ATTACK_TIME_TEXT,
1186
1187 ID_RELEASE_TIME_SLIDER,
1188 ID_RELEASE_TIME_TEXT,
1189#endif
1190
1193
1195
1196#ifdef ADVANCED_SETTINGS
1197 ID_OLD_SENSITIVITY_SLIDER = END_OF_BASIC_SLIDERS,
1198 ID_OLD_SENSITIVITY_TEXT,
1199
1200 END_OF_ADVANCED_SLIDERS,
1201 END_OF_SLIDERS = END_OF_ADVANCED_SLIDERS,
1202#else
1204#endif
1205
1207};
1208
1209namespace {
1210
1213
1214 double Value(long sliderSetting) const
1215 {
1216 return
1217 valueMin +
1218 (double(sliderSetting) / sliderMax) * (valueMax - valueMin);
1219 }
1220
1221 long SliderSetting(double value) const
1222 {
1223 return std::clamp<long>(
1224 0.5 + sliderMax * (value - valueMin) / (valueMax - valueMin),
1225 0, sliderMax);
1226 }
1227
1228 wxString Text(double value) const
1229 {
1230 if (formatAsInt)
1231 return wxString::Format(format, (int)(value));
1232 else
1233 return wxString::Format(format, value);
1234 }
1235
1236 void CreateControls(int id, ShuttleGui &S) const
1237 {
1238 wxTextCtrl *const text = S.Id(id + 1)
1239 .Validator<FloatingPointValidator<double>>(
1240 formatAsInt ? 0 : 2,
1241 nullptr,
1242 NumValidatorStyle::DEFAULT,
1243 valueMin, valueMax
1244 )
1245 .AddTextBox(textBoxCaption, wxT(""), 0);
1246
1247 wxSlider *const slider =
1248 S.Id(id)
1249 .Name( sliderName )
1250 .Style(wxSL_HORIZONTAL)
1251 .MinSize( { 150, -1 } )
1252 .AddSlider( {}, 0, sliderMax);
1253 }
1254
1256 double valueMin;
1257 double valueMax;
1259 // (valueMin - valueMax) / sliderMax is the value increment of the slider
1260 const wxChar* format;
1264
1265 ControlInfo(MemberPointer f, double vMin, double vMax, long sMax, const wxChar* fmt, bool fAsInt,
1266 const TranslatableString &caption, const TranslatableString &name)
1267 : field(f), valueMin(vMin), valueMax(vMax), sliderMax(sMax), format(fmt), formatAsInt(fAsInt)
1268 , textBoxCaption(caption), sliderName(name)
1269 {
1270 }
1271};
1272
1274 static const ControlInfo table[] = {
1276 0.0, 48.0, 48, wxT("%d"), true,
1277 XXO("&Noise reduction (dB):"), XO("Noise reduction")),
1279 0.0, 24.0, 48, wxT("%.2f"), false,
1280 XXO("&Sensitivity:"), XO("Sensitivity")),
1281#ifdef ATTACK_AND_RELEASE
1283 0, 1.0, 100, wxT("%.2f"), false,
1284 XXO("Attac&k time (secs):"), XO("Attack time")),
1286 0, 1.0, 100, wxT("%.2f"), false,
1287 XXO("R&elease time (secs):"), XO("Release time")),
1288#endif
1290 0, 12, 12, wxT("%d"), true,
1291 XXO("&Frequency smoothing (bands):"), XO("Frequency smoothing")),
1292
1293#ifdef ADVANCED_SETTINGS
1295 -20.0, 20.0, 4000, wxT("%.2f"), false,
1296 XXO("Sensiti&vity (dB):"), XO("Old Sensitivity")),
1297 // add here
1298#endif
1299 };
1300
1301return table;
1302}
1303
1304} // namespace
1305
1306
1313
1315#ifdef ISOLATE_CHOICE
1317#endif
1318#ifdef RESIDUE_CHOICE
1320#endif
1321
1322#ifdef ADVANCED_SETTINGS
1323 EVT_CHOICE(ID_CHOICE_METHOD, EffectNoiseReduction::Dialog::OnMethodChoice)
1324#endif
1325
1328
1331
1334
1335#ifdef ATTACK_AND_RELEASE
1336 EVT_SLIDER(ID_ATTACK_TIME_SLIDER, EffectNoiseReduction::Dialog::OnSlider)
1337 EVT_TEXT(ID_ATTACK_TIME_TEXT, EffectNoiseReduction::Dialog::OnText)
1338
1339 EVT_SLIDER(ID_RELEASE_TIME_SLIDER, EffectNoiseReduction::Dialog::OnSlider)
1340 EVT_TEXT(ID_RELEASE_TIME_TEXT, EffectNoiseReduction::Dialog::OnText)
1341#endif
1342
1343
1344#ifdef ADVANCED_SETTINGS
1345 EVT_SLIDER(ID_OLD_SENSITIVITY_SLIDER, EffectNoiseReduction::Dialog::OnSlider)
1346 EVT_TEXT(ID_OLD_SENSITIVITY_TEXT, EffectNoiseReduction::Dialog::OnText)
1347#endif
1349
1351 EffectSettingsAccess &access,
1353 wxWindow *parent, bool bHasProfile, bool bAllowTwiddleSettings)
1354 : EffectDialog( parent, XO("Noise Reduction"), EffectTypeProcess,wxDEFAULT_DIALOG_STYLE, eHelpButton )
1355 , m_pEffect(effect)
1356 , mAccess{access}
1357 , m_pSettings(settings) // point to
1358 , mTempSettings(*settings) // copy
1359 , mbHasProfile(bHasProfile)
1360 , mbAllowTwiddleSettings(bAllowTwiddleSettings)
1361 // NULL out the control members until the controls are created.
1362 , mKeepSignal(NULL)
1363#ifdef ISOLATE_CHOICE
1364 , mKeepNoise(NULL)
1365#endif
1366#ifdef RESIDUE_CHOICE
1367 , mResidue(NULL)
1368#endif
1369{
1371
1372 wxButton *const pButtonPreview =
1373 (wxButton *)wxWindow::FindWindowById(ID_EFFECT_PREVIEW, this);
1374 wxButton *const pButtonReduceNoise =
1375 (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
1376
1377 if (mbHasProfile || mbAllowTwiddleSettings) {
1378 pButtonPreview->Enable(!mbAllowTwiddleSettings);
1379 pButtonReduceNoise->SetFocus();
1380 }
1381 else {
1382 pButtonPreview->Enable(false);
1383 pButtonReduceNoise->Enable(false);
1384 }
1385}
1386
1388{
1389 // If Isolate is chosen, disable controls that define
1390 // "what to do with noise" rather than "what is noise."
1391 // Else, enable them.
1392 // This does NOT include sensitivity, NEW or old, nor
1393 // the choice of window functions, size, or step.
1394 // The method choice is not included, because it affects
1395 // which sensitivity slider is operative, and that is part
1396 // of what defines noise.
1397
1398 static const int toDisable[] = {
1401
1404
1405#ifdef ATTACK_AND_RELEASE
1406 ID_ATTACK_TIME_SLIDER,
1407 ID_ATTACK_TIME_TEXT,
1408
1409 ID_RELEASE_TIME_SLIDER,
1410 ID_RELEASE_TIME_TEXT,
1411#endif
1412 };
1413 static const auto nToDisable = sizeof(toDisable) / sizeof(toDisable[0]);
1414
1415 bool bIsolating =
1416#ifdef ISOLATE_CHOICE
1417 mKeepNoise->GetValue();
1418#else
1419 false;
1420#endif
1421 for (auto ii = nToDisable; ii--;)
1422 wxWindow::FindWindowById(toDisable[ii], this)->Enable(!bIsolating);
1423}
1424
1425#ifdef ADVANCED_SETTINGS
1426void EffectNoiseReduction::Dialog::EnableDisableSensitivityControls()
1427{
1428 wxChoice *const pChoice =
1429 static_cast<wxChoice*>(wxWindow::FindWindowById(ID_CHOICE_METHOD, this));
1430 const bool bOldMethod =
1431 pChoice->GetSelection() == DM_OLD_METHOD;
1432 wxWindow::FindWindowById(ID_OLD_SENSITIVITY_SLIDER, this)->Enable(bOldMethod);
1433 wxWindow::FindWindowById(ID_OLD_SENSITIVITY_TEXT, this)->Enable(bOldMethod);
1434 wxWindow::FindWindowById(ID_NEW_SENSITIVITY_SLIDER, this)->Enable(!bOldMethod);
1435 wxWindow::FindWindowById(ID_NEW_SENSITIVITY_TEXT, this)->Enable(!bOldMethod);
1436}
1437#endif
1438
1439void EffectNoiseReduction::Dialog::OnGetProfile(wxCommandEvent & WXUNUSED(event))
1440{
1441 // Project has not be changed so skip pushing state
1443
1445 return;
1446
1447 // Return code distinguishes this first step from the actual effect
1448 EndModal(1);
1449}
1450
1451// This handles the whole radio group
1452void EffectNoiseReduction::Dialog::OnNoiseReductionChoice( wxCommandEvent & WXUNUSED(event))
1453{
1454 if (mKeepSignal->GetValue())
1455 mTempSettings.mNoiseReductionChoice = NRC_REDUCE_NOISE;
1456#ifdef ISOLATE_CHOICE
1457 else if (mKeepNoise->GetValue())
1458 mTempSettings.mNoiseReductionChoice = NRC_ISOLATE_NOISE;
1459#endif
1460#ifdef RESIDUE_CHOICE
1461 else
1462 mTempSettings.mNoiseReductionChoice = NRC_LEAVE_RESIDUE;
1463#endif
1464 DisableControlsIfIsolating();
1465}
1466
1467#ifdef ADVANCED_SETTINGS
1468void EffectNoiseReduction::Dialog::OnMethodChoice(wxCommandEvent &)
1469{
1470 EnableDisableSensitivityControls();
1471}
1472#endif
1473
1474void EffectNoiseReduction::Dialog::OnPreview(wxCommandEvent & WXUNUSED(event))
1475{
1477 return;
1478
1479 // Save & restore parameters around Preview, because we didn't do OK.
1480 auto cleanup = valueRestorer( *m_pSettings );
1481 *m_pSettings = mTempSettings;
1482 m_pSettings->mDoProfile = false;
1483
1484 m_pEffect->Preview(mAccess,
1485 // Don't need any UI updates for preview
1486 {},
1487 false);
1488}
1489
1490void EffectNoiseReduction::Dialog::OnReduceNoise( wxCommandEvent & WXUNUSED(event))
1491{
1493 return;
1494
1495 EndModal(2);
1496}
1497
1498void EffectNoiseReduction::Dialog::OnCancel(wxCommandEvent & WXUNUSED(event))
1499{
1500 EndModal(0);
1501}
1502
1503void EffectNoiseReduction::Dialog::OnHelp(wxCommandEvent & WXUNUSED(event))
1504{
1505 HelpSystem::ShowHelp(this, "Noise_Reduction", true);
1506}
1507
1509{
1510 S.StartStatic(XO("Step 1"));
1511 {
1512 S.AddVariableText(XO(
1513"Select a few seconds of just noise so Audacity knows what to filter out,\nthen click Get Noise Profile:"));
1514 //m_pButton_GetProfile =
1515 S.Id(ID_BUTTON_GETPROFILE).AddButton(XXO("&Get Noise Profile"));
1516 }
1517 S.EndStatic();
1518
1519 S.StartStatic(XO("Step 2"));
1520 {
1521 S.AddVariableText(XO(
1522"Select all of the audio you want filtered, choose how much noise you want\nfiltered out, and then click 'OK' to reduce noise.\n"));
1523
1524 S.StartMultiColumn(3, wxEXPAND);
1525 S.SetStretchyCol(2);
1526 {
1527 for (int id = FIRST_SLIDER; id < END_OF_BASIC_SLIDERS; id += 2) {
1528 const ControlInfo &info = controlInfo()[(id - FIRST_SLIDER) / 2];
1529 info.CreateControls(id, S);
1530 }
1531 }
1532 S.EndMultiColumn();
1533
1534 S.StartMultiColumn(
1535 2
1536#ifdef RESIDUE_CHOICE
1537 +1
1538#endif
1539#ifdef ISOLATE_CHOICE
1540 +1
1541#endif
1542 ,
1543 wxALIGN_CENTER_HORIZONTAL);
1544 {
1545 S.AddPrompt(XXO("Noise:"));
1546 mKeepSignal = S.Id(ID_RADIOBUTTON_KEEPSIGNAL)
1547 /* i18n-hint: Translate differently from "Residue" ! */
1548 .AddRadioButton(XXO("Re&duce"));
1549#ifdef ISOLATE_CHOICE
1550 mKeepNoise = S.Id(ID_RADIOBUTTON_KEEPNOISE)
1551 .AddRadioButtonToGroup(XXO("&Isolate"));
1552#endif
1553#ifdef RESIDUE_CHOICE
1554 mResidue = S.Id(ID_RADIOBUTTON_RESIDUE)
1555 /* i18n-hint: Means the difference between effect and original sound. Translate differently from "Reduce" ! */
1556 .AddRadioButtonToGroup(XXO("Resid&ue"));
1557#endif
1558 }
1559 S.EndMultiColumn();
1560 }
1561 S.EndStatic();
1562
1563
1564#ifdef ADVANCED_SETTINGS
1565 S.StartStatic(XO("Advanced Settings"));
1566 {
1567 S.StartMultiColumn(2);
1568 {
1569 S.TieChoice(XXO("&Window types:"),
1570 mTempSettings.mWindowTypes,
1571 []{
1572 TranslatableStrings windowTypeChoices;
1573 for (size_t ii = 0; ii < WT_N_WINDOW_TYPES; ++ii)
1574 windowTypeChoices.push_back(windowTypesInfo[ii].name);
1575 return windowTypeChoices;
1576 }()
1577 );
1578
1579 S.TieChoice(XXO("Window si&ze:"),
1580 mTempSettings.mWindowSizeChoice,
1581 {
1582 XO("8") ,
1583 XO("16") ,
1584 XO("32") ,
1585 XO("64") ,
1586 XO("128") ,
1587 XO("256") ,
1588 XO("512") ,
1589 XO("1024") ,
1590 XO("2048 (default)") ,
1591 XO("4096") ,
1592 XO("8192") ,
1593 XO("16384") ,
1594 }
1595 );
1596
1597 S.TieChoice(XXO("S&teps per window:"),
1598 mTempSettings.mStepsPerWindowChoice,
1599 {
1600 XO("2") ,
1601 XO("4 (default)") ,
1602 XO("8") ,
1603 XO("16") ,
1604 XO("32") ,
1605 XO("64") ,
1606 }
1607 );
1608
1609 S.Id(ID_CHOICE_METHOD)
1610 .TieChoice(XXO("Discrimination &method:"),
1611 mTempSettings.mMethod,
1612 []{
1613 TranslatableStrings methodChoices;
1614 auto nn = DM_N_METHODS;
1615#ifndef OLD_METHOD_AVAILABLE
1616 --nn;
1617#endif
1618 for (auto ii = 0; ii < nn; ++ii)
1619 methodChoices.push_back(discriminationMethodInfo[ii].name);
1620 return methodChoices;
1621 }());
1622 }
1623 S.EndMultiColumn();
1624
1625 S.StartMultiColumn(3, wxEXPAND);
1626 S.SetStretchyCol(2);
1627 {
1628 for (int id = END_OF_BASIC_SLIDERS; id < END_OF_ADVANCED_SLIDERS; id += 2) {
1629 const ControlInfo &info = controlInfo()[(id - FIRST_SLIDER) / 2];
1630 info.CreateControls(id, S);
1631 }
1632 }
1633 S.EndMultiColumn();
1634 }
1635 S.EndStatic();
1636#endif
1637}
1638
1640{
1641 // Do the choice controls:
1643 return false;
1644
1645 for (int id = FIRST_SLIDER; id < END_OF_SLIDERS; id += 2) {
1646 wxSlider* slider =
1647 static_cast<wxSlider*>(wxWindow::FindWindowById(id, this));
1648 wxTextCtrl* text =
1649 static_cast<wxTextCtrl*>(wxWindow::FindWindowById(id + 1, this));
1650 const ControlInfo &info = controlInfo()[(id - FIRST_SLIDER) / 2];
1651 const double field = mTempSettings.*(info.field);
1652 text->SetValue(info.Text(field));
1653 slider->SetValue(info.SliderSetting(field));
1654 }
1655
1656 mKeepSignal->SetValue(mTempSettings.mNoiseReductionChoice == NRC_REDUCE_NOISE);
1657#ifdef ISOLATE_CHOICE
1658 mKeepNoise->SetValue(mTempSettings.mNoiseReductionChoice == NRC_ISOLATE_NOISE);
1659#endif
1660#ifdef RESIDUE_CHOICE
1661 mResidue->SetValue(mTempSettings.mNoiseReductionChoice == NRC_LEAVE_RESIDUE);
1662#endif
1663
1664 // Set the enabled states of controls
1665 DisableControlsIfIsolating();
1666#ifdef ADVANCED_SETTINGS
1667 EnableDisableSensitivityControls();
1668#endif
1669
1670 return true;
1671}
1672
1674{
1675 if( !wxWindow::Validate() )
1676 return false;
1677 // Do the choice controls:
1679 return false;
1680
1681 wxCommandEvent dummy;
1682 OnNoiseReductionChoice(dummy);
1683
1684 return mTempSettings.Validate(m_pEffect);
1685}
1686
1687void EffectNoiseReduction::Dialog::OnText(wxCommandEvent &event)
1688{
1689 int id = event.GetId();
1690 int idx = (id - FIRST_SLIDER - 1) / 2;
1691 const ControlInfo &info = controlInfo()[idx];
1692 wxTextCtrl* text =
1693 static_cast<wxTextCtrl*>(wxWindow::FindWindowById(id, this));
1694 wxSlider* slider =
1695 static_cast<wxSlider*>(wxWindow::FindWindowById(id - 1, this));
1696 double &field = mTempSettings.*(info.field);
1697
1698 text->GetValue().ToDouble(&field);
1699 slider->SetValue(info.SliderSetting(field));
1700}
1701
1703{
1704 int id = event.GetId();
1705 int idx = (id - FIRST_SLIDER) / 2;
1706 const ControlInfo &info = controlInfo()[idx];
1707 wxSlider* slider =
1708 static_cast<wxSlider*>(wxWindow::FindWindowById(id, this));
1709 wxTextCtrl* text =
1710 static_cast<wxTextCtrl*>(wxWindow::FindWindowById(id + 1, this));
1711 double &field = mTempSettings.*(info.field);
1712
1713 field = info.Value(slider->GetValue());
1714 text->SetValue(info.Text(field));
1715}
wxT("CloseDown"))
END_EVENT_TABLE()
int min(int a, int b)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
const TranslatableString name
Definition: Distortion.cpp:76
#define ID_EFFECT_PREVIEW
Definition: Effect.h:198
EffectType
@ EffectTypeProcess
std::function< DialogFactoryResults(wxWindow &parent, EffectPlugin &, EffectUIServices &, EffectSettingsAccess &) > EffectDialogFactory
Type of function that creates a dialog for an effect.
int format
Definition: ExportPCM.cpp:53
eWindowFunctions
Definition: FFT.h:110
@ eWinFuncRectangular
Definition: FFT.h:111
@ eWinFuncHamming
Definition: FFT.h:113
@ eWinFuncBlackman
Definition: FFT.h:115
@ eWinFuncHann
Definition: FFT.h:114
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
#define field(n, t)
Definition: ImportAUP.cpp:165
ValueRestorer< T > valueRestorer(T &var)
inline functions provide convenient parameter type deduction
Definition: MemoryX.h:251
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:560
#define RESIDUE_CHOICE
std::vector< float > FloatVector
@ ID_GAIN_TEXT
@ ID_BUTTON_GETPROFILE
@ ID_GAIN_SLIDER
@ ID_NEW_SENSITIVITY_SLIDER
@ ID_FREQ_SLIDER
@ END_OF_SLIDERS
@ END_OF_BASIC_SLIDERS
@ ID_RADIOBUTTON_RESIDUE
@ ID_NEW_SENSITIVITY_TEXT
@ ID_RADIOBUTTON_KEEPSIGNAL
@ FIRST_SLIDER
@ ID_FREQ_TEXT
@ ID_RADIOBUTTON_KEEPNOISE
FileConfig * gPrefs
Definition: Prefs.cpp:70
static ProjectFileIORegistry::AttributeWriterEntry entry
@ eHelpButton
Definition: ShuttleGui.h:598
#define S(N)
Definition: ToChars.cpp:64
static Settings & settings()
Definition: TrackInfo.cpp:87
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
double mT1
Definition: EffectBase.h:113
std::shared_ptr< TrackList > mOutputTracks
Definition: EffectBase.h:111
double mT0
Definition: EffectBase.h:112
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: EffectBase.cpp:232
bool TransferDataToWindow() override
Definition: EffectUI.cpp:1516
bool TransferDataFromWindow() override
Definition: EffectUI.cpp:1524
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:396
bool IsBatchProcessing() const override
Definition: Effect.cpp:302
Performs effect computation.
void SetSkipStateFlag(bool flag)
static EffectManager & Get()
Dialog used with EffectNoiseReduction.
const Settings & GetTempSettings() const
void OnNoiseReductionChoice(wxCommandEvent &event)
void OnSlider(wxCommandEvent &event)
void OnHelp(wxCommandEvent &event)
EffectNoiseReduction::Settings mTempSettings
EffectSettingsAccess & mAccess
This dialog is modal, so mAccess will live long enough for it.
void PopulateOrExchange(ShuttleGui &S) override
void OnText(wxCommandEvent &event)
Dialog(EffectNoiseReduction *effect, EffectSettingsAccess &access, Settings *settings, wxWindow *parent, bool bHasProfile, bool bAllowTwiddleSettings)
void OnReduceNoise(wxCommandEvent &event)
void OnPreview(wxCommandEvent &event) override
EffectNoiseReduction * m_pEffect
void OnGetProfile(wxCommandEvent &event)
void OnCancel(wxCommandEvent &event)
EffectNoiseReduction::Settings * m_pSettings
bool TransferDataFromWindow() override
int PromptUser(EffectNoiseReduction *effect, EffectSettingsAccess &access, wxWindow &parent, bool bHasProfile, bool bAllowTwiddleSettings)
bool Validate(EffectNoiseReduction *effect) const
Statistics(size_t spectrumSize, double rate, int windowTypes)
Worker(eWindowFunctions inWindowType, eWindowFunctions outWindowType, EffectNoiseReduction &effect, const Settings &settings, Statistics &statistics)
EffectNoiseReduction::Settings Settings
bool Process(TrackList &tracks, double mT0, double mT1)
static bool Processor(SpectrumTransformer &transformer)
void ApplyFreqSmoothing(FloatVector &gains)
EffectNoiseReduction & mEffect
bool DoStart() override
Called before any calls to ProcessWindow.
bool Classify(unsigned nWindows, int band)
bool DoFinish() override
Called after the last call to ProcessWindow().
EffectNoiseReduction::Statistics Statistics
std::unique_ptr< Window > NewWindow(size_t windowSize) override
Allocates a window to place in the queue.
A two-pass effect to reduce background noise.
static const ComponentInterfaceSymbol Symbol
TranslatableString GetDescription() const override
ComponentInterfaceSymbol GetSymbol() const override
std::unique_ptr< Settings > mSettings
int ShowHostInterface(EffectPlugin &plugin, wxWindow &parent, const EffectDialogFactory &factory, std::shared_ptr< EffectInstance > &pInstance, EffectSettingsAccess &access, bool forceModal=false) override
bool Process(EffectInstance &instance, EffectSettings &settings) override
EffectType GetType() const override
Type determines how it behaves.
std::unique_ptr< Statistics > mStatistics
Factory of instances of an effect.
Definition: EffectPlugin.h:36
static int DoMessageBox(const EffectPlugin &plugin, const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={})
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
static void ShowHelp(wxWindow *parent, const FilePath &localFileName, const URLString &remoteURL, bool bModal=false, bool alwaysDefaultBrowser=false)
Definition: HelpSystem.cpp:233
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:625
A class that transforms a portion of a wave track (preserving duration) by applying Fourier transform...
std::vector< float > FloatVector
const unsigned mStepsPerWindow
Window & Nth(int n)
Access the queue, so you can inspect and modify any window in it.
std::shared_ptr< EffectInstance > MakeInstance() const override
Make an object maintaining short-term state of an Effect.
virtual bool TransferDataFromWindow(EffectSettings &settings)
A flat linked list of tracks supporting Add, Remove, Clear, and Contains, serialization of the list o...
Definition: Track.h:1339
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:1457
Subclass of SpectrumTransformer that rewrites a track.
bool Process(const WindowProcessor &processor, WaveTrack *track, size_t queueLength, sampleCount start, sampleCount len)
Invokes Start(), ProcessSamples(), and Finish()
bool DoFinish() override
Called after the last call to ProcessWindow().
bool DoStart() override
Called before any calls to ProcessWindow.
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
Definition: WaveTrack.h:51
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
void writePrefs(const StructureType *structure, const wxString &prefix, const PrefsTableEntry< StructureType, FieldType > *fields, size_t numFields)
void readPrefs(StructureType *structure, const wxString &prefix, const PrefsTableEntry< StructureType, FieldType > *fields, size_t numFields)
BuiltinEffectsModule::Registration< EffectNoiseReduction > reg
const struct anonymous_namespace{NoiseReduction.cpp}::DiscriminationMethodInfo discriminationMethodInfo[DM_N_METHODS]
const struct anonymous_namespace{NoiseReduction.cpp}::WindowTypesInfo windowTypesInfo[WT_N_WINDOW_TYPES]
constexpr fastfloat_really_inline int32_t power(int32_t q) noexcept
Definition: fast_float.h:1454
STL namespace.
Externalized state of a plug-in.
doubleEffectNoiseReduction::Settings::* MemberPointer
ControlInfo(MemberPointer f, double vMin, double vMax, long sMax, const wxChar *fmt, bool fAsInt, const TranslatableString &caption, const TranslatableString &name)
FieldType defaultValue
MemberPointer field
const wxChar * name
FieldTypeStructureType::* MemberPointer