Audacity  2.2.2
NoiseRemoval.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  NoiseRemoval.cpp
6 
7  Dominic Mazzoni
8 
9 *******************************************************************//****************************************************************//*******************************************************************/
40 
41 #include "../Audacity.h"
42 #include "../Experimental.h"
43 
44 #if !defined(EXPERIMENTAL_NOISE_REDUCTION)
45 
46 #include "NoiseRemoval.h"
47 
48 #include "../WaveTrack.h"
49 #include "../Prefs.h"
50 #include "../Project.h"
51 #include "../FileNames.h"
52 #include "../ShuttleGui.h"
53 
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/file.h>
62 #include <wx/ffile.h>
63 #include <wx/bitmap.h>
64 #include <wx/brush.h>
65 #include <wx/button.h>
66 #include <wx/choice.h>
67 #include <wx/radiobut.h>
68 #include <wx/dcmemory.h>
69 #include <wx/image.h>
70 #include <wx/intl.h>
71 #include <wx/sizer.h>
72 #include <wx/statbox.h>
73 #include <wx/stattext.h>
74 #include <wx/textctrl.h>
75 #include <wx/valtext.h>
76 
77 
78 #include "../PlatformCompatibility.h"
79 
80 EffectNoiseRemoval::EffectNoiseRemoval()
81 {
82  mWindowSize = 2048;
83  mSpectrumSize = 1 + mWindowSize / 2;
84 
85  gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseSensitivity"),
86  &mSensitivity, 0.0);
87  gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseGain"),
88  &mNoiseGain, -24.0);
89  gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseFreqSmoothing"),
90  &mFreqSmoothingHz, 150.0);
91  gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseAttackDecayTime"),
92  &mAttackDecayTime, 0.15);
93  gPrefs->Read(wxT("/Effects/NoiseRemoval/NoiseLeaveNoise"),
94  &mbLeaveNoise, false);
95 // mbLeaveNoise = false;
96 
97 
98  mMinSignalTime = 0.05f;
99  mHasProfile = false;
100  mDoProfile = true;
101 
102  mNoiseThreshold.reinit(mSpectrumSize);
103 
104  Init();
105 }
106 
107 EffectNoiseRemoval::~EffectNoiseRemoval()
108 {
109 }
110 
111 // IdentInterface implementation
112 
113 IdentInterfaceSymbol EffectNoiseRemoval::GetSymbol()
114 {
115  return XO("Noise Removal");
116 }
117 
118 wxString EffectNoiseRemoval::GetDescription()
119 {
120  return _("Removes constant background noise such as fans, tape noise, or hums");
121 }
122 
123 // EffectDefinitionInterface implementation
124 
125 EffectType EffectNoiseRemoval::GetType()
126 {
127  return EffectTypeProcess;
128 }
129 
130 bool EffectNoiseRemoval::SupportsAutomation()
131 {
132  return false;
133 }
134 
135 // Effect implementation
136 
137 #define MAX_NOISE_LEVEL 30
138 bool EffectNoiseRemoval::Init()
139 {
140  mLevel = gPrefs->Read(wxT("/Effects/NoiseRemoval/Noise_Level"), 3L);
141  if ((mLevel < 0) || (mLevel > MAX_NOISE_LEVEL)) { // corrupted Prefs?
142  mLevel = 0; //Off-skip
143  gPrefs->Write(wxT("/Effects/NoiseRemoval/Noise_Level"), mLevel);
144  }
145  return gPrefs->Flush();
146 }
147 
148 bool EffectNoiseRemoval::CheckWhetherSkipEffect()
149 {
150  return (mLevel == 0);
151 }
152 
153 bool EffectNoiseRemoval::PromptUser(wxWindow *parent)
154 {
155  NoiseRemovalDialog dlog(this, parent);
156  dlog.mSensitivity = mSensitivity;
157  dlog.mGain = -mNoiseGain;
158  dlog.mFreq = mFreqSmoothingHz;
159  dlog.mTime = mAttackDecayTime;
160  dlog.mbLeaveNoise = mbLeaveNoise;
161  dlog.mKeepSignal->SetValue(!mbLeaveNoise);
162  dlog.mKeepNoise->SetValue(mbLeaveNoise);
163 
164  // We may want to twiddle the levels if we are setting
165  // from an automation dialog, the only case in which we can
166  // get here without any wavetracks.
167  bool bAllowTwiddleSettings = (GetNumWaveTracks() == 0);
168 
169  if (mHasProfile || bAllowTwiddleSettings) {
170  dlog.m_pButton_Preview->Enable(GetNumWaveTracks() != 0);
171  dlog.m_pButton_RemoveNoise->SetDefault();
172  } else {
173  dlog.m_pButton_Preview->Enable(false);
174  dlog.m_pButton_RemoveNoise->Enable(false);
175  }
176 
177  dlog.TransferDataToWindow();
178  dlog.mKeepNoise->SetValue(dlog.mbLeaveNoise);
179  dlog.CentreOnParent();
180  dlog.ShowModal();
181 
182  if (dlog.GetReturnCode() == 0) {
183  return false;
184  }
185 
186  mSensitivity = dlog.mSensitivity;
187  mNoiseGain = -dlog.mGain;
188  mFreqSmoothingHz = dlog.mFreq;
189  mAttackDecayTime = dlog.mTime;
190  mbLeaveNoise = dlog.mbLeaveNoise;
191 
192  gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseSensitivity"), mSensitivity);
193  gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseGain"), mNoiseGain);
194  gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseFreqSmoothing"), mFreqSmoothingHz);
195  gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseAttackDecayTime"), mAttackDecayTime);
196  gPrefs->Write(wxT("/Effects/NoiseRemoval/NoiseLeaveNoise"), mbLeaveNoise);
197 
198  mDoProfile = (dlog.GetReturnCode() == 1);
199  return gPrefs->Flush();
200 }
201 
202 bool EffectNoiseRemoval::Process()
203 {
204  Initialize();
205 
206  // This same code will both remove noise and profile it,
207  // depending on 'mDoProfile'
208  this->CopyInputTracks(); // Set up mOutputTracks.
209  bool bGoodResult = true;
210 
211  SelectedTrackListOfKindIterator iter(Track::Wave, mOutputTracks.get());
212  WaveTrack *track = (WaveTrack *) iter.First();
213  int count = 0;
214  while (track) {
215  double trackStart = track->GetStartTime();
216  double trackEnd = track->GetEndTime();
217  double t0 = mT0 < trackStart? trackStart: mT0;
218  double t1 = mT1 > trackEnd? trackEnd: mT1;
219 
220  if (t1 > t0) {
221  auto start = track->TimeToLongSamples(t0);
222  auto end = track->TimeToLongSamples(t1);
223  auto len = end - start;
224 
225  if (!ProcessOne(count, track, start, len)) {
226  bGoodResult = false;
227  break;
228  }
229  }
230  track = (WaveTrack *) iter.Next();
231  count++;
232  }
233 
234  if (bGoodResult && mDoProfile) {
235  mHasProfile = true;
236  mDoProfile = false;
237  }
238 
239  this->ReplaceProcessedTracks(bGoodResult);
240  return bGoodResult;
241 }
242 
243 void EffectNoiseRemoval::ApplyFreqSmoothing(float *spec)
244 {
245  Floats tmp{ mSpectrumSize };
246  int j, j0, j1;
247 
248  for(int i = 0; i < mSpectrumSize; i++) {
249  j0 = wxMax(0, i - mFreqSmoothingBins);
250  j1 = wxMin(mSpectrumSize-1, i + mFreqSmoothingBins);
251  tmp[i] = 0.0;
252  for(j = j0; j <= j1; j++) {
253  tmp[i] += spec[j];
254  }
255  tmp[i] /= (j1 - j0 + 1);
256  }
257 
258  for(size_t i = 0; i < mSpectrumSize; i++)
259  spec[i] = tmp[i];
260 }
261 
262 void EffectNoiseRemoval::Initialize()
263 {
264  mSampleRate = mProjectRate;
265  mFreqSmoothingBins = (int)(mFreqSmoothingHz * mWindowSize / mSampleRate);
266  mAttackDecayBlocks = 1 +
267  (int)(mAttackDecayTime * mSampleRate / (mWindowSize / 2));
268  mNoiseAttenFactor = DB_TO_LINEAR(mNoiseGain);
269  mOneBlockAttackDecay = DB_TO_LINEAR(mNoiseGain / mAttackDecayBlocks);
270  // Applies to power, divide by 10:
271  mSensitivityFactor = pow(10.0, mSensitivity/10.0);
272  mMinSignalBlocks =
273  (int)(mMinSignalTime * mSampleRate / (mWindowSize / 2));
274  if( mMinSignalBlocks < 1 )
275  mMinSignalBlocks = 1;
276  mHistoryLen = (2 * mAttackDecayBlocks) - 1;
277 
278  if (mHistoryLen < mMinSignalBlocks)
279  mHistoryLen = mMinSignalBlocks;
280 
281  mSpectrums.reinit(mHistoryLen, mSpectrumSize);
282  mGains.reinit(mHistoryLen, mSpectrumSize);
283  mRealFFTs.reinit(mHistoryLen, mSpectrumSize);
284  mImagFFTs.reinit(mHistoryLen, mSpectrumSize);
285 
286  // Initialize the FFT
287  hFFT = GetFFT(mWindowSize);
288 
289  mFFTBuffer.reinit(mWindowSize);
290  mInWaveBuffer.reinit(mWindowSize);
291  mWindow.reinit(mWindowSize);
292  mOutOverlapBuffer.reinit(mWindowSize);
293 
294  // Create a Hanning window function
295  for(size_t i=0; i<mWindowSize; i++)
296  mWindow[i] = 0.5 - 0.5 * cos((2.0*M_PI*i) / mWindowSize);
297 
298  if (mDoProfile) {
299  for (size_t i = 0; i < mSpectrumSize; i++)
300  mNoiseThreshold[i] = float(0);
301  }
302 }
303 
304 void EffectNoiseRemoval::End()
305 {
306  hFFT.reset();
307 
308  if (mDoProfile) {
309  ApplyFreqSmoothing(mNoiseThreshold.get());
310  }
311 
312  mSpectrums.reset();
313  mGains.reset();
314  mRealFFTs.reset();
315  mImagFFTs.reset();
316 
317  mFFTBuffer.reset();
318  mInWaveBuffer.reset();
319  mWindow.reset();
320  mOutOverlapBuffer.reset();
321 
322  mOutputTrack.reset();
323 }
324 
325 void EffectNoiseRemoval::StartNewTrack()
326 {
327  for(size_t i = 0; i < mHistoryLen; i++) {
328  for(size_t j = 0; j < mSpectrumSize; j++) {
329  mSpectrums[i][j] = 0;
330  mGains[i][j] = mNoiseAttenFactor;
331  mRealFFTs[i][j] = 0.0;
332  mImagFFTs[i][j] = 0.0;
333  }
334  }
335 
336  for(size_t j = 0; j < mWindowSize; j++)
337  mOutOverlapBuffer[j] = 0.0;
338 
339  mInputPos = 0;
340  mInSampleCount = 0;
341  mOutSampleCount = -(int)((mWindowSize / 2) * (mHistoryLen - 1));
342 }
343 
344 void EffectNoiseRemoval::ProcessSamples(size_t len, float *buffer)
345 {
346  while(len && mOutSampleCount < mInSampleCount) {
347  size_t avail = wxMin(len, mWindowSize - mInputPos);
348  for(size_t i = 0; i < avail; i++)
349  mInWaveBuffer[mInputPos + i] = buffer[i];
350  buffer += avail;
351  len -= avail;
352  mInputPos += avail;
353 
354  if (mInputPos == int(mWindowSize)) {
355  FillFirstHistoryWindow();
356  if (mDoProfile)
357  GetProfile();
358  else
359  RemoveNoise();
360  RotateHistoryWindows();
361 
362  // Rotate halfway for overlap-add
363  for(size_t i = 0; i < mWindowSize / 2; i++) {
364  mInWaveBuffer[i] = mInWaveBuffer[i + mWindowSize / 2];
365  }
366  mInputPos = mWindowSize / 2;
367  }
368  }
369 }
370 
371 void EffectNoiseRemoval::FillFirstHistoryWindow()
372 {
373  for(size_t i = 0; i < mWindowSize; i++)
374  mFFTBuffer[i] = mInWaveBuffer[i];
375  RealFFTf(mFFTBuffer.get(), hFFT.get());
376  for(size_t i = 1; i + 1 < mSpectrumSize; i++) {
377  mRealFFTs[0][i] = mFFTBuffer[hFFT->BitReversed[i] ];
378  mImagFFTs[0][i] = mFFTBuffer[hFFT->BitReversed[i]+1];
379  mSpectrums[0][i] = mRealFFTs[0][i]*mRealFFTs[0][i] + mImagFFTs[0][i]*mImagFFTs[0][i];
380  mGains[0][i] = mNoiseAttenFactor;
381  }
382  // DC and Fs/2 bins need to be handled specially
383  mSpectrums[0][0] = mFFTBuffer[0]*mFFTBuffer[0];
384  mSpectrums[0][mSpectrumSize-1] = mFFTBuffer[1]*mFFTBuffer[1];
385  mGains[0][0] = mNoiseAttenFactor;
386  mGains[0][mSpectrumSize-1] = mNoiseAttenFactor;
387 }
388 
389 namespace {
390  inline void Rotate(ArraysOf<float> &arrays, size_t historyLen)
391  {
392  Floats temp = std::move( arrays[ historyLen - 1 ] );
393 
394  for ( size_t nn = historyLen - 1; nn--; )
395  arrays[ nn + 1 ] = std::move( arrays[ nn ] );
396  arrays[0] = std::move( temp );
397  }
398 }
399 
400 void EffectNoiseRemoval::RotateHistoryWindows()
401 {
402  // Remember the last window so we can reuse it
403  Rotate(mSpectrums, mHistoryLen);
404  Rotate(mGains, mHistoryLen);
405  Rotate(mRealFFTs, mHistoryLen);
406  Rotate(mImagFFTs, mHistoryLen);
407 }
408 
409 void EffectNoiseRemoval::FinishTrack()
410 {
411  // Keep flushing empty input buffers through the history
412  // windows until we've output exactly as many samples as
413  // were input.
414  // Well, not exactly, but not more than mWindowSize/2 extra samples at the end.
415  // We'll DELETE them later in ProcessOne.
416 
417  Floats empty{ mWindowSize / 2 };
418  for(size_t i = 0; i < mWindowSize / 2; i++)
419  empty[i] = 0.0;
420 
421  while (mOutSampleCount < mInSampleCount) {
422  ProcessSamples(mWindowSize / 2, empty.get());
423  }
424 }
425 
426 void EffectNoiseRemoval::GetProfile()
427 {
428  // The noise threshold for each frequency is the maximum
429  // level achieved at that frequency for a minimum of
430  // mMinSignalBlocks blocks in a row - the max of a min.
431 
432  int start = mHistoryLen - mMinSignalBlocks;
433  int finish = mHistoryLen;
434  int i;
435 
436  for (size_t j = 0; j < mSpectrumSize; j++) {
437  float min = mSpectrums[start][j];
438  for (i = start+1; i < finish; i++) {
439  if (mSpectrums[i][j] < min)
440  min = mSpectrums[i][j];
441  }
442  if (min > mNoiseThreshold[j])
443  mNoiseThreshold[j] = min;
444  }
445 
446  mOutSampleCount += mWindowSize / 2; // what is this for? Not used when we are getting the profile?
447 }
448 
449 void EffectNoiseRemoval::RemoveNoise()
450 {
451  size_t center = mHistoryLen / 2;
452  size_t start = center - mMinSignalBlocks/2;
453  size_t finish = start + mMinSignalBlocks;
454 
455  // Raise the gain for elements in the center of the sliding history
456  for (size_t j = 0; j < mSpectrumSize; j++) {
457  float min = mSpectrums[start][j];
458  for (size_t i = start+1; i < finish; i++) {
459  if (mSpectrums[i][j] < min)
460  min = mSpectrums[i][j];
461  }
462  if (min > mSensitivityFactor * mNoiseThreshold[j] && mGains[center][j] < 1.0) {
463  if (mbLeaveNoise) mGains[center][j] = 0.0;
464  else mGains[center][j] = 1.0;
465  } else {
466  if (mbLeaveNoise) mGains[center][j] = 1.0;
467  }
468  }
469 
470  // Decay the gain in both directions;
471  // note that mOneBlockAttackDecay is less than 1.0
472  // of linear attenuation per block
473  for (size_t j = 0; j < mSpectrumSize; j++) {
474  for (size_t i = center + 1; i < mHistoryLen; i++) {
475  if (mGains[i][j] < mGains[i - 1][j] * mOneBlockAttackDecay)
476  mGains[i][j] = mGains[i - 1][j] * mOneBlockAttackDecay;
477  if (mGains[i][j] < mNoiseAttenFactor)
478  mGains[i][j] = mNoiseAttenFactor;
479  }
480  for (size_t i = center; i--;) {
481  if (mGains[i][j] < mGains[i + 1][j] * mOneBlockAttackDecay)
482  mGains[i][j] = mGains[i + 1][j] * mOneBlockAttackDecay;
483  if (mGains[i][j] < mNoiseAttenFactor)
484  mGains[i][j] = mNoiseAttenFactor;
485  }
486  }
487 
488 
489  // Apply frequency smoothing to output gain
490  int out = mHistoryLen - 1; // end of the queue
491 
492  ApplyFreqSmoothing(mGains[out].get());
493 
494  // Apply gain to FFT
495  for (size_t j = 0; j < (mSpectrumSize-1); j++) {
496  mFFTBuffer[j*2 ] = mRealFFTs[out][j] * mGains[out][j];
497  mFFTBuffer[j*2+1] = mImagFFTs[out][j] * mGains[out][j];
498  }
499  // The Fs/2 component is stored as the imaginary part of the DC component
500  mFFTBuffer[1] = mRealFFTs[out][mSpectrumSize-1] * mGains[out][mSpectrumSize-1];
501 
502  // Invert the FFT into the output buffer
503  InverseRealFFTf(mFFTBuffer.get(), hFFT.get());
504 
505  // Overlap-add
506  for(size_t j = 0; j < (mSpectrumSize-1); j++) {
507  mOutOverlapBuffer[j*2 ] += mFFTBuffer[hFFT->BitReversed[j] ] * mWindow[j*2 ];
508  mOutOverlapBuffer[j*2+1] += mFFTBuffer[hFFT->BitReversed[j]+1] * mWindow[j*2+1];
509  }
510 
511  // Output the first half of the overlap buffer, they're done -
512  // and then shift the next half over.
513  if (mOutSampleCount >= 0) { // ...but not if it's the first half-window
514  mOutputTrack->Append((samplePtr)mOutOverlapBuffer.get(), floatSample,
515  mWindowSize / 2);
516  }
517  mOutSampleCount += mWindowSize / 2;
518  for(size_t j = 0; j < mWindowSize / 2; j++) {
519  mOutOverlapBuffer[j] = mOutOverlapBuffer[j + (mWindowSize / 2)];
520  mOutOverlapBuffer[j + (mWindowSize / 2)] = 0.0;
521  }
522 }
523 
524 bool EffectNoiseRemoval::ProcessOne(int count, WaveTrack * track,
525  sampleCount start, sampleCount len)
526 {
527  if (track == NULL)
528  return false;
529 
530  StartNewTrack();
531 
532  if (!mDoProfile)
533  mOutputTrack = mFactory->NewWaveTrack(track->GetSampleFormat(),
534  track->GetRate());
535 
536  auto bufferSize = track->GetMaxBlockSize();
537  Floats buffer{ bufferSize };
538 
539  bool bLoopSuccess = true;
540  auto samplePos = start;
541  while (samplePos < start + len) {
542  //Get a blockSize of samples (smaller than the size of the buffer)
543  //Adjust the block size if it is the final block in the track
544  const auto blockSize = limitSampleBufferSize(
545  track->GetBestBlockSize(samplePos),
546  start + len - samplePos
547  );
548 
549  //Get the samples from the track and put them in the buffer
550  track->Get((samplePtr)buffer.get(), floatSample, samplePos, blockSize);
551 
552  mInSampleCount += blockSize;
553  ProcessSamples(blockSize, buffer.get());
554 
555  samplePos += blockSize;
556 
557  // Update the Progress meter
558  if (TrackProgress(count, (samplePos - start).as_double() / len.as_double())) {
559  bLoopSuccess = false;
560  break;
561  }
562  }
563 
564  FinishTrack();
565 
566  if (!mDoProfile) {
567  // Flush the output WaveTrack (since it's buffered)
568  mOutputTrack->Flush();
569 
570  // Take the output track and insert it in place of the original
571  // sample data (as operated on -- this may not match mT0/mT1)
572  if (bLoopSuccess) {
573  double t0 = mOutputTrack->LongSamplesToTime(start);
574  double tLen = mOutputTrack->LongSamplesToTime(len);
575  // Filtering effects always end up with more data than they started with. Delete this 'tail'.
576  mOutputTrack->HandleClear(tLen, mOutputTrack->GetEndTime(), false, false);
577  track->ClearAndPaste(t0, t0 + tLen, mOutputTrack.get(), true, false);
578  }
579  }
580 
581  return bLoopSuccess;
582 }
583 
584 // WDR: class implementations
585 
586 //----------------------------------------------------------------------------
587 // NoiseRemovalDialog
588 //----------------------------------------------------------------------------
589 
590 // WDR: event table for NoiseRemovalDialog
591 
592 enum {
593  ID_BUTTON_GETPROFILE = 10001,
594  ID_BUTTON_LEAVENOISE,
596  ID_RADIOBUTTON_KEEPNOISE,
597  ID_SENSITIVITY_SLIDER,
600  ID_TIME_SLIDER,
601  ID_SENSITIVITY_TEXT,
602  ID_GAIN_TEXT,
603  ID_FREQ_TEXT,
604  ID_TIME_TEXT,
605 };
606 
607 #define SENSITIVITY_MIN 0 // Corresponds to -20 dB
608 #define SENSITIVITY_MAX 4000 // Corresponds to 20 dB
609 
610 #define GAIN_MIN 0
611 #define GAIN_MAX 48 // Corresponds to -48 dB
612 
613 #define FREQ_MIN 0
614 #define FREQ_MAX 100 // Corresponds to 1000 Hz
615 
616 #define TIME_MIN 0
617 #define TIME_MAX 100 // Corresponds to 1.000 seconds
618 
619 
620 BEGIN_EVENT_TABLE(NoiseRemovalDialog,wxDialogWrapper)
621  EVT_BUTTON(wxID_OK, NoiseRemovalDialog::OnRemoveNoise)
622  EVT_BUTTON(wxID_CANCEL, NoiseRemovalDialog::OnCancel)
625  EVT_RADIOBUTTON(ID_RADIOBUTTON_KEEPNOISE, NoiseRemovalDialog::OnKeepNoise)
626  EVT_RADIOBUTTON(ID_RADIOBUTTON_KEEPSIGNAL, NoiseRemovalDialog::OnKeepNoise)
627  EVT_SLIDER(ID_SENSITIVITY_SLIDER, NoiseRemovalDialog::OnSensitivitySlider)
628  EVT_SLIDER(ID_GAIN_SLIDER, NoiseRemovalDialog::OnGainSlider)
629  EVT_SLIDER(ID_FREQ_SLIDER, NoiseRemovalDialog::OnFreqSlider)
630  EVT_SLIDER(ID_TIME_SLIDER, NoiseRemovalDialog::OnTimeSlider)
631  EVT_TEXT(ID_SENSITIVITY_TEXT, NoiseRemovalDialog::OnSensitivityText)
632  EVT_TEXT(ID_GAIN_TEXT, NoiseRemovalDialog::OnGainText)
633  EVT_TEXT(ID_FREQ_TEXT, NoiseRemovalDialog::OnFreqText)
634  EVT_TEXT(ID_TIME_TEXT, NoiseRemovalDialog::OnTimeText)
636 
638  wxWindow *parent)
639  : EffectDialog( parent, _("Noise Removal"), EffectTypeProcess)
640 {
641  m_pEffect = effect;
642 
643  // NULL out the control members until the controls are created.
644  m_pButton_GetProfile = NULL;
645  m_pButton_Preview = NULL;
646  m_pButton_RemoveNoise = NULL;
647 
648  Init();
649 
650  m_pButton_Preview =
651  (wxButton *)wxWindow::FindWindowById(ID_EFFECT_PREVIEW, this);
652  m_pButton_RemoveNoise =
653  (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
654 }
655 
656 void NoiseRemovalDialog::OnGetProfile( wxCommandEvent & WXUNUSED(event))
657 {
658  EndModal(1);
659 }
660 
661 void NoiseRemovalDialog::OnKeepNoise( wxCommandEvent & WXUNUSED(event))
662 {
663  mbLeaveNoise = mKeepNoise->GetValue();
664 }
665 
666 void NoiseRemovalDialog::OnPreview(wxCommandEvent & WXUNUSED(event))
667 {
668  // Save & restore parameters around Preview, because we didn't do OK.
669  bool oldDoProfile = m_pEffect->mDoProfile;
670  bool oldLeaveNoise = m_pEffect->mbLeaveNoise;
671  double oldSensitivity = m_pEffect->mSensitivity;
672  double oldGain = m_pEffect->mNoiseGain;
673  double oldFreq = m_pEffect->mFreqSmoothingHz;
674  double oldTime = m_pEffect->mAttackDecayTime;
675 
676  TransferDataFromWindow();
677 
678  m_pEffect->mDoProfile = false;
679  m_pEffect->mbLeaveNoise = mbLeaveNoise;
680  m_pEffect->mSensitivity = mSensitivity;
681  m_pEffect->mNoiseGain = -mGain;
682  m_pEffect->mFreqSmoothingHz = mFreq;
683  m_pEffect->mAttackDecayTime = mTime;
684 
685  auto cleanup = finally( [&] {
686  m_pEffect->mSensitivity = oldSensitivity;
687  m_pEffect->mNoiseGain = oldGain;
688  m_pEffect->mFreqSmoothingHz = oldFreq;
689  m_pEffect->mAttackDecayTime = oldTime;
690  m_pEffect->mbLeaveNoise = oldLeaveNoise;
691  m_pEffect->mDoProfile = oldDoProfile;
692  } );
693 
694  m_pEffect->Preview();
695 }
696 
697 void NoiseRemovalDialog::OnRemoveNoise( wxCommandEvent & WXUNUSED(event))
698 {
699  mbLeaveNoise = mKeepNoise->GetValue();
700  EndModal(2);
701 }
702 
703 void NoiseRemovalDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
704 {
705  EndModal(0);
706 }
707 
708 void NoiseRemovalDialog::PopulateOrExchange(ShuttleGui & S)
709 {
710  S.StartStatic(_("Step 1"));
711  {
712  S.AddVariableText(_("Select a few seconds of just noise so Audacity knows what to filter out,\nthen click Get Noise Profile:"));
713  m_pButton_GetProfile = S.Id(ID_BUTTON_GETPROFILE).AddButton(_("&Get Noise Profile"));
714  }
715  S.EndStatic();
716 
717  S.StartStatic(_("Step 2"));
718  {
719  S.AddVariableText(_("Select all of the audio you want filtered, choose how much noise you want\nfiltered out, and then click 'OK' to remove noise.\n"));
720 
721  S.StartMultiColumn(3, wxEXPAND);
722  S.SetStretchyCol(2);
723  {
724  wxTextValidator vld(wxFILTER_NUMERIC);
725  mGainT = S.Id(ID_GAIN_TEXT).AddTextBox(_("Noise re&duction (dB):"), wxT(""), 0);
726  S.SetStyle(wxSL_HORIZONTAL);
727  mGainT->SetValidator(vld);
728  mGainS = S.Id(ID_GAIN_SLIDER).AddSlider(wxT(""), 0, GAIN_MAX);
729  mGainS->SetName(_("Noise reduction"));
730  mGainS->SetRange(GAIN_MIN, GAIN_MAX);
731  mGainS->SetSizeHints(150, -1);
732 
733  mSensitivityT = S.Id(ID_SENSITIVITY_TEXT).AddTextBox(_("&Sensitivity (dB):"),
734  wxT(""),
735  0);
736  S.SetStyle(wxSL_HORIZONTAL);
737  mSensitivityT->SetValidator(vld);
738  mSensitivityS = S.Id(ID_SENSITIVITY_SLIDER).AddSlider(wxT(""), 0, SENSITIVITY_MAX);
739  mSensitivityS->SetName(_("Sensitivity"));
740  mSensitivityS->SetRange(SENSITIVITY_MIN, SENSITIVITY_MAX);
741  mSensitivityS->SetSizeHints(150, -1);
742 
743  mFreqT = S.Id(ID_FREQ_TEXT).AddTextBox(_("Fr&equency smoothing (Hz):"),
744  wxT(""),
745  0);
746  S.SetStyle(wxSL_HORIZONTAL);
747  mFreqT->SetValidator(vld);
748  mFreqS = S.Id(ID_FREQ_SLIDER).AddSlider(wxT(""), 0, FREQ_MAX);
749  mFreqS->SetName(_("Frequency smoothing"));
750  mFreqS->SetRange(FREQ_MIN, FREQ_MAX);
751  mFreqS->SetSizeHints(150, -1);
752 
753  mTimeT = S.Id(ID_TIME_TEXT).AddTextBox(_("Attac&k/decay time (secs):"),
754  wxT(""),
755  0);
756  S.SetStyle(wxSL_HORIZONTAL);
757  mTimeT->SetValidator(vld);
758  mTimeS = S.Id(ID_TIME_SLIDER).AddSlider(wxT(""), 0, TIME_MAX);
759  mTimeS->SetName(_("Attack/decay time"));
760  mTimeS->SetRange(TIME_MIN, TIME_MAX);
761  mTimeS->SetSizeHints(150, -1);
762 
763  S.AddPrompt(_("Noise:"));
764  mKeepSignal = S.Id(ID_RADIOBUTTON_KEEPSIGNAL)
765  .AddRadioButton(_("Re&move"));
766  mKeepNoise = S.Id(ID_RADIOBUTTON_KEEPNOISE)
767  .AddRadioButtonToGroup(_("&Isolate"));
768  }
769  S.EndMultiColumn();
770  }
771  S.EndStatic();
772 }
773 
774 bool NoiseRemovalDialog::TransferDataToWindow()
775 {
776  mSensitivityT->SetValue(wxString::Format(wxT("%.2f"), mSensitivity));
777  mGainT->SetValue(wxString::Format(wxT("%d"), (int)mGain));
778  mFreqT->SetValue(wxString::Format(wxT("%d"), (int)mFreq));
779  mTimeT->SetValue(wxString::Format(wxT("%.2f"), mTime));
780  mKeepNoise->SetValue(mbLeaveNoise);
781  mKeepSignal->SetValue(!mbLeaveNoise);
782 
783  mSensitivityS->SetValue(TrapLong(mSensitivity*100.0 + (SENSITIVITY_MAX-SENSITIVITY_MIN+1)/2.0, SENSITIVITY_MIN, SENSITIVITY_MAX));
784  mGainS->SetValue(TrapLong(mGain, GAIN_MIN, GAIN_MAX));
785  mFreqS->SetValue(TrapLong(mFreq / 10, FREQ_MIN, FREQ_MAX));
786  mTimeS->SetValue(TrapLong(mTime * TIME_MAX + 0.5, TIME_MIN, TIME_MAX));
787 
788  return true;
789 }
790 
791 bool NoiseRemovalDialog::TransferDataFromWindow()
792 {
793  // Nothing to do here
794  return true;
795 }
796 
797 void NoiseRemovalDialog::OnSensitivityText(wxCommandEvent & WXUNUSED(event))
798 {
799  mSensitivityT->GetValue().ToDouble(&mSensitivity);
800  mSensitivityS->SetValue(TrapLong(mSensitivity*100.0 + (SENSITIVITY_MAX-SENSITIVITY_MIN+1)/2.0, SENSITIVITY_MIN, SENSITIVITY_MAX));
801 }
802 
803 void NoiseRemovalDialog::OnGainText(wxCommandEvent & WXUNUSED(event))
804 {
805  mGainT->GetValue().ToDouble(&mGain);
806  mGainS->SetValue(TrapLong(mGain, GAIN_MIN, GAIN_MAX));
807 }
808 
809 void NoiseRemovalDialog::OnFreqText(wxCommandEvent & WXUNUSED(event))
810 {
811  mFreqT->GetValue().ToDouble(&mFreq);
812  mFreqS->SetValue(TrapLong(mFreq / 10, FREQ_MIN, FREQ_MAX));
813 }
814 
815 void NoiseRemovalDialog::OnTimeText(wxCommandEvent & WXUNUSED(event))
816 {
817  mTimeT->GetValue().ToDouble(&mTime);
818  mTimeS->SetValue(TrapLong(mTime * TIME_MAX + 0.5, TIME_MIN, TIME_MAX));
819 }
820 
821 void NoiseRemovalDialog::OnSensitivitySlider(wxCommandEvent & WXUNUSED(event))
822 {
823  mSensitivity = mSensitivityS->GetValue()/100.0 - 20.0;
824  mSensitivityT->SetValue(wxString::Format(wxT("%.2f"), mSensitivity));
825 }
826 
827 void NoiseRemovalDialog::OnGainSlider(wxCommandEvent & WXUNUSED(event))
828 {
829  mGain = mGainS->GetValue();
830  mGainT->SetValue(wxString::Format(wxT("%d"), (int)mGain));
831 }
832 
833 void NoiseRemovalDialog::OnFreqSlider(wxCommandEvent & WXUNUSED(event))
834 {
835  mFreq = mFreqS->GetValue() * 10;
836  mFreqT->SetValue(wxString::Format(wxT("%d"), (int)mFreq));
837 }
838 
839 void NoiseRemovalDialog::OnTimeSlider(wxCommandEvent & WXUNUSED(event))
840 {
841  mTime = mTimeS->GetValue() / (TIME_MAX*1.0);
842  mTimeT->SetValue(wxString::Format(wxT("%.2f"), mTime));
843 }
844 
845 #endif
AudacityPrefs * gPrefs
Definition: Prefs.cpp:73
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI...
Definition: ShuttleGui.h:409
void EndMultiColumn()
HFFT GetFFT(size_t fftlen)
Definition: RealFFTf.cpp:110
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
New (Jun-2006) base class for effects dialogs. Likely to get greater use in future.
Definition: Effect.h:560
size_t GetBestBlockSize(sampleCount t) const
Definition: WaveTrack.cpp:1607
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: Types.h:178
void AddPrompt(const wxString &Prompt)
Right aligned text string.
Definition: ShuttleGui.cpp:239
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
A two-pass effect to remove background noise.
wxTextCtrl * AddTextBox(const wxString &Caption, const wxString &Value, const int nChars)
Definition: ShuttleGui.cpp:540
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
char * samplePtr
Definition: Types.h:203
A Track that contains audio waveform data.
Definition: WaveTrack.h:60
ShuttleGui & Id(int id)
void SetStyle(int Style)
Definition: ShuttleGui.h:287
int min(int a, int b)
IdentInterfaceSymbol pairs a persistent string identifier used internally with an optional...
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:1625
long TrapLong(long x, long min, long max)
Definition: Effect.h:740
Dialog used with EffectNoiseRemoval.
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
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
EffectType
#define M_PI
Definition: Distortion.cpp:28
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
wxRadioButton * AddRadioButtonToGroup(const wxString &Prompt)
Definition: ShuttleGui.cpp:484
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
const double MIN_Threshold_Linear DB_TO_LINEAR(MIN_Threshold_dB)
wxButton * AddButton(const wxString &Text, int PositionFlags=wxALIGN_CENTRE)
Definition: ShuttleGui.cpp:341
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:203
wxRadioButton * AddRadioButton(const wxString &Prompt)
Definition: ShuttleGui.cpp:468
wxSlider * AddSlider(const wxString &Prompt, int pos, int Max, int Min=0)
Definition: ShuttleGui.cpp:497