Audacity  2.2.2
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 *******************************************************************//****************************************************************/
39 #include "../Audacity.h"
40 #include "../Experimental.h"
41 #include "NoiseReduction.h"
42 #include "EffectManager.h"
43 
44 #include "../ShuttleGui.h"
45 #include "../Prefs.h"
46 
47 #include "../WaveTrack.h"
48 #include "../widgets/ErrorDialog.h"
49 
50 #include <algorithm>
51 #include <vector>
52 #include <math.h>
53 
54 #if defined(__WXMSW__) && !defined(__CYGWIN__)
55 #include <float.h>
56 #define finite(x) _finite(x)
57 #endif
58 
59 #include <wx/button.h>
60 #include <wx/choice.h>
61 #include <wx/dialog.h>
62 #include <wx/radiobut.h>
63 #include <wx/slider.h>
64 #include <wx/valtext.h>
65 #include <wx/textctrl.h>
66 #include <wx/sizer.h>
67 
68 // SPECTRAL_SELECTION not to affect this effect for now, as there might be no indication that it does.
69 // [Discussed and agreed for v2.1 by Steve, Paul, Bill].
70 #undef EXPERIMENTAL_SPECTRAL_EDITING
71 
72 typedef std::vector<float> FloatVector;
73 
74 // Define both of these to make the radio button three-way
75 #define RESIDUE_CHOICE
76 //#define ISOLATE_CHOICE
77 
78 // Define for Attack and release controls.
79 // #define ATTACK_AND_RELEASE
80 
81 // Define to expose other advanced, experimental dialog controls
82 //#define ADVANCED_SETTINGS
83 
84 // Define to make the old statistical methods an available choice
85 //#define OLD_METHOD_AVAILABLE
86 
87 namespace {
88 
90  DM_MEDIAN,
91  DM_SECOND_GREATEST,
92  DM_OLD_METHOD,
93 
94  DM_N_METHODS,
95  DM_DEFAULT_METHOD = DM_SECOND_GREATEST,
96 };
97 
98 const struct DiscriminationMethodInfo {
99  const wxChar *name;
100 } discriminationMethodInfo[DM_N_METHODS] = {
101  // Experimental only, don't need translations
102  { wxT("Median") },
103  { wxT("Second greatest") },
104  { wxT("Old") },
105 };
106 
107 // magic number used only in the old statistics
108 // and the old discrimination
109 const float minSignalTime = 0.05f;
110 
112  WT_RECTANGULAR_HANN = 0, // 2.0.6 behavior, requires 1/2 step
113  WT_HANN_RECTANGULAR, // requires 1/2 step
114  WT_HANN_HANN, // requires 1/4 step
115  WT_BLACKMAN_HANN, // requires 1/4 step
116  WT_HAMMING_RECTANGULAR, // requires 1/2 step
117  WT_HAMMING_HANN, // requires 1/4 step
118  WT_HAMMING_INV_HAMMING, // requires 1/2 step
119 
120  WT_N_WINDOW_TYPES,
121  WT_DEFAULT_WINDOW_TYPES = WT_HANN_HANN
122 };
123 
124 const struct WindowTypesInfo {
125  const wxChar *name;
126  unsigned minSteps;
127  double inCoefficients[3];
128  double outCoefficients[3];
129  double productConstantTerm;
130 } windowTypesInfo [WT_N_WINDOW_TYPES] = {
131  // In all of these cases (but the last), the constant term of the product of windows
132  // is the product of the windows' two constant terms,
133  // plus one half the product of the first cosine coefficients.
134 
135  // Experimental only, don't need translations
136  { wxT("none, Hann (2.0.6 behavior)"), 2, { 1, 0, 0 }, { 0.5, -0.5, 0 }, 0.5 },
137  { wxT("Hann, none"), 2, { 0.5, -0.5, 0 }, { 1, 0, 0 }, 0.5 },
138  { wxT("Hann, Hann (default)"), 4, { 0.5, -0.5, 0 }, { 0.5, -0.5, 0 }, 0.375 },
139  { wxT("Blackman, Hann"), 4, { 0.42, -0.5, 0.08 }, { 0.5, -0.5, 0 }, 0.335 },
140  { wxT("Hamming, none"), 2, { 0.54, -0.46, 0.0 }, { 1, 0, 0 }, 0.54 },
141  { wxT("Hamming, Hann"), 4, { 0.54, -0.46, 0.0 }, { 0.5, -0.5, 0 }, 0.385 },
142  { wxT("Hamming, Reciprocal Hamming"), 2, { 0.54, -0.46, 0.0 }, { 1, 0, 0 }, 1.0 }, // output window is special
143 };
144 
145 enum {
146  DEFAULT_WINDOW_SIZE_CHOICE = 8, // corresponds to 2048
147  DEFAULT_STEPS_PER_WINDOW_CHOICE = 1 // corresponds to 4, minimum for WT_HANN_HANN
148 };
149 
151  NRC_REDUCE_NOISE,
152  NRC_ISOLATE_NOISE,
153  NRC_LEAVE_RESIDUE,
154 };
155 
156 } // namespace
157 
158 //----------------------------------------------------------------------------
159 // EffectNoiseReduction::Statistics
160 //----------------------------------------------------------------------------
161 
163 {
164 public:
165  Statistics(size_t spectrumSize, double rate, int windowTypes)
166  : mRate(rate)
167  , mWindowSize((spectrumSize - 1) * 2)
168  , mWindowTypes(windowTypes)
169  , mTotalWindows(0)
170  , mTrackWindows(0)
171  , mSums(spectrumSize)
172  , mMeans(spectrumSize)
173 #ifdef OLD_METHOD_AVAILABLE
174  , mNoiseThreshold(spectrumSize)
175 #endif
176  {}
177 
178  // Noise profile statistics follow
179 
180  double mRate; // Rate of profile track(s) -- processed tracks must match
181  size_t mWindowSize;
183 
188 
189 #ifdef OLD_METHOD_AVAILABLE
190  // Old statistics:
191  FloatVector mNoiseThreshold;
192 #endif
193 };
194 
195 //----------------------------------------------------------------------------
196 // EffectNoiseReduction::Settings
197 //----------------------------------------------------------------------------
198 
199 // This object is the memory of the effect between uses
200 // (other than noise profile statistics)
202 {
203 public:
204  Settings();
206 
207  bool PromptUser(EffectNoiseReduction *effect,
208  wxWindow *parent, bool bHasProfile, bool bAllowTwiddleSettings);
209  bool PrefsIO(bool read);
210  bool Validate(EffectNoiseReduction *effect) const;
211 
212  size_t WindowSize() const { return 1u << (3 + mWindowSizeChoice); }
213  unsigned StepsPerWindow() const { return 1u << (1 + mStepsPerWindowChoice); }
214 
216 
217  // Stored in preferences:
218 
219  // Basic:
220  double mNewSensitivity; // - log10 of a probability... yeah.
221  double mFreqSmoothingBands; // really an integer
222  double mNoiseGain; // in dB, positive
223  double mAttackTime; // in secs
224  double mReleaseTime; // in secs
225 
226  // Advanced:
227  double mOldSensitivity; // in dB, plus or minus
228 
229  // Basic:
231 
232  // Advanced:
236  int mMethod;
237 };
238 
240  : mDoProfile(true)
241 {
242  PrefsIO(true);
243 }
244 
245 //----------------------------------------------------------------------------
246 // EffectNoiseReduction::Worker
247 //----------------------------------------------------------------------------
248 
249 // This object holds information needed only during effect calculation
251 {
252 public:
255 
256  Worker(const Settings &settings, double sampleRate
258  , double f0, double f1
259 #endif
260  );
261  ~Worker();
262 
263  bool Process(EffectNoiseReduction &effect,
264  Statistics &statistics, TrackFactory &factory,
265  SelectedTrackListOfKindIterator &iter, double mT0, double mT1);
266 
267 private:
268  bool ProcessOne(EffectNoiseReduction &effect,
269  Statistics &statistics,
270  TrackFactory &factory,
271  int count, WaveTrack *track,
272  sampleCount start, sampleCount len);
273 
274  void StartNewTrack();
275  void ProcessSamples(Statistics &statistics,
276  WaveTrack *outputTrack, size_t len, float *buffer);
277  void FillFirstHistoryWindow();
278  void ApplyFreqSmoothing(FloatVector &gains);
279  void GatherStatistics(Statistics &statistics);
280  inline bool Classify(const Statistics &statistics, int band);
281  void ReduceNoise(const Statistics &statistics, WaveTrack *outputTrack);
282  void RotateHistoryWindows();
283  void FinishTrackStatistics(Statistics &statistics);
284  void FinishTrack(Statistics &statistics, WaveTrack *outputTrack);
285 
286 private:
287 
288  const bool mDoProfile;
289 
290  const double mSampleRate;
291 
292  const size_t mWindowSize;
293  // These have that size:
298  // These have that size, or 0:
301 
302  const size_t mSpectrumSize;
304  const size_t mFreqSmoothingBins;
305  // When spectral selection limits the affected band:
306  int mBinLow; // inclusive lower bound
307  int mBinHigh; // exclusive upper bound
308 
310  const unsigned mStepsPerWindow;
311  const size_t mStepSize;
312  const int mMethod;
313  const double mNewSensitivity;
314 
315 
316  sampleCount mInSampleCount;
317  sampleCount mOutStepCount;
319 
324 
326  unsigned mCenter;
327  unsigned mHistoryLen;
328 
329  struct Record
330  {
331  Record(size_t spectrumSize)
332  : mSpectrums(spectrumSize)
333  , mGains(spectrumSize)
334  , mRealFFTs(spectrumSize - 1)
335  , mImagFFTs(spectrumSize - 1)
336  {
337  }
338 
343  };
344  std::vector<movable_ptr<Record>> mQueue;
345 };
346 
347 /****************************************************************//*****************************************************************/
353 
354 //----------------------------------------------------------------------------
355 // EffectNoiseReduction::Dialog
356 //----------------------------------------------------------------------------
357 
359 {
360 public:
361  // constructors and destructors
362  Dialog
363  (EffectNoiseReduction *effect,
364  Settings *settings,
365  wxWindow *parent, bool bHasProfile,
366  bool bAllowTwiddleSettings);
367 
368  void PopulateOrExchange(ShuttleGui & S);
369  bool TransferDataToWindow() override;
370  bool TransferDataFromWindow() override;
371 
372  const Settings &GetTempSettings() const
373  { return mTempSettings; }
374 
375 private:
377 
378 #ifdef ADVANCED_SETTINGS
379  void EnableDisableSensitivityControls();
380 #endif
381 
382  // handlers
383  void OnGetProfile( wxCommandEvent &event );
384  void OnNoiseReductionChoice( wxCommandEvent &event );
385 #ifdef ADVANCED_SETTINGS
386  void OnMethodChoice(wxCommandEvent &);
387 #endif
388  void OnPreview(wxCommandEvent &event);
389  void OnReduceNoise( wxCommandEvent &event );
390  void OnCancel( wxCommandEvent &event );
391 
392  void OnText(wxCommandEvent &event);
393  void OnSlider(wxCommandEvent &event);
394 
395  // data members
396 
400 
403 
404 
405  wxRadioButton *mKeepSignal;
406 #ifdef ISOLATE_CHOICE
407  wxRadioButton *mKeepNoise;
408 #endif
409 #ifdef RESIDUE_CHOICE
410  wxRadioButton *mResidue;
411 #endif
412 
413 private:
414  DECLARE_EVENT_TABLE()
415 };
416 
418 : mSettings(std::make_unique<EffectNoiseReduction::Settings>())
419 {
420  Init();
421 }
422 
424 {
425 }
426 
427 // IdentInterface implementation
428 
430 {
432 }
433 
435 {
436  return _("Removes background noise such as fans, tape noise, or hums");
437 }
438 
439 // EffectIdentInterface implementation
440 
442 {
443  return EffectTypeProcess;
444 }
445 
447 {
448  return true;
449 }
450 
452 {
453  return false;
454 }
455 
456 bool EffectNoiseReduction::PromptUser(wxWindow *parent)
457 {
458  // We may want to twiddle the levels if we are setting
459  // from an automation dialog, the only case in which we can
460  // get here without any wavetracks.
461  return mSettings->PromptUser(this, parent,
462  bool(mStatistics), (GetNumWaveTracks() == 0));
463 }
464 
466 (EffectNoiseReduction *effect, wxWindow *parent,
467  bool bHasProfile, bool bAllowTwiddleSettings)
468 {
470  (effect, this, parent, bHasProfile, bAllowTwiddleSettings);
471 
472  dlog.CentreOnParent();
473  dlog.ShowModal();
474 
475  if (dlog.GetReturnCode() == 0)
476  return false;
477 
478  *this = dlog.GetTempSettings();
479  mDoProfile = (dlog.GetReturnCode() == 1);
480 
481  return PrefsIO(false);
482 }
483 
484 namespace {
485  template <typename StructureType, typename FieldType>
486  struct PrefsTableEntry {
487  typedef FieldType (StructureType::*MemberPointer);
488 
489  MemberPointer field;
490  const wxChar *name;
491  FieldType defaultValue;
492  };
493 
494  template <typename StructureType, typename FieldType>
495  void readPrefs(
496  StructureType *structure, const wxString &prefix,
497  const PrefsTableEntry<StructureType, FieldType> *fields, int numFields)
498  {
499  for (int ii = 0; ii < numFields; ++ii) {
500  const PrefsTableEntry<StructureType, FieldType> &entry = fields[ii];
501  gPrefs->Read(prefix + entry.name, &(structure->*(entry.field)),
502  entry.defaultValue);
503  }
504  }
505 
506  template <typename StructureType, typename FieldType>
507  void writePrefs(
508  StructureType *structure, const wxString &prefix,
509  const PrefsTableEntry<StructureType, FieldType> *fields, int numFields)
510  {
511  for (int ii = 0; ii < numFields; ++ii) {
512  const PrefsTableEntry<StructureType, FieldType> &entry = fields[ii];
513  gPrefs->Write(prefix + entry.name, structure->*(entry.field));
514  }
515  }
516 }
517 
519 {
520  static const double DEFAULT_OLD_SENSITIVITY = 0.0;
521 
522  static const PrefsTableEntry<Settings, double> doubleTable[] = {
523  { &Settings::mNewSensitivity, wxT("Sensitivity"), 6.0 },
524  { &Settings::mNoiseGain, wxT("Gain"), 12.0 },
525  { &Settings::mAttackTime, wxT("AttackTime"), 0.02 },
526  { &Settings::mReleaseTime, wxT("ReleaseTime"), 0.10 },
527  { &Settings::mFreqSmoothingBands, wxT("FreqSmoothing"), 3.0 },
528 
529  // Advanced settings
530  { &Settings::mOldSensitivity, wxT("OldSensitivity"), DEFAULT_OLD_SENSITIVITY },
531  };
532  static int doubleTableSize = sizeof(doubleTable) / sizeof(doubleTable[0]);
533 
534  static const PrefsTableEntry<Settings, int> intTable[] = {
535  { &Settings::mNoiseReductionChoice, wxT("ReductionChoice"), NRC_REDUCE_NOISE },
536 
537  // Advanced settings
538  { &Settings::mWindowTypes, wxT("WindowTypes"), WT_DEFAULT_WINDOW_TYPES },
539  { &Settings::mWindowSizeChoice, wxT("WindowSize"), DEFAULT_WINDOW_SIZE_CHOICE },
540  { &Settings::mStepsPerWindowChoice, wxT("StepsPerWindow"), DEFAULT_STEPS_PER_WINDOW_CHOICE },
541  { &Settings::mMethod, wxT("Method"), DM_DEFAULT_METHOD },
542  };
543  static int intTableSize = sizeof(intTable) / sizeof(intTable[0]);
544 
545  static const wxString prefix(wxT("/Effects/NoiseReduction/"));
546 
547  if (read) {
548  readPrefs(this, prefix, doubleTable, doubleTableSize);
549  readPrefs(this, prefix, intTable, intTableSize);
550 
551  // Ignore preferences for unavailable options.
552 #ifndef RESIDUE_CHOICE
553  if (mNoiseReductionChoice == NRC_LEAVE_RESIDUE)
554  mNoiseReductionChoice = NRC_ISOLATE_NOISE;
555 #endif
556 
557 #ifndef ADVANCED_SETTINGS
558  // Initialize all hidden advanced settings to defaults.
559  mWindowTypes = WT_DEFAULT_WINDOW_TYPES;
560  mWindowSizeChoice = DEFAULT_WINDOW_SIZE_CHOICE;
561  mStepsPerWindowChoice = DEFAULT_STEPS_PER_WINDOW_CHOICE;
562  mMethod = DM_DEFAULT_METHOD;
563  mOldSensitivity = DEFAULT_OLD_SENSITIVITY;
564 #endif
565 
566 #ifndef OLD_METHOD_AVAILABLE
567  if (mMethod == DM_OLD_METHOD)
568  mMethod = DM_DEFAULT_METHOD;
569 #endif
570 
571  return true;
572  }
573  else {
574  writePrefs(this, prefix, doubleTable, doubleTableSize);
575  writePrefs(this, prefix, intTable, intTableSize);
576  return gPrefs->Flush();
577  }
578 }
579 
581 {
582  if (StepsPerWindow() < windowTypesInfo[mWindowTypes].minSteps) {
583  effect->Effect::MessageBox(_("Steps per block are too few for the window types."));
584  return false;
585  }
586 
587  if (StepsPerWindow() > WindowSize()) {
588  effect->Effect::MessageBox(_("Steps per block cannot exceed the window size."));
589  return false;
590  }
591 
592  if (mMethod == DM_MEDIAN && StepsPerWindow() > 4) {
593  effect->Effect::MessageBox(_("Median method is not implemented for more than four steps per window."));
594  return false;
595  }
596 
597  return true;
598 }
599 
601 {
602  // This same code will either reduce noise or profile it
603 
604  this->CopyInputTracks(); // Set up mOutputTracks.
605 
607  WaveTrack *track = (WaveTrack *) iter.First();
608  if (!track)
609  return false;
610 
611  // Initialize statistics if gathering them, or check for mismatched (advanced)
612  // settings if reducing noise.
613  if (mSettings->mDoProfile) {
614  size_t spectrumSize = 1 + mSettings->WindowSize() / 2;
615  mStatistics = std::make_unique<Statistics>
616  (spectrumSize, track->GetRate(), mSettings->mWindowTypes);
617  }
618  else if (mStatistics->mWindowSize != mSettings->WindowSize()) {
619  // possible only with advanced settings
620  ::Effect::MessageBox(_("You must specify the same window size for steps 1 and 2."));
621  return false;
622  }
623  else if (mStatistics->mWindowTypes != mSettings->mWindowTypes) {
624  // A warning only
625  ::Effect::MessageBox(_("Warning: window types are not the same as for profiling."));
626  }
627 
628  Worker worker(*mSettings, mStatistics->mRate
630  , mF0, mF1
631 #endif
632  );
633  bool bGoodResult = worker.Process(*this, *mStatistics, *mFactory, iter, mT0, mT1);
634  if (mSettings->mDoProfile) {
635  if (bGoodResult)
636  mSettings->mDoProfile = false; // So that "repeat last effect" will reduce noise
637  else
638  mStatistics.reset(); // So that profiling must be done again before noise reduction
639  }
640  this->ReplaceProcessedTracks(bGoodResult);
641  return bGoodResult;
642 }
643 
645 {
646 }
647 
649 (EffectNoiseReduction &effect, Statistics &statistics, TrackFactory &factory,
650  SelectedTrackListOfKindIterator &iter, double mT0, double mT1)
651 {
652  int count = 0;
653  WaveTrack *track = (WaveTrack *) iter.First();
654  while (track) {
655  if (track->GetRate() != mSampleRate) {
656  if (mDoProfile)
657  effect.Effect::MessageBox(_("All noise profile data must have the same sample rate."));
658  else
659  effect.Effect::MessageBox(_("The sample rate of the noise profile must match that of the sound to be processed."));
660  return false;
661  }
662 
663  double trackStart = track->GetStartTime();
664  double trackEnd = track->GetEndTime();
665  double t0 = std::max(trackStart, mT0);
666  double t1 = std::min(trackEnd, mT1);
667 
668  if (t1 > t0) {
669  auto start = track->TimeToLongSamples(t0);
670  auto end = track->TimeToLongSamples(t1);
671  auto len = end - start;
672 
673  if (!ProcessOne(effect, statistics, factory,
674  count, track, start, len))
675  return false;
676  }
677  track = (WaveTrack *) iter.Next();
678  ++count;
679  }
680 
681  if (mDoProfile) {
682  if (statistics.mTotalWindows == 0) {
683  effect.Effect::MessageBox(_("Selected noise profile is too short."));
684  return false;
685  }
686  }
687 
688  return true;
689 }
690 
692 {
693  // Given an array of gain mutipliers, average them
694  // GEOMETRICALLY. Don't multiply and take nth root --
695  // that may quickly cause underflows. Instead, average the logs.
696 
697  if (mFreqSmoothingBins == 0)
698  return;
699 
700  {
701  float *pScratch = &mFreqSmoothingScratch[0];
702  std::fill(pScratch, pScratch + mSpectrumSize, 0.0f);
703  }
704 
705  for (size_t ii = 0; ii < mSpectrumSize; ++ii)
706  gains[ii] = log(gains[ii]);
707 
708  // ii must be signed
709  for (int ii = 0; ii < (int)mSpectrumSize; ++ii) {
710  const int j0 = std::max(0, ii - (int)mFreqSmoothingBins);
711  const int j1 = std::min(mSpectrumSize - 1, ii + mFreqSmoothingBins);
712  for(int jj = j0; jj <= j1; ++jj) {
713  mFreqSmoothingScratch[ii] += gains[jj];
714  }
715  mFreqSmoothingScratch[ii] /= (j1 - j0 + 1);
716  }
717 
718  for (size_t ii = 0; ii < mSpectrumSize; ++ii)
719  gains[ii] = exp(mFreqSmoothingScratch[ii]);
720 }
721 
723 (const Settings &settings, double sampleRate
725 , double f0, double f1
726 #endif
727 )
728 : mDoProfile(settings.mDoProfile)
729 
730 , mSampleRate(sampleRate)
731 
732 , mWindowSize(settings.WindowSize())
733 , hFFT(GetFFT(mWindowSize))
734 , mFFTBuffer(mWindowSize)
735 , mInWaveBuffer(mWindowSize)
736 , mOutOverlapBuffer(mWindowSize)
737 , mInWindow()
738 , mOutWindow()
739 
740 , mSpectrumSize(1 + mWindowSize / 2)
741 , mFreqSmoothingScratch(mSpectrumSize)
742 , mFreqSmoothingBins((int)(settings.mFreqSmoothingBands))
743 , mBinLow(0)
744 , mBinHigh(mSpectrumSize)
745 
746 , mNoiseReductionChoice(settings.mNoiseReductionChoice)
747 , mStepsPerWindow(settings.StepsPerWindow())
748 , mStepSize(mWindowSize / mStepsPerWindow)
749 , mMethod(settings.mMethod)
750 
751 // Sensitivity setting is a base 10 log, turn it into a natural log
752 , mNewSensitivity(settings.mNewSensitivity * log(10.0))
753 
754 , mInSampleCount(0)
755 , mOutStepCount(0)
756 , mInWavePos(0)
757 {
758 #ifdef EXPERIMENTAL_SPECTRAL_EDITING
759  {
760  const double bin = mSampleRate / mWindowSize;
761  if (f0 >= 0.0 )
762  mBinLow = floor(f0 / bin);
763  if (f1 >= 0.0)
764  mBinHigh = ceil(f1 / bin);
765  }
766 #endif
767 
768  const double noiseGain = -settings.mNoiseGain;
769  const unsigned nAttackBlocks = 1 + (int)(settings.mAttackTime * sampleRate / mStepSize);
770  const unsigned nReleaseBlocks = 1 + (int)(settings.mReleaseTime * sampleRate / mStepSize);
771  // Applies to amplitudes, divide by 20:
772  mNoiseAttenFactor = DB_TO_LINEAR(noiseGain);
773  // Apply to gain factors which apply to amplitudes, divide by 20:
774  mOneBlockAttack = DB_TO_LINEAR(noiseGain / nAttackBlocks);
775  mOneBlockRelease = DB_TO_LINEAR(noiseGain / nReleaseBlocks);
776  // Applies to power, divide by 10:
777  mOldSensitivityFactor = pow(10.0, settings.mOldSensitivity / 10.0);
778 
779  mNWindowsToExamine = (mMethod == DM_OLD_METHOD)
780  ? std::max(2, (int)(minSignalTime * sampleRate / mStepSize))
781  : 1 + mStepsPerWindow;
782 
783  mCenter = mNWindowsToExamine / 2;
784  wxASSERT(mCenter >= 1); // release depends on this assumption
785 
786  if (mDoProfile)
787 #ifdef OLD_METHOD_AVAILABLE
788  mHistoryLen = mNWindowsToExamine;
789 #else
790  mHistoryLen = 1;
791 #endif
792  else {
793  // Allow long enough queue for sufficient inspection of the middle
794  // and for attack processing
795  // See ReduceNoise()
796  mHistoryLen = std::max(mNWindowsToExamine, mCenter + nAttackBlocks);
797  }
798 
799  mQueue.resize(mHistoryLen);
800  for (unsigned ii = 0; ii < mHistoryLen; ++ii)
801  mQueue[ii] = make_movable<Record>(mSpectrumSize);
802 
803  // Create windows
804 
805  const double constantTerm =
806  windowTypesInfo[settings.mWindowTypes].productConstantTerm;
807 
808  // One or the other window must by multiplied by this to correct for
809  // overlap. Must scale down as steps get smaller, and overlaps larger.
810  const double multiplier = 1.0 / (constantTerm * mStepsPerWindow);
811 
812  // Create the analysis window
813  switch (settings.mWindowTypes) {
814  case WT_RECTANGULAR_HANN:
815  break;
816  default:
817  {
818  const bool rectangularOut =
819  settings.mWindowTypes == WT_HAMMING_RECTANGULAR ||
820  settings.mWindowTypes == WT_HANN_RECTANGULAR;
821  const double m =
822  rectangularOut ? multiplier : 1;
823  const double *const coefficients =
824  windowTypesInfo[settings.mWindowTypes].inCoefficients;
825  const double c0 = coefficients[0];
826  const double c1 = coefficients[1];
827  const double c2 = coefficients[2];
828  mInWindow.resize(mWindowSize);
829  for (size_t ii = 0; ii < mWindowSize; ++ii)
830  mInWindow[ii] = m *
831  (c0 + c1 * cos((2.0*M_PI*ii) / mWindowSize)
832  + c2 * cos((4.0*M_PI*ii) / mWindowSize));
833  }
834  break;
835  }
836 
837  if (!mDoProfile) {
838  // Create the synthesis window
839  switch (settings.mWindowTypes) {
840  case WT_HANN_RECTANGULAR:
841  case WT_HAMMING_RECTANGULAR:
842  break;
843  case WT_HAMMING_INV_HAMMING:
844  {
845  mOutWindow.resize(mWindowSize);
846  for (size_t ii = 0; ii < mWindowSize; ++ii)
847  mOutWindow[ii] = multiplier / mInWindow[ii];
848  }
849  break;
850  default:
851  {
852  const double *const coefficients =
853  windowTypesInfo[settings.mWindowTypes].outCoefficients;
854  const double c0 = coefficients[0];
855  const double c1 = coefficients[1];
856  const double c2 = coefficients[2];
857  mOutWindow.resize(mWindowSize);
858  for (size_t ii = 0; ii < mWindowSize; ++ii)
859  mOutWindow[ii] = multiplier *
860  (c0 + c1 * cos((2.0 * M_PI * ii) / mWindowSize)
861  + c2 * cos((4.0 * M_PI * ii) / mWindowSize));
862  }
863  break;
864  }
865  }
866 }
867 
869 {
870  float *pFill;
871  for(unsigned ii = 0; ii < mHistoryLen; ++ii) {
872  Record &record = *mQueue[ii];
873 
874  pFill = &record.mSpectrums[0];
875  std::fill(pFill, pFill + mSpectrumSize, 0.0f);
876 
877  pFill = &record.mRealFFTs[0];
878  std::fill(pFill, pFill + mSpectrumSize - 1, 0.0f);
879 
880  pFill = &record.mImagFFTs[0];
881  std::fill(pFill, pFill + mSpectrumSize - 1, 0.0f);
882 
883  pFill = &record.mGains[0];
884  std::fill(pFill, pFill + mSpectrumSize, mNoiseAttenFactor);
885  }
886 
887  pFill = &mOutOverlapBuffer[0];
888  std::fill(pFill, pFill + mWindowSize, 0.0f);
889 
890  pFill = &mInWaveBuffer[0];
891  std::fill(pFill, pFill + mWindowSize, 0.0f);
892 
893  if (mDoProfile)
894  {
895  // We do not want leading zero padded windows
896  mInWavePos = 0;
897  mOutStepCount = -(int)(mHistoryLen - 1);
898  }
899  else
900  {
901  // So that the queue gets primed with some windows,
902  // zero-padded in front, the first having mStepSize
903  // samples of wave data:
904  mInWavePos = mWindowSize - mStepSize;
905  // This starts negative, to count up until the queue fills:
906  mOutStepCount = -(int)(mHistoryLen - 1)
907  // ... and then must pass over the padded windows,
908  // before the first full window:
909  - (int)(mStepsPerWindow - 1);
910  }
911 
912  mInSampleCount = 0;
913 }
914 
916 (Statistics &statistics, WaveTrack *outputTrack,
917  size_t len, float *buffer)
918 {
919  while (len && mOutStepCount * mStepSize < mInSampleCount) {
920  auto avail = std::min(len, mWindowSize - mInWavePos);
921  memmove(&mInWaveBuffer[mInWavePos], buffer, avail * sizeof(float));
922  buffer += avail;
923  len -= avail;
924  mInWavePos += avail;
925 
926  if (mInWavePos == (int)mWindowSize) {
927  FillFirstHistoryWindow();
928  if (mDoProfile)
929  GatherStatistics(statistics);
930  else
931  ReduceNoise(statistics, outputTrack);
932  ++mOutStepCount;
933  RotateHistoryWindows();
934 
935  // Rotate for overlap-add
936  memmove(&mInWaveBuffer[0], &mInWaveBuffer[mStepSize],
937  (mWindowSize - mStepSize) * sizeof(float));
938  mInWavePos -= mStepSize;
939  }
940  }
941 }
942 
944 {
945  // Transform samples to frequency domain, windowed as needed
946  if (mInWindow.size() > 0)
947  for (size_t ii = 0; ii < mWindowSize; ++ii)
948  mFFTBuffer[ii] = mInWaveBuffer[ii] * mInWindow[ii];
949  else
950  memmove(&mFFTBuffer[0], &mInWaveBuffer[0], mWindowSize * sizeof(float));
951  RealFFTf(&mFFTBuffer[0], hFFT.get());
952 
953  Record &record = *mQueue[0];
954 
955  // Store real and imaginary parts for later inverse FFT, and compute
956  // power
957  {
958  float *pReal = &record.mRealFFTs[1];
959  float *pImag = &record.mImagFFTs[1];
960  float *pPower = &record.mSpectrums[1];
961  int *pBitReversed = &hFFT->BitReversed[1];
962  const auto last = mSpectrumSize - 1;
963  for (unsigned int ii = 1; ii < last; ++ii) {
964  const int kk = *pBitReversed++;
965  const float realPart = *pReal++ = mFFTBuffer[kk];
966  const float imagPart = *pImag++ = mFFTBuffer[kk + 1];
967  *pPower++ = realPart * realPart + imagPart * imagPart;
968  }
969  // DC and Fs/2 bins need to be handled specially
970  const float dc = mFFTBuffer[0];
971  record.mRealFFTs[0] = dc;
972  record.mSpectrums[0] = dc*dc;
973 
974  const float nyquist = mFFTBuffer[1];
975  record.mImagFFTs[0] = nyquist; // For Fs/2, not really imaginary
976  record.mSpectrums[last] = nyquist * nyquist;
977  }
978 
979  if (mNoiseReductionChoice != NRC_ISOLATE_NOISE)
980  {
981  // Default all gains to the reduction factor,
982  // until we decide to raise some of them later
983  float *pGain = &record.mGains[0];
984  std::fill(pGain, pGain + mSpectrumSize, mNoiseAttenFactor);
985  }
986 }
987 
989 {
990  std::rotate(mQueue.begin(), mQueue.end() - 1, mQueue.end());
991 }
992 
994 {
995  const int windows = statistics.mTrackWindows;
996  const int multiplier = statistics.mTotalWindows;
997  const int denom = windows + multiplier;
998 
999  // Combine averages in case of multiple profile tracks.
1000  if (windows)
1001  for (int ii = 0, nn = statistics.mMeans.size(); ii < nn; ++ii) {
1002  float &mean = statistics.mMeans[ii];
1003  float &sum = statistics.mSums[ii];
1004  mean = (mean * multiplier + sum) / denom;
1005  // Reset for next track
1006  sum = 0;
1007  }
1008 
1009  // Reset for next track
1010  statistics.mTrackWindows = 0;
1011  statistics.mTotalWindows = denom;
1012 }
1013 
1015 (Statistics &statistics, WaveTrack *outputTrack)
1016 {
1017  // Keep flushing empty input buffers through the history
1018  // windows until we've output exactly as many samples as
1019  // were input.
1020  // Well, not exactly, but not more than one step-size of extra samples
1021  // at the end.
1022  // We'll DELETE them later in ProcessOne.
1023 
1024  FloatVector empty(mStepSize);
1025 
1026  while (mOutStepCount * mStepSize < mInSampleCount) {
1027  ProcessSamples(statistics, outputTrack, mStepSize, &empty[0]);
1028  }
1029 }
1030 
1032 {
1033  ++statistics.mTrackWindows;
1034 
1035  {
1036  // NEW statistics
1037  const float *pPower = &mQueue[0]->mSpectrums[0];
1038  float *pSum = &statistics.mSums[0];
1039  for (size_t jj = 0; jj < mSpectrumSize; ++jj) {
1040  *pSum++ += *pPower++;
1041  }
1042  }
1043 
1044 #ifdef OLD_METHOD_AVAILABLE
1045  // The noise threshold for each frequency is the maximum
1046  // level achieved at that frequency for a minimum of
1047  // mMinSignalBlocks blocks in a row - the max of a min.
1048 
1049  auto finish = mHistoryLen;
1050 
1051  {
1052  // old statistics
1053  const float *pPower = &mQueue[0]->mSpectrums[0];
1054  float *pThreshold = &statistics.mNoiseThreshold[0];
1055  for (int jj = 0; jj < mSpectrumSize; ++jj) {
1056  float min = *pPower++;
1057  for (unsigned ii = 1; ii < finish; ++ii)
1058  min = std::min(min, mQueue[ii]->mSpectrums[jj]);
1059  *pThreshold = std::max(*pThreshold, min);
1060  ++pThreshold;
1061  }
1062  }
1063 #endif
1064 }
1065 
1066 // Return true iff the given band of the "center" window looks like noise.
1067 // Examine the band in a few neighboring windows to decide.
1068 inline
1069 bool EffectNoiseReduction::Worker::Classify(const Statistics &statistics, int band)
1070 {
1071  switch (mMethod) {
1072 #ifdef OLD_METHOD_AVAILABLE
1073  case DM_OLD_METHOD:
1074  {
1075  float min = mQueue[0]->mSpectrums[band];
1076  for (unsigned ii = 1; ii < mNWindowsToExamine; ++ii)
1077  min = std::min(min, mQueue[ii]->mSpectrums[band]);
1078  return min <= mOldSensitivityFactor * statistics.mNoiseThreshold[band];
1079  }
1080 #endif
1081  // New methods suppose an exponential distribution of power values
1082  // in the noise; NEW sensitivity is meant to be log of probability
1083  // that noise strays above the threshold. Call that probability
1084  // 1 - F. The quantile function of an exponential distribution is
1085  // log (1 - F) * mean. Thus simply multiply mean by sensitivity
1086  // to get the threshold.
1087  case DM_MEDIAN:
1088  // This method examines the window and all windows
1089  // that partly overlap it, and takes a median, to
1090  // avoid being fooled by up and down excursions into
1091  // either the mistake of classifying noise as not noise
1092  // (leaving a musical noise chime), or the opposite
1093  // (distorting the signal with a drop out).
1094  if (mNWindowsToExamine == 3)
1095  // No different from second greatest.
1096  goto secondGreatest;
1097  else if (mNWindowsToExamine == 5)
1098  {
1099  float greatest = 0.0, second = 0.0, third = 0.0;
1100  for (unsigned ii = 0; ii < mNWindowsToExamine; ++ii) {
1101  const float power = mQueue[ii]->mSpectrums[band];
1102  if (power >= greatest)
1103  third = second, second = greatest, greatest = power;
1104  else if (power >= second)
1105  third = second, second = power;
1106  else if (power >= third)
1107  third = power;
1108  }
1109  return third <= mNewSensitivity * statistics.mMeans[band];
1110  }
1111  else {
1112  wxASSERT(false);
1113  return true;
1114  }
1115  secondGreatest:
1116  case DM_SECOND_GREATEST:
1117  {
1118  // This method just throws out the high outlier. It
1119  // should be less prone to distortions and more prone to
1120  // chimes.
1121  float greatest = 0.0, second = 0.0;
1122  for (unsigned ii = 0; ii < mNWindowsToExamine; ++ii) {
1123  const float power = mQueue[ii]->mSpectrums[band];
1124  if (power >= greatest)
1125  second = greatest, greatest = power;
1126  else if (power >= second)
1127  second = power;
1128  }
1129  return second <= mNewSensitivity * statistics.mMeans[band];
1130  }
1131  default:
1132  wxASSERT(false);
1133  return true;
1134  }
1135 }
1136 
1138 (const Statistics &statistics, WaveTrack *outputTrack)
1139 {
1140  // Raise the gain for elements in the center of the sliding history
1141  // or, if isolating noise, zero out the non-noise
1142  {
1143  float *pGain = &mQueue[mCenter]->mGains[0];
1144  if (mNoiseReductionChoice == NRC_ISOLATE_NOISE) {
1145  // All above or below the selected frequency range is non-noise
1146  std::fill(pGain, pGain + mBinLow, 0.0f);
1147  std::fill(pGain + mBinHigh, pGain + mSpectrumSize, 0.0f);
1148  pGain += mBinLow;
1149  for (int jj = mBinLow; jj < mBinHigh; ++jj) {
1150  const bool isNoise = Classify(statistics, jj);
1151  *pGain++ = isNoise ? 1.0 : 0.0;
1152  }
1153  }
1154  else {
1155  // All above or below the selected frequency range is non-noise
1156  std::fill(pGain, pGain + mBinLow, 1.0f);
1157  std::fill(pGain + mBinHigh, pGain + mSpectrumSize, 1.0f);
1158  pGain += mBinLow;
1159  for (int jj = mBinLow; jj < mBinHigh; ++jj) {
1160  const bool isNoise = Classify(statistics, jj);
1161  if (!isNoise)
1162  *pGain = 1.0;
1163  ++pGain;
1164  }
1165  }
1166  }
1167 
1168  if (mNoiseReductionChoice != NRC_ISOLATE_NOISE)
1169  {
1170  // In each direction, define an exponential decay of gain from the
1171  // center; make actual gains the maximum of mNoiseAttenFactor, and
1172  // the decay curve, and their prior values.
1173 
1174  // First, the attack, which goes backward in time, which is,
1175  // toward higher indices in the queue.
1176  for (size_t jj = 0; jj < mSpectrumSize; ++jj) {
1177  for (unsigned ii = mCenter + 1; ii < mHistoryLen; ++ii) {
1178  const float minimum =
1179  std::max(mNoiseAttenFactor,
1180  mQueue[ii - 1]->mGains[jj] * mOneBlockAttack);
1181  float &gain = mQueue[ii]->mGains[jj];
1182  if (gain < minimum)
1183  gain = minimum;
1184  else
1185  // We can stop now, our attack curve is intersecting
1186  // the decay curve of some window previously processed.
1187  break;
1188  }
1189  }
1190 
1191  // Now, release. We need only look one window ahead. This part will
1192  // be visited again when we examine the next window, and
1193  // carry the decay further.
1194  {
1195  float *pNextGain = &mQueue[mCenter - 1]->mGains[0];
1196  const float *pThisGain = &mQueue[mCenter]->mGains[0];
1197  for (int nn = mSpectrumSize; nn--;) {
1198  *pNextGain =
1199  std::max(*pNextGain,
1200  std::max(mNoiseAttenFactor,
1201  *pThisGain++ * mOneBlockRelease));
1202  ++pNextGain;
1203  }
1204  }
1205  }
1206 
1207 
1208  if (mOutStepCount >= -(int)(mStepsPerWindow - 1)) {
1209  Record &record = *mQueue[mHistoryLen - 1]; // end of the queue
1210  const auto last = mSpectrumSize - 1;
1211 
1212  if (mNoiseReductionChoice != NRC_ISOLATE_NOISE)
1213  // Apply frequency smoothing to output gain
1214  // Gains are not less than mNoiseAttenFactor
1215  ApplyFreqSmoothing(record.mGains);
1216 
1217  // Apply gain to FFT
1218  {
1219  const float *pGain = &record.mGains[1];
1220  const float *pReal = &record.mRealFFTs[1];
1221  const float *pImag = &record.mImagFFTs[1];
1222  float *pBuffer = &mFFTBuffer[2];
1223  auto nn = mSpectrumSize - 2;
1224  if (mNoiseReductionChoice == NRC_LEAVE_RESIDUE) {
1225  for (; nn--;) {
1226  // Subtract the gain we would otherwise apply from 1, and
1227  // negate that to flip the phase.
1228  const double gain = *pGain++ - 1.0;
1229  *pBuffer++ = *pReal++ * gain;
1230  *pBuffer++ = *pImag++ * gain;
1231  }
1232  mFFTBuffer[0] = record.mRealFFTs[0] * (record.mGains[0] - 1.0);
1233  // The Fs/2 component is stored as the imaginary part of the DC component
1234  mFFTBuffer[1] = record.mImagFFTs[0] * (record.mGains[last] - 1.0);
1235  }
1236  else {
1237  for (; nn--;) {
1238  const double gain = *pGain++;
1239  *pBuffer++ = *pReal++ * gain;
1240  *pBuffer++ = *pImag++ * gain;
1241  }
1242  mFFTBuffer[0] = record.mRealFFTs[0] * record.mGains[0];
1243  // The Fs/2 component is stored as the imaginary part of the DC component
1244  mFFTBuffer[1] = record.mImagFFTs[0] * record.mGains[last];
1245  }
1246  }
1247 
1248  // Invert the FFT into the output buffer
1249  InverseRealFFTf(&mFFTBuffer[0], hFFT.get());
1250 
1251  // Overlap-add
1252  if (mOutWindow.size() > 0) {
1253  float *pOut = &mOutOverlapBuffer[0];
1254  float *pWindow = &mOutWindow[0];
1255  int *pBitReversed = &hFFT->BitReversed[0];
1256  for (unsigned int jj = 0; jj < last; ++jj) {
1257  int kk = *pBitReversed++;
1258  *pOut++ += mFFTBuffer[kk] * (*pWindow++);
1259  *pOut++ += mFFTBuffer[kk + 1] * (*pWindow++);
1260  }
1261  }
1262  else {
1263  float *pOut = &mOutOverlapBuffer[0];
1264  int *pBitReversed = &hFFT->BitReversed[0];
1265  for (unsigned int jj = 0; jj < last; ++jj) {
1266  int kk = *pBitReversed++;
1267  *pOut++ += mFFTBuffer[kk];
1268  *pOut++ += mFFTBuffer[kk + 1];
1269  }
1270  }
1271 
1272  float *buffer = &mOutOverlapBuffer[0];
1273  if (mOutStepCount >= 0) {
1274  // Output the first portion of the overlap buffer, they're done
1275  outputTrack->Append((samplePtr)buffer, floatSample, mStepSize);
1276  }
1277 
1278  // Shift the remainder over.
1279  memmove(buffer, buffer + mStepSize, sizeof(float) * (mWindowSize - mStepSize));
1280  std::fill(buffer + mWindowSize - mStepSize, buffer + mWindowSize, 0.0f);
1281  }
1282 }
1283 
1285 (EffectNoiseReduction &effect, Statistics &statistics, TrackFactory &factory,
1286  int count, WaveTrack * track, sampleCount start, sampleCount len)
1287 {
1288  if (track == NULL)
1289  return false;
1290 
1291  StartNewTrack();
1292 
1293  WaveTrack::Holder outputTrack;
1294  if(!mDoProfile)
1295  outputTrack = factory.NewWaveTrack(track->GetSampleFormat(), track->GetRate());
1296 
1297  auto bufferSize = track->GetMaxBlockSize();
1298  FloatVector buffer(bufferSize);
1299 
1300  bool bLoopSuccess = true;
1301  auto samplePos = start;
1302  while (bLoopSuccess && samplePos < start + len) {
1303  //Get a blockSize of samples (smaller than the size of the buffer)
1304  const auto blockSize = limitSampleBufferSize(
1305  track->GetBestBlockSize(samplePos),
1306  start + len - samplePos
1307  );
1308 
1309  //Get the samples from the track and put them in the buffer
1310  track->Get((samplePtr)&buffer[0], floatSample, samplePos, blockSize);
1311  samplePos += blockSize;
1312 
1313  mInSampleCount += blockSize;
1314  ProcessSamples(statistics, outputTrack.get(), blockSize, &buffer[0]);
1315 
1316  // Update the Progress meter, let user cancel
1317  bLoopSuccess =
1318  !effect.TrackProgress(count,
1319  ( samplePos - start ).as_double() /
1320  len.as_double() );
1321  }
1322 
1323  if (bLoopSuccess) {
1324  if (mDoProfile)
1325  FinishTrackStatistics(statistics);
1326  else
1327  FinishTrack(statistics, &*outputTrack);
1328  }
1329 
1330  if (bLoopSuccess && !mDoProfile) {
1331  // Flush the output WaveTrack (since it's buffered)
1332  outputTrack->Flush();
1333 
1334  // Take the output track and insert it in place of the original
1335  // sample data (as operated on -- this may not match mT0/mT1)
1336  double t0 = outputTrack->LongSamplesToTime(start);
1337  double tLen = outputTrack->LongSamplesToTime(len);
1338  // Filtering effects always end up with more data than they started with. Delete this 'tail'.
1339  outputTrack->HandleClear(tLen, outputTrack->GetEndTime(), false, false);
1340  track->ClearAndPaste(t0, t0 + tLen, &*outputTrack, true, false);
1341  }
1342 
1343  return bLoopSuccess;
1344 }
1345 
1346 //----------------------------------------------------------------------------
1347 // EffectNoiseReduction::Dialog
1348 //----------------------------------------------------------------------------
1349 
1350 enum {
1353 #ifdef ISOLATE_CHOICE
1354  ID_RADIOBUTTON_KEEPNOISE,
1355 #endif
1356 #ifdef RESIDUE_CHOICE
1358 #endif
1359 
1360 #ifdef ADVANCED_SETTINGS
1361  ID_CHOICE_METHOD,
1362 #endif
1363 
1364  // Slider/text pairs
1367 
1370 
1371 #ifdef ATTACK_AND_RELEASE
1372  ID_ATTACK_TIME_SLIDER,
1373  ID_ATTACK_TIME_TEXT,
1374 
1375  ID_RELEASE_TIME_SLIDER,
1376  ID_RELEASE_TIME_TEXT,
1377 #endif
1378 
1381 
1383 
1384 #ifdef ADVANCED_SETTINGS
1385  ID_OLD_SENSITIVITY_SLIDER = END_OF_BASIC_SLIDERS,
1386  ID_OLD_SENSITIVITY_TEXT,
1387 
1388  END_OF_ADVANCED_SLIDERS,
1389  END_OF_SLIDERS = END_OF_ADVANCED_SLIDERS,
1390 #else
1391  END_OF_SLIDERS = END_OF_BASIC_SLIDERS,
1392 #endif
1393 
1395 };
1396 
1397 namespace {
1398 
1399 struct ControlInfo {
1400  typedef double (EffectNoiseReduction::Settings::*MemberPointer);
1401 
1402  double Value(long sliderSetting) const
1403  {
1404  return
1405  valueMin +
1406  (double(sliderSetting) / sliderMax) * (valueMax - valueMin);
1407  }
1408 
1409  long SliderSetting(double value) const
1410  {
1411  return TrapLong(
1412  0.5 + sliderMax * (value - valueMin) / (valueMax - valueMin),
1413  0, sliderMax);
1414  }
1415 
1416  wxString Text(double value) const
1417  {
1418  if (formatAsInt)
1419  return wxString::Format(format, (int)(value));
1420  else
1421  return wxString::Format(format, value);
1422  }
1423 
1424  void CreateControls(int id, wxTextValidator &vld, ShuttleGui &S) const
1425  {
1426  wxTextCtrl *const text =
1427  S.Id(id + 1).AddTextBox(textBoxCaption(), wxT(""), 0);
1428  S.SetStyle(wxSL_HORIZONTAL);
1429  text->SetValidator(vld);
1430 
1431  wxSlider *const slider =
1432  S.Id(id).AddSlider( {}, 0, sliderMax);
1433  slider->SetName(sliderName());
1434  slider->SetRange(0, sliderMax);
1435  slider->SetSizeHints(150, -1);
1436  }
1437 
1438  MemberPointer field;
1439  double valueMin;
1440  double valueMax;
1441  long sliderMax;
1442  // (valueMin - valueMax) / sliderMax is the value increment of the slider
1443  const wxChar* format;
1444  bool formatAsInt;
1445  const wxString textBoxCaption_; wxString textBoxCaption() const { return wxGetTranslation(textBoxCaption_); }
1446  const wxString sliderName_; wxString sliderName() const { return wxGetTranslation(sliderName_); }
1447 
1448  ControlInfo(MemberPointer f, double vMin, double vMax, long sMax, const wxChar* fmt, bool fAsInt,
1449  const wxString &caption, const wxString &name)
1450  : field(f), valueMin(vMin), valueMax(vMax), sliderMax(sMax), format(fmt), formatAsInt(fAsInt)
1451  , textBoxCaption_(caption), sliderName_(name)
1452  {
1453  }
1454 };
1455 
1456 const ControlInfo *controlInfo() {
1457  static const ControlInfo table[] = {
1459  0.0, 48.0, 48, wxT("%d"), true,
1460  XO("&Noise reduction (dB):"), XO("Noise reduction")),
1462  0.0, 24.0, 48, wxT("%.2f"), false,
1463  XO("&Sensitivity:"), XO("Sensitivity")),
1464 #ifdef ATTACK_AND_RELEASE
1466  0, 1.0, 100, wxT("%.2f"), false,
1467  XO("Attac&k time (secs):"), XO("Attack time")),
1469  0, 1.0, 100, wxT("%.2f"), false,
1470  XO("R&elease time (secs):"), XO("Release time")),
1471 #endif
1473  0, 12, 12, wxT("%d"), true,
1474  XO("&Frequency smoothing (bands):"), XO("Frequency smoothing")),
1475 
1476 #ifdef ADVANCED_SETTINGS
1478  -20.0, 20.0, 4000, wxT("%.2f"), false,
1479  XO("Sensiti&vity (dB):"), XO("Old Sensitivity")),
1480  // add here
1481 #endif
1482  };
1483 
1484 return table;
1485 }
1486 
1487 } // namespace
1488 
1489 
1491  EVT_BUTTON(wxID_OK, EffectNoiseReduction::Dialog::OnReduceNoise)
1492  EVT_BUTTON(wxID_CANCEL, EffectNoiseReduction::Dialog::OnCancel)
1495 
1496  EVT_RADIOBUTTON(ID_RADIOBUTTON_KEEPSIGNAL, EffectNoiseReduction::Dialog::OnNoiseReductionChoice)
1497 #ifdef ISOLATE_CHOICE
1498  EVT_RADIOBUTTON(ID_RADIOBUTTON_KEEPNOISE, EffectNoiseReduction::Dialog::OnNoiseReductionChoice)
1499 #endif
1500 #ifdef RESIDUE_CHOICE
1502 #endif
1503 
1504 #ifdef ADVANCED_SETTINGS
1505  EVT_CHOICE(ID_CHOICE_METHOD, EffectNoiseReduction::Dialog::OnMethodChoice)
1506 #endif
1507 
1509  EVT_TEXT(ID_GAIN_TEXT, EffectNoiseReduction::Dialog::OnText)
1510 
1511  EVT_SLIDER(ID_NEW_SENSITIVITY_SLIDER, EffectNoiseReduction::Dialog::OnSlider)
1513 
1514  EVT_SLIDER(ID_FREQ_SLIDER, EffectNoiseReduction::Dialog::OnSlider)
1515  EVT_TEXT(ID_FREQ_TEXT, EffectNoiseReduction::Dialog::OnText)
1516 
1517 #ifdef ATTACK_AND_RELEASE
1518  EVT_SLIDER(ID_ATTACK_TIME_SLIDER, EffectNoiseReduction::Dialog::OnSlider)
1519  EVT_TEXT(ID_ATTACK_TIME_TEXT, EffectNoiseReduction::Dialog::OnText)
1520 
1521  EVT_SLIDER(ID_RELEASE_TIME_SLIDER, EffectNoiseReduction::Dialog::OnSlider)
1522  EVT_TEXT(ID_RELEASE_TIME_TEXT, EffectNoiseReduction::Dialog::OnText)
1523 #endif
1524 
1525 
1526 #ifdef ADVANCED_SETTINGS
1527  EVT_SLIDER(ID_OLD_SENSITIVITY_SLIDER, EffectNoiseReduction::Dialog::OnSlider)
1528  EVT_TEXT(ID_OLD_SENSITIVITY_TEXT, EffectNoiseReduction::Dialog::OnText)
1529 #endif
1531 
1534  EffectNoiseReduction::Settings *settings,
1535  wxWindow *parent, bool bHasProfile, bool bAllowTwiddleSettings)
1536  : EffectDialog( parent, _("Noise Reduction"), EffectTypeProcess)
1537  , m_pEffect(effect)
1538  , m_pSettings(settings) // point to
1539  , mTempSettings(*settings) // copy
1540  , mbHasProfile(bHasProfile)
1541  , mbAllowTwiddleSettings(bAllowTwiddleSettings)
1542  // NULL out the control members until the controls are created.
1543  , mKeepSignal(NULL)
1544 #ifdef ISOLATE_CHOICE
1545  , mKeepNoise(NULL)
1546 #endif
1547 #ifdef RESIDUE_CHOICE
1548  , mResidue(NULL)
1549 #endif
1550 {
1552 
1553  wxButton *const pButtonPreview =
1554  (wxButton *)wxWindow::FindWindowById(ID_EFFECT_PREVIEW, this);
1555  wxButton *const pButtonReduceNoise =
1556  (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
1557 
1558  if (mbHasProfile || mbAllowTwiddleSettings) {
1559  pButtonPreview->Enable(!mbAllowTwiddleSettings);
1560  pButtonReduceNoise->SetFocus();
1561  }
1562  else {
1563  pButtonPreview->Enable(false);
1564  pButtonReduceNoise->Enable(false);
1565  }
1566 }
1567 
1569 {
1570  // If Isolate is chosen, disable controls that define
1571  // "what to do with noise" rather than "what is noise."
1572  // Else, enable them.
1573  // This does NOT include sensitivity, NEW or old, nor
1574  // the choice of window functions, size, or step.
1575  // The method choice is not included, because it affects
1576  // which sensitivity slider is operative, and that is part
1577  // of what defines noise.
1578 
1579  static const int toDisable[] = {
1581  ID_GAIN_TEXT,
1582 
1584  ID_FREQ_TEXT,
1585 
1586 #ifdef ATTACK_AND_RELEASE
1587  ID_ATTACK_TIME_SLIDER,
1588  ID_ATTACK_TIME_TEXT,
1589 
1590  ID_RELEASE_TIME_SLIDER,
1591  ID_RELEASE_TIME_TEXT,
1592 #endif
1593  };
1594  static const int nToDisable = sizeof(toDisable) / sizeof(toDisable[0]);
1595 
1596  bool bIsolating =
1597 #ifdef ISOLATE_CHOICE
1598  mKeepNoise->GetValue();
1599 #else
1600  false;
1601 #endif
1602  for (int ii = nToDisable; ii--;)
1603  wxWindow::FindWindowById(toDisable[ii], this)->Enable(!bIsolating);
1604 }
1605 
1606 #ifdef ADVANCED_SETTINGS
1607 void EffectNoiseReduction::Dialog::EnableDisableSensitivityControls()
1608 {
1609  wxChoice *const pChoice =
1610  static_cast<wxChoice*>(wxWindow::FindWindowById(ID_CHOICE_METHOD, this));
1611  const bool bOldMethod =
1612  pChoice->GetSelection() == DM_OLD_METHOD;
1613  wxWindow::FindWindowById(ID_OLD_SENSITIVITY_SLIDER, this)->Enable(bOldMethod);
1614  wxWindow::FindWindowById(ID_OLD_SENSITIVITY_TEXT, this)->Enable(bOldMethod);
1615  wxWindow::FindWindowById(ID_NEW_SENSITIVITY_SLIDER, this)->Enable(!bOldMethod);
1616  wxWindow::FindWindowById(ID_NEW_SENSITIVITY_TEXT, this)->Enable(!bOldMethod);
1617 }
1618 #endif
1619 
1620 void EffectNoiseReduction::Dialog::OnGetProfile(wxCommandEvent & WXUNUSED(event))
1621 {
1622  // Project has not be changed so skip pushing state
1624 
1625  if (!TransferDataFromWindow())
1626  return;
1627 
1628  // Return code distinguishes this first step from the actual effect
1629  EndModal(1);
1630 }
1631 
1632 // This handles the whole radio group
1633 void EffectNoiseReduction::Dialog::OnNoiseReductionChoice( wxCommandEvent & WXUNUSED(event))
1634 {
1635  if (mKeepSignal->GetValue())
1636  mTempSettings.mNoiseReductionChoice = NRC_REDUCE_NOISE;
1637 #ifdef ISOLATE_CHOICE
1638  else if (mKeepNoise->GetValue())
1639  mTempSettings.mNoiseReductionChoice = NRC_ISOLATE_NOISE;
1640 #endif
1641 #ifdef RESIDUE_CHOICE
1642  else
1643  mTempSettings.mNoiseReductionChoice = NRC_LEAVE_RESIDUE;
1644 #endif
1645  DisableControlsIfIsolating();
1646 }
1647 
1648 #ifdef ADVANCED_SETTINGS
1649 void EffectNoiseReduction::Dialog::OnMethodChoice(wxCommandEvent &)
1650 {
1651  EnableDisableSensitivityControls();
1652 }
1653 #endif
1654 
1655 void EffectNoiseReduction::Dialog::OnPreview(wxCommandEvent & WXUNUSED(event))
1656 {
1657  if (!TransferDataFromWindow())
1658  return;
1659 
1660  // Save & restore parameters around Preview, because we didn't do OK.
1661  auto cleanup = valueRestorer( *m_pSettings );
1662  *m_pSettings = mTempSettings;
1663  m_pSettings->mDoProfile = false;
1664 
1665  m_pEffect->Preview();
1666 }
1667 
1668 void EffectNoiseReduction::Dialog::OnReduceNoise( wxCommandEvent & WXUNUSED(event))
1669 {
1670  if (!TransferDataFromWindow())
1671  return;
1672 
1673  EndModal(2);
1674 }
1675 
1676 void EffectNoiseReduction::Dialog::OnCancel(wxCommandEvent & WXUNUSED(event))
1677 {
1678  EndModal(0);
1679 }
1680 
1682 {
1683  S.StartStatic(_("Step 1"));
1684  {
1685  S.AddVariableText(_(
1686  "Select a few seconds of just noise so Audacity knows what to filter out,\nthen click Get Noise Profile:"));
1687  //m_pButton_GetProfile =
1688  S.Id(ID_BUTTON_GETPROFILE).AddButton(_("&Get Noise Profile"));
1689  }
1690  S.EndStatic();
1691 
1692  S.StartStatic(_("Step 2"));
1693  {
1694  S.AddVariableText(_(
1695  "Select all of the audio you want filtered, choose how much noise you want\nfiltered out, and then click 'OK' to reduce noise.\n"));
1696 
1697  S.StartMultiColumn(3, wxEXPAND);
1698  S.SetStretchyCol(2);
1699  {
1700  wxTextValidator vld(wxFILTER_NUMERIC);
1701  for (int id = FIRST_SLIDER; id < END_OF_BASIC_SLIDERS; id += 2) {
1702  const ControlInfo &info = controlInfo()[(id - FIRST_SLIDER) / 2];
1703  info.CreateControls(id, vld, S);
1704  }
1705  }
1706  S.EndMultiColumn();
1707 
1708  S.StartMultiColumn(
1709  2
1710 #ifdef RESIDUE_CHOICE
1711  +1
1712 #endif
1713 #ifdef ISOLATE_CHOICE
1714  +1
1715 #endif
1716  ,
1717  wxALIGN_CENTER_HORIZONTAL);
1718  {
1719  S.AddPrompt(_("Noise:"));
1720  mKeepSignal = S.Id(ID_RADIOBUTTON_KEEPSIGNAL)
1721  .AddRadioButton(_("Re&duce")); /* i18n-hint: Translate differently from "Residue" ! */
1722 #ifdef ISOLATE_CHOICE
1723  mKeepNoise = S.Id(ID_RADIOBUTTON_KEEPNOISE)
1724  .AddRadioButtonToGroup(_("&Isolate"));
1725 #endif
1726 #ifdef RESIDUE_CHOICE
1727  mResidue = S.Id(ID_RADIOBUTTON_RESIDUE)
1728  .AddRadioButtonToGroup(_("Resid&ue")); /* i18n-hint: Means the difference between effect and original sound. Translate differently from "Reduce" ! */
1729 #endif
1730  }
1731  S.EndMultiColumn();
1732  }
1733  S.EndStatic();
1734 
1735 
1736 #ifdef ADVANCED_SETTINGS
1737  S.StartStatic(_("Advanced Settings"));
1738  {
1739  S.StartMultiColumn(2);
1740  {
1741  {
1742  wxArrayString windowTypeChoices;
1743  for (int ii = 0; ii < WT_N_WINDOW_TYPES; ++ii)
1744  windowTypeChoices.Add(windowTypesInfo[ii].name);
1745  S.TieChoice(_("&Window types") + wxString(wxT(":")),
1746  mTempSettings.mWindowTypes,
1747  &windowTypeChoices);
1748  }
1749 
1750  {
1751  wxArrayString windowSizeChoices;
1752  windowSizeChoices.Add(_("8"));
1753  windowSizeChoices.Add(_("16"));
1754  windowSizeChoices.Add(_("32"));
1755  windowSizeChoices.Add(_("64"));
1756  windowSizeChoices.Add(_("128"));
1757  windowSizeChoices.Add(_("256"));
1758  windowSizeChoices.Add(_("512"));
1759  windowSizeChoices.Add(_("1024"));
1760  windowSizeChoices.Add(_("2048 (default)"));
1761  windowSizeChoices.Add(_("4096"));
1762  windowSizeChoices.Add(_("8192"));
1763  windowSizeChoices.Add(_("16384"));
1764  S.TieChoice(_("Window si&ze") + wxString(wxT(":")),
1765  mTempSettings.mWindowSizeChoice,
1766  &windowSizeChoices);
1767  }
1768 
1769  {
1770  wxArrayString stepsPerWindowChoices;
1771  stepsPerWindowChoices.Add(_("2"));
1772  stepsPerWindowChoices.Add(_("4 (default)"));
1773  stepsPerWindowChoices.Add(_("8"));
1774  stepsPerWindowChoices.Add(_("16"));
1775  stepsPerWindowChoices.Add(_("32"));
1776  stepsPerWindowChoices.Add(_("64"));
1777  S.TieChoice(_("S&teps per window") + wxString(wxT(":")),
1778  mTempSettings.mStepsPerWindowChoice,
1779  &stepsPerWindowChoices);
1780  }
1781 
1782  S.Id(ID_CHOICE_METHOD);
1783  {
1784  wxArrayString methodChoices;
1785  int nn = DM_N_METHODS;
1786 #ifndef OLD_METHOD_AVAILABLE
1787  --nn;
1788 #endif
1789  for (int ii = 0; ii < nn; ++ii)
1790  methodChoices.Add(discriminationMethodInfo[ii].name);
1791  S.TieChoice(_("Discrimination &method") + wxString(wxT(":")),
1792  mTempSettings.mMethod,
1793  &methodChoices);
1794  }
1795  }
1796  S.EndMultiColumn();
1797 
1798  S.StartMultiColumn(3, wxEXPAND);
1799  S.SetStretchyCol(2);
1800  {
1801  wxTextValidator vld(wxFILTER_NUMERIC);
1802  for (int id = END_OF_BASIC_SLIDERS; id < END_OF_ADVANCED_SLIDERS; id += 2) {
1803  const ControlInfo &info = controlInfo[(id - FIRST_SLIDER) / 2];
1804  info.CreateControls(id, vld, S);
1805  }
1806  }
1807  S.EndMultiColumn();
1808  }
1809  S.EndStatic();
1810 #endif
1811 }
1812 
1814 {
1815  // Do the choice controls:
1817  return false;
1818 
1819  for (int id = FIRST_SLIDER; id < END_OF_SLIDERS; id += 2) {
1820  wxSlider* slider =
1821  static_cast<wxSlider*>(wxWindow::FindWindowById(id, this));
1822  wxTextCtrl* text =
1823  static_cast<wxTextCtrl*>(wxWindow::FindWindowById(id + 1, this));
1824  const ControlInfo &info = controlInfo()[(id - FIRST_SLIDER) / 2];
1825  const double field = mTempSettings.*(info.field);
1826  text->SetValue(info.Text(field));
1827  slider->SetValue(info.SliderSetting(field));
1828  }
1829 
1830  mKeepSignal->SetValue(mTempSettings.mNoiseReductionChoice == NRC_REDUCE_NOISE);
1831 #ifdef ISOLATE_CHOICE
1832  mKeepNoise->SetValue(mTempSettings.mNoiseReductionChoice == NRC_ISOLATE_NOISE);
1833 #endif
1834 #ifdef RESIDUE_CHOICE
1835  mResidue->SetValue(mTempSettings.mNoiseReductionChoice == NRC_LEAVE_RESIDUE);
1836 #endif
1837 
1838  // Set the enabled states of controls
1839  DisableControlsIfIsolating();
1840 #ifdef ADVANCED_SETTINGS
1841  EnableDisableSensitivityControls();
1842 #endif
1843 
1844  return true;
1845 }
1846 
1848 {
1849  // Do the choice controls:
1851  return false;
1852 
1853  wxCommandEvent dummy;
1854  OnNoiseReductionChoice(dummy);
1855 
1856  return mTempSettings.Validate(m_pEffect);
1857 }
1858 
1859 void EffectNoiseReduction::Dialog::OnText(wxCommandEvent &event)
1860 {
1861  int id = event.GetId();
1862  int idx = (id - FIRST_SLIDER - 1) / 2;
1863  const ControlInfo &info = controlInfo()[idx];
1864  wxTextCtrl* text =
1865  static_cast<wxTextCtrl*>(wxWindow::FindWindowById(id, this));
1866  wxSlider* slider =
1867  static_cast<wxSlider*>(wxWindow::FindWindowById(id - 1, this));
1868  double &field = mTempSettings.*(info.field);
1869 
1870  text->GetValue().ToDouble(&field);
1871  slider->SetValue(info.SliderSetting(field));
1872 }
1873 
1874 void EffectNoiseReduction::Dialog::OnSlider(wxCommandEvent &event)
1875 {
1876  int id = event.GetId();
1877  int idx = (id - FIRST_SLIDER) / 2;
1878  const ControlInfo &info = controlInfo()[idx];
1879  wxSlider* slider =
1880  static_cast<wxSlider*>(wxWindow::FindWindowById(id, this));
1881  wxTextCtrl* text =
1882  static_cast<wxTextCtrl*>(wxWindow::FindWindowById(id + 1, this));
1883  double &field = mTempSettings.*(info.field);
1884 
1885  field = info.Value(slider->GetValue());
1886  text->SetValue(info.Text(field));
1887 }
1888 
bool ProcessOne(EffectNoiseReduction &effect, Statistics &statistics, TrackFactory &factory, int count, WaveTrack *track, sampleCount start, sampleCount len)
wxChoice * TieChoice(const wxString &Prompt, WrappedType &WrappedRef, const wxArrayString *pChoices)
#define EXPERIMENTAL_SPECTRAL_EDITING
Definition: Project.h:143
NoiseReductionChoice
double mT1
Definition: Effect.h:460
bool Init() override
bool Process(EffectNoiseReduction &effect, Statistics &statistics, TrackFactory &factory, SelectedTrackListOfKindIterator &iter, double mT0, double mT1)
int MessageBox(const wxString &message, long style=DefaultMessageBoxStyle, const wxString &titleStr=wxString{})
Definition: Effect.cpp:2655
wxString GetDescription() override
bool TrackProgress(int whichTrack, double frac, const wxString &=wxEmptyString)
Definition: Effect.cpp:1978
virtual bool TransferDataFromWindow()
Definition: Effect.cpp:1874
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:366
bool Get(samplePtr buffer, sampleFormat format, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true) const
Definition: WaveTrack.cpp:1950
void CopyInputTracks()
Definition: Effect.cpp:2029
bool PromptUser(wxWindow *parent) override
A two-pass effect to reduce background noise.
DiscriminationMethod
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: Effect.cpp:2160
void EndMultiColumn()
HFFT GetFFT(size_t fftlen)
Definition: RealFFTf.cpp:110
void Init()
Definition: Effect.cpp:2680
void OnReduceNoise(wxCommandEvent &event)
void ClearAndPaste(double t0, double t1, const Track *src, bool preserve=true, bool merge=true, const TimeWarper *effectWarper=NULL)
Definition: WaveTrack.cpp:759
void PopulateOrExchange(ShuttleGui &S)
#define XO(s)
Definition: Internat.h:30
void OnCancel(wxCommandEvent &event)
bool Validate(EffectNoiseReduction *effect) const
New (Jun-2006) base class for effects dialogs. Likely to get greater use in future.
Definition: Effect.h:559
size_t GetBestBlockSize(sampleCount t) const
Definition: WaveTrack.cpp:1586
double mSampleRate
Definition: Effect.h:455
EffectNoiseReduction::Settings mTempSettings
void OnPreview(wxCommandEvent &event)
Used to create a WaveTrack, or a LabelTrack.. Implementation of the functions of this class are dispe...
Definition: Track.h:708
double mF0
Definition: Effect.h:462
Track * Next(bool skiplinked=false) override
Definition: Track.cpp:513
void AddPrompt(const wxString &Prompt)
Right aligned text string.
Definition: ShuttleGui.cpp:215
std::unique_ptr< FFTParam, FFTDeleter > HFFT
Definition: RealFFTf.h:24
#define ID_EFFECT_PREVIEW
Definition: Effect.h:556
bool Process() override
wxFileConfig * gPrefs
Definition: Prefs.cpp:72
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:493
bool TransferDataFromWindow() override
Definition: Effect.cpp:2724
int format
Definition: ExportPCM.cpp:56
std::unique_ptr< WaveTrack > Holder
Definition: WaveTrack.h:84
#define NOISEREDUCTION_PLUGIN_SYMBOL
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:998
void OnSlider(wxCommandEvent &event)
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
ShuttleGui & Id(int id)
Statistics(size_t spectrumSize, double rate, int windowTypes)
void SetStyle(int Style)
Definition: ShuttleGui.h:252
void FinishTrack(Statistics &statistics, WaveTrack *outputTrack)
EffectType GetType() override
void OnNoiseReductionChoice(wxCommandEvent &event)
if(pTrack &&pTrack->GetDisplay()!=WaveTrack::Spectrum)
const Settings & GetTempSettings() const
int min(int a, int b)
std::vector< movable_ptr< Record > > mQueue
EffectNoiseReduction * m_pEffect
void Append(samplePtr buffer, sampleFormat format, size_t len, unsigned int stride=1, XMLWriter *blockFileLog=NULL)
Append the sample data to the WaveTrack. You must call Flush() after the last Append.
Definition: WaveTrack.cpp:1536
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:1604
void OnText(wxCommandEvent &event)
static EffectManager & Get()
long TrapLong(long x, long min, long max)
Definition: Effect.h:735
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
std::unique_ptr< WaveTrack > NewWaveTrack(sampleFormat format=(sampleFormat) 0, double rate=0)
Definition: WaveTrack.cpp:78
void GatherStatistics(Statistics &statistics)
_("Move Track &Down")+wxT("\t")+(GetActiveProject() -> GetCommandManager() ->GetKeyFromName(wxT("TrackMoveDown"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveTopID, _("Move Track to &Top")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveTop"))), OnMoveTrack) POPUP_MENU_ITEM(OnMoveBottomID, _("Move Track to &Bottom")+wxT("\t")+(GetActiveProject() ->GetCommandManager() ->GetKeyFromName(wxT("TrackMoveBottom"))), OnMoveTrack) void TrackMenuTable::OnSetName(wxCommandEvent &)
void ProcessSamples(Statistics &statistics, WaveTrack *outputTrack, size_t len, float *buffer)
void InverseRealFFTf(fft_type *buffer, const FFTParam *h)
Definition: RealFFTf.cpp:269
void RealFFTf(fft_type *buffer, const FFTParam *h)
Definition: RealFFTf.cpp:167
TrackFactory * mFactory
Definition: Effect.h:456
ValueRestorer< T > valueRestorer(T &var)
Definition: MemoryX.h:875
void OnGetProfile(wxCommandEvent &event)
WindowTypes
const wxChar * name
Definition: Distortion.cpp:94
int GetNumWaveTracks()
Definition: Effect.h:343
sampleCount TimeToLongSamples(double t0) const
Convert correctly between an (absolute) time in seconds and a number of samples.
Definition: WaveTrack.cpp:1822
#define M_PI
Definition: Distortion.cpp:28
bool TransferDataToWindow() override
Definition: Effect.cpp:2716
sampleFormat GetSampleFormat() const
Definition: WaveTrack.h:140
wxStaticText * AddVariableText(const wxString &Str, bool bCenter=false, int PositionFlags=0)
Definition: ShuttleGui.cpp:373
wxStaticBox * StartStatic(const wxString &Str, int iProp=0)
Definition: ShuttleGui.cpp:701
void SetSkipStateFlag(bool flag)
std::vector< float > FloatVector
bool PromptUser(EffectNoiseReduction *effect, wxWindow *parent, bool bHasProfile, bool bAllowTwiddleSettings)
Track * First(TrackList *val=NULL) override
Definition: Track.cpp:502
void ReduceNoise(const Statistics &statistics, WaveTrack *outputTrack)
double GetEndTime() const
Get the time at which the last clip in the track ends, plus recorded stuff.
Definition: WaveTrack.cpp:1852
Dialog(EffectNoiseReduction *effect, Settings *settings, wxWindow *parent, bool bHasProfile, bool bAllowTwiddleSettings)
wxRadioButton * AddRadioButtonToGroup(const wxString &Prompt)
Definition: ShuttleGui.cpp:443
wxString GetSymbol() override
bool Classify(const Statistics &statistics, int band)
END_EVENT_TABLE()
double GetRate() const
Definition: WaveTrack.cpp:397
double mF1
Definition: Effect.h:463
void ApplyFreqSmoothing(FloatVector &gains)
bool TransferDataFromWindow() override
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
std::shared_ptr< TrackList > mOutputTracks
Definition: Effect.h:458
#define RESIDUE_CHOICE
Dialog used with EffectNoiseReduction.
std::unique_ptr< Settings > mSettings
wxButton * AddButton(const wxString &Text, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:301
void FinishTrackStatistics(Statistics &statistics)
Worker(const Settings &settings, double sampleRate)
EffectNoiseReduction::Settings Settings
std::unique_ptr< Statistics > mStatistics
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:192
EffectNoiseReduction::Statistics Statistics
double mT0
Definition: Effect.h:459
wxRadioButton * AddRadioButton(const wxString &Prompt)
Definition: ShuttleGui.cpp:427
EffectNoiseReduction::Settings * m_pSettings
double GetStartTime() const
Get the time at which the first clip in the track starts.
Definition: WaveTrack.cpp:1832
bool CheckWhetherSkipEffect() override
wxSlider * AddSlider(const wxString &Prompt, int pos, int Max, int Min=0)
Definition: ShuttleGui.cpp:456