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