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