Audacity  3.0.3
ClickRemoval.cpp
Go to the documentation of this file.
1 /**********************************************************************
2 
3  Audacity: A Digital Audio Editor
4 
5  ClickRemoval.cpp
6 
7  Craig DeForest
8 
9 *******************************************************************//*******************************************************************/
26 
27 
28 #include "ClickRemoval.h"
29 #include "LoadEffects.h"
30 
31 #include <math.h>
32 
33 #include <wx/intl.h>
34 #include <wx/slider.h>
35 #include <wx/valgen.h>
36 
37 #include "Prefs.h"
38 #include "../Shuttle.h"
39 #include "../ShuttleGui.h"
40 #include "../widgets/AudacityMessageBox.h"
41 #include "../widgets/valnum.h"
42 
43 #include "../WaveTrack.h"
44 
45 enum
46 {
47  ID_Thresh = 10000,
48  ID_Width
49 };
50 
51 // Define keys, defaults, minimums, and maximums for the effect parameters
52 //
53 // Name Type Key Def Min Max Scale
54 Param( Threshold, int, wxT("Threshold"), 200, 0, 900, 1 );
55 Param( Width, int, wxT("Width"), 20, 0, 40, 1 );
56 
58 { XO("Click Removal") };
59 
61 
62 BEGIN_EVENT_TABLE(EffectClickRemoval, wxEvtHandler)
68 
70 {
71  mThresholdLevel = DEF_Threshold;
72  mClickWidth = DEF_Width;
73 
74  SetLinearEffectFlag(false);
75 
76  windowSize = 8192;
77  sep = 2049;
78 }
79 
81 {
82 }
83 
84 // ComponentInterface implementation
85 
87 {
88  return Symbol;
89 }
90 
92 {
93  return XO("Click Removal is designed to remove clicks on audio tracks");
94 }
95 
97 {
98  return L"Click_Removal";
99 }
100 
101 // EffectDefinitionInterface implementation
102 
104 {
105  return EffectTypeProcess;
106 }
107 
108 // EffectClientInterface implementation
110  S.SHUTTLE_PARAM( mThresholdLevel, Threshold );
111  S.SHUTTLE_PARAM( mClickWidth, Width );
112  return true;
113 }
114 
116 {
117  parms.Write(KEY_Threshold, mThresholdLevel);
118  parms.Write(KEY_Width, mClickWidth);
119 
120  return true;
121 }
122 
124 {
125  ReadAndVerifyInt(Threshold);
126  ReadAndVerifyInt(Width);
127 
128  mThresholdLevel = Threshold;
129  mClickWidth = Width;
130 
131  return true;
132 }
133 
134 // Effect implementation
135 
137 {
138  return ((mClickWidth == 0) || (mThresholdLevel == 0));
139 }
140 
142 {
143  wxString base = wxT("/Effects/ClickRemoval/");
144 
145  // Migrate settings from 2.1.0 or before
146 
147  // Already migrated, so bail
148  if (gPrefs->Exists(base + wxT("Migrated")))
149  {
150  return true;
151  }
152 
153  // Load the old "current" settings
154  if (gPrefs->Exists(base))
155  {
156  mThresholdLevel = gPrefs->Read(base + wxT("ClickThresholdLevel"), 200);
157  if ((mThresholdLevel < MIN_Threshold) || (mThresholdLevel > MAX_Threshold))
158  { // corrupted Prefs?
159  mThresholdLevel = 0; //Off-skip
160  }
161  mClickWidth = gPrefs->Read(base + wxT("ClickWidth"), 20);
162  if ((mClickWidth < MIN_Width) || (mClickWidth > MAX_Width))
163  { // corrupted Prefs?
164  mClickWidth = 0; //Off-skip
165  }
166 
168 
169  // Do not migrate again
170  gPrefs->Write(base + wxT("Migrated"), true);
171  gPrefs->Flush();
172  }
173 
174  return true;
175 }
176 
178 {
179  this->CopyInputTracks(); // Set up mOutputTracks.
180  bool bGoodResult = true;
181  mbDidSomething = false;
182 
183  int count = 0;
184  for( auto track : mOutputTracks->Selected< WaveTrack >() ) {
185  double trackStart = track->GetStartTime();
186  double trackEnd = track->GetEndTime();
187  double t0 = mT0 < trackStart? trackStart: mT0;
188  double t1 = mT1 > trackEnd? trackEnd: mT1;
189 
190  if (t1 > t0) {
191  auto start = track->TimeToLongSamples(t0);
192  auto end = track->TimeToLongSamples(t1);
193  auto len = end - start;
194 
195  if (!ProcessOne(count, track, start, len))
196  {
197  bGoodResult = false;
198  break;
199  }
200  }
201 
202  count++;
203  }
204  if (bGoodResult && !mbDidSomething) // Processing successful, but ineffective.
206  XO("Algorithm not effective on this audio. Nothing changed."),
207  wxOK | wxICON_ERROR );
208 
209  this->ReplaceProcessedTracks(bGoodResult && mbDidSomething);
210  return bGoodResult && mbDidSomething;
211 }
212 
214 {
215  if (len <= windowSize / 2)
216  {
218  XO("Selection must be larger than %d samples.")
219  .Format(windowSize / 2),
220  wxOK | wxICON_ERROR );
221  return false;
222  }
223 
224  auto idealBlockLen = track->GetMaxBlockSize() * 4;
225  if (idealBlockLen % windowSize != 0)
226  idealBlockLen += (windowSize - (idealBlockLen % windowSize));
227 
228  bool bResult = true;
229  decltype(len) s = 0;
230  Floats buffer{ idealBlockLen };
231  Floats datawindow{ windowSize };
232  while ((len - s) > windowSize / 2)
233  {
234  auto block = limitSampleBufferSize( idealBlockLen, len - s );
235 
236  track->GetFloats(buffer.get(), start + s, block);
237 
238  for (decltype(block) i = 0; i + windowSize / 2 < block; i += windowSize / 2)
239  {
240  auto wcopy = std::min( windowSize, block - i );
241 
242  for(decltype(wcopy) j = 0; j < wcopy; j++)
243  datawindow[j] = buffer[i+j];
244  for(auto j = wcopy; j < windowSize; j++)
245  datawindow[j] = 0;
246 
247  mbDidSomething |= RemoveClicks(windowSize, datawindow.get());
248 
249  for(decltype(wcopy) j = 0; j < wcopy; j++)
250  buffer[i+j] = datawindow[j];
251  }
252 
253  if (mbDidSomething) // RemoveClicks() actually did something.
254  track->Set((samplePtr) buffer.get(), floatSample, start + s, block);
255 
256  s += block;
257 
258  if (TrackProgress(count, s.as_double() /
259  len.as_double())) {
260  bResult = false;
261  break;
262  }
263  }
264 
265  return bResult;
266 }
267 
268 bool EffectClickRemoval::RemoveClicks(size_t len, float *buffer)
269 {
270  bool bResult = false; // This effect usually does nothing.
271  size_t i;
272  size_t j;
273  int left = 0;
274 
275  float msw;
276  int ww;
277  int s2 = sep/2;
278  Floats ms_seq{ len };
279  Floats b2{ len };
280 
281  for( i=0; i<len; i++)
282  b2[i] = buffer[i]*buffer[i];
283 
284  /* Shortcut for rms - multiple passes through b2, accumulating
285  * as we go.
286  */
287  for(i=0;i<len;i++)
288  ms_seq[i]=b2[i];
289 
290  for(i=1; (int)i < sep; i *= 2) {
291  for(j=0;j<len-i; j++)
292  ms_seq[j] += ms_seq[j+i];
293  }
294 
295  /* Cheat by truncating sep to next-lower power of two... */
296  sep = i;
297 
298  for( i=0; i<len-sep; i++ ) {
299  ms_seq[i] /= sep;
300  }
301  /* ww runs from about 4 to mClickWidth. wrc is the reciprocal;
302  * chosen so that integer roundoff doesn't clobber us.
303  */
304  int wrc;
305  for(wrc=mClickWidth/4; wrc>=1; wrc /= 2) {
306  ww = mClickWidth/wrc;
307 
308  for( i=0; i<len-sep; i++ ){
309  msw = 0;
310  for( j=0; (int)j<ww; j++) {
311  msw += b2[i+s2+j];
312  }
313  msw /= ww;
314 
315  if(msw >= mThresholdLevel * ms_seq[i]/10) {
316  if( left == 0 ) {
317  left = i+s2;
318  }
319  } else {
320  if(left != 0 && ((int)i-left+s2) <= ww*2) {
321  float lv = buffer[left];
322  float rv = buffer[i+ww+s2];
323  for(j=left; j<i+ww+s2; j++) {
324  bResult = true;
325  buffer[j]= (rv*(j-left) + lv*(i+ww+s2-j))/(float)(i+ww+s2-left);
326  b2[j] = buffer[j]*buffer[j];
327  }
328  left=0;
329  } else if(left != 0) {
330  left = 0;
331  }
332  }
333  }
334  }
335  return bResult;
336 }
337 
339 {
340  S.AddSpace(0, 5);
341  S.SetBorder(10);
342 
343  S.StartMultiColumn(3, wxEXPAND);
344  S.SetStretchyCol(2);
345  {
346  // Threshold
347  mThreshT = S.Id(ID_Thresh)
348  .Validator<IntegerValidator<int>>(
349  &mThresholdLevel, NumValidatorStyle::DEFAULT,
350  MIN_Threshold, MAX_Threshold
351  )
352  .AddTextBox(XXO("&Threshold (lower is more sensitive):"),
353  wxT(""),
354  10);
355 
356  mThreshS = S.Id(ID_Thresh)
357  .Name(XO("Threshold"))
358  .Style(wxSL_HORIZONTAL)
359  .Validator<wxGenericValidator>(&mThresholdLevel)
360  .MinSize( { 150, -1 } )
361  .AddSlider( {}, mThresholdLevel, MAX_Threshold, MIN_Threshold);
362 
363  // Click width
364  mWidthT = S.Id(ID_Width)
365  .Validator<IntegerValidator<int>>(
366  &mClickWidth, NumValidatorStyle::DEFAULT, MIN_Width, MAX_Width)
367  .AddTextBox(XXO("Max &Spike Width (higher is more sensitive):"),
368  wxT(""),
369  10);
370 
371  mWidthS = S.Id(ID_Width)
372  .Name(XO("Max Spike Width"))
373  .Style(wxSL_HORIZONTAL)
374  .Validator<wxGenericValidator>(&mClickWidth)
375  .MinSize( { 150, -1 } )
376  .AddSlider( {}, mClickWidth, MAX_Width, MIN_Width);
377  }
378  S.EndMultiColumn();
379 
380  return;
381 }
382 
384 {
385  if (!mUIParent->TransferDataToWindow())
386  {
387  return false;
388  }
389 
390  return true;
391 }
392 
394 {
395  if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
396  {
397  return false;
398  }
399 
400  return true;
401 }
402 
403 void EffectClickRemoval::OnWidthText(wxCommandEvent & WXUNUSED(evt))
404 {
405  mWidthT->GetValidator()->TransferFromWindow();
406  mWidthS->GetValidator()->TransferToWindow();
407 }
408 
409 void EffectClickRemoval::OnThreshText(wxCommandEvent & WXUNUSED(evt))
410 {
411  mThreshT->GetValidator()->TransferFromWindow();
412  mThreshS->GetValidator()->TransferToWindow();
413 }
414 
415 void EffectClickRemoval::OnWidthSlider(wxCommandEvent & WXUNUSED(evt))
416 {
417  mWidthS->GetValidator()->TransferFromWindow();
418  mWidthT->GetValidator()->TransferToWindow();
419 }
420 
421 void EffectClickRemoval::OnThreshSlider(wxCommandEvent & WXUNUSED(evt))
422 {
423  mThreshS->GetValidator()->TransferFromWindow();
424  mThreshT->GetValidator()->TransferToWindow();
425 }
EffectClickRemoval::PopulateOrExchange
void PopulateOrExchange(ShuttleGui &S) override
Definition: ClickRemoval.cpp:338
TranslatableString
Holds a msgid for the translation catalog; may also bind format arguments.
Definition: TranslatableString.h:32
CommandParameters
CommandParameters, derived from wxFileConfig, is essentially doing the same things as the Shuttle cla...
Definition: EffectAutomationParameters.h:67
WaveTrack
A Track that contains audio waveform data.
Definition: WaveTrack.h:69
EffectTypeProcess
@ EffectTypeProcess
Definition: EffectInterface.h:59
EffectClickRemoval::GetType
EffectType GetType() override
Definition: ClickRemoval.cpp:103
gPrefs
FileConfig * gPrefs
Definition: Prefs.cpp:70
EffectClickRemoval::OnWidthText
void OnWidthText(wxCommandEvent &evt)
Definition: ClickRemoval.cpp:403
Effect::MessageBox
int MessageBox(const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={})
Definition: Effect.cpp:2482
Effect::CopyInputTracks
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:2071
Format
Abstract base class used in importing a file.
Effect::mT1
double mT1
Definition: Effect.h:467
EffectClickRemoval::mThresholdLevel
int mThresholdLevel
Definition: ClickRemoval.h:75
ShuttleGui::AddSpace
wxSizerItem * AddSpace(int width, int height, int prop=0)
Definition: ShuttleGui.cpp:2459
EffectClickRemoval::GetDescription
TranslatableString GetDescription() override
Definition: ClickRemoval.cpp:91
EffectClickRemoval::~EffectClickRemoval
virtual ~EffectClickRemoval()
Definition: ClickRemoval.cpp:80
XO
#define XO(s)
Definition: Internat.h:31
EffectClickRemoval::SetAutomationParameters
bool SetAutomationParameters(CommandParameters &parms) override
Definition: ClickRemoval.cpp:123
ShuttleParams
Shuttle that deals with parameters. This is a base class with lots of virtual functions that do nothi...
Definition: Shuttle.h:62
ID_Width
@ ID_Width
Definition: ClickRemoval.cpp:48
ShuttleGuiBase::EndMultiColumn
void EndMultiColumn()
Definition: ShuttleGui.cpp:1238
EffectClickRemoval::sep
int sep
Definition: ClickRemoval.h:77
Effect::SaveUserPreset
bool SaveUserPreset(const RegistryPath &name) override
Definition: Effect.cpp:570
EffectClickRemoval::DefineParams
bool DefineParams(ShuttleParams &S) override
Definition: ClickRemoval.cpp:109
ComponentInterfaceSymbol
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
Definition: ComponentInterfaceSymbol.h:27
Param
Param(Threshold, int, wxT("Threshold"), 200, 0, 900, 1)
EffectClickRemoval::mThreshT
wxTextCtrl * mThreshT
Definition: ClickRemoval.h:82
ShuttleGui::Id
ShuttleGui & Id(int id)
Definition: ShuttleGui.cpp:2274
EffectClickRemoval::CheckWhetherSkipEffect
bool CheckWhetherSkipEffect() override
Definition: ClickRemoval.cpp:136
EffectClickRemoval
An Effect for removing clicks.
Definition: ClickRemoval.h:27
EffectClickRemoval::windowSize
size_t windowSize
Definition: ClickRemoval.h:74
WaveTrack::Set
void Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len)
Definition: WaveTrack.cpp:2067
floatSample
@ floatSample
Definition: SampleFormat.h:34
anonymous_namespace{ClickRemoval.cpp}::reg
BuiltinEffectsModule::Registration< EffectClickRemoval > reg
Definition: ClickRemoval.cpp:60
EffectClickRemoval::GetSymbol
ComponentInterfaceSymbol GetSymbol() override
Definition: ClickRemoval.cpp:86
ReadAndVerifyInt
#define ReadAndVerifyInt(name)
Definition: Effect.h:632
EffectClickRemoval::TransferDataFromWindow
bool TransferDataFromWindow() override
Definition: ClickRemoval.cpp:393
XXO
#define XXO(s)
Definition: Internat.h:44
Effect::mT0
double mT0
Definition: Effect.h:466
sampleCount::as_double
double as_double() const
Definition: SampleCount.h:45
EffectClickRemoval::Process
bool Process() override
Definition: ClickRemoval.cpp:177
ShuttleGuiBase::StartMultiColumn
void StartMultiColumn(int nCols, int PositionFlags=wxALIGN_LEFT)
Definition: ShuttleGui.cpp:1229
EffectClickRemoval::Symbol
static const ComponentInterfaceSymbol Symbol
Definition: ClickRemoval.h:29
EffectClickRemoval::mClickWidth
int mClickWidth
Definition: ClickRemoval.h:76
EffectClickRemoval::OnWidthSlider
void OnWidthSlider(wxCommandEvent &evt)
Definition: ClickRemoval.cpp:415
EffectClickRemoval::mbDidSomething
bool mbDidSomething
Definition: ClickRemoval.h:73
ID_Thresh
@ ID_Thresh
Definition: ClickRemoval.cpp:47
Effect::ReplaceProcessedTracks
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: Effect.cpp:2193
EffectClickRemoval::TransferDataToWindow
bool TransferDataToWindow() override
Definition: ClickRemoval.cpp:383
ShuttleGui::Validator
ShuttleGui & Validator(const Factory &f)
Definition: ShuttleGui.h:678
Effect::mOutputTracks
std::shared_ptr< TrackList > mOutputTracks
Definition: Effect.h:465
Effect::GetCurrentSettingsGroup
RegistryPath GetCurrentSettingsGroup() override
Definition: Effect.cpp:866
LoadEffects.h
samplePtr
char * samplePtr
Definition: SampleFormat.h:49
min
int min(int a, int b)
Definition: CompareAudioCommand.cpp:106
WaveTrack::GetMaxBlockSize
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:1661
ShuttleGui::Name
ShuttleGui & Name(const TranslatableString &name)
Definition: ShuttleGui.h:663
EffectClickRemoval::mThreshS
wxSlider * mThreshS
Definition: ClickRemoval.h:80
FileConfig::Flush
virtual bool Flush(bool bCurrentOnly=false) wxOVERRIDE
Definition: FileConfig.cpp:143
EffectClickRemoval::RemoveClicks
bool RemoveClicks(size_t len, float *buffer)
Definition: ClickRemoval.cpp:268
BuiltinEffectsModule::Registration
Definition: LoadEffects.h:40
Effect::mUIParent
wxWindow * mUIParent
Definition: Effect.h:478
TaggedIdentifier< ManualPageIDTag >
sampleCount
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
EffectClickRemoval::ProcessOne
bool ProcessOne(int count, WaveTrack *track, sampleCount start, sampleCount len)
Definition: ClickRemoval.cpp:213
EffectClickRemoval::GetAutomationParameters
bool GetAutomationParameters(CommandParameters &parms) override
Definition: ClickRemoval.cpp:115
EffectClickRemoval::mWidthS
wxSlider * mWidthS
Definition: ClickRemoval.h:79
EffectClickRemoval::OnThreshSlider
void OnThreshSlider(wxCommandEvent &evt)
Definition: ClickRemoval.cpp:421
Prefs.h
ShuttleGuiBase::SetBorder
void SetBorder(int Border)
Definition: ShuttleGui.h:489
EffectClickRemoval::OnThreshText
void OnThreshText(wxCommandEvent &evt)
Definition: ClickRemoval.cpp:409
EffectType
EffectType
Definition: EffectInterface.h:55
ShuttleGuiBase::SetStretchyCol
void SetStretchyCol(int i)
Used to modify an already placed FlexGridSizer to make a column stretchy.
Definition: ShuttleGui.cpp:202
limitSampleBufferSize
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:23
WaveTrack::GetFloats
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
Retrieve samples from a track in floating-point format, regardless of the storage format.
Definition: WaveTrack.h:266
END_EVENT_TABLE
END_EVENT_TABLE()
ClickRemoval.h
Effect::TrackProgress
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={})
Definition: Effect.cpp:2025
ArrayOf< float >
EffectClickRemoval::ManualPage
ManualPageID ManualPage() override
Definition: ClickRemoval.cpp:96
ShuttleGui
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:631
EffectClickRemoval::Startup
bool Startup() override
Definition: ClickRemoval.cpp:141
EffectClickRemoval::mWidthT
wxTextCtrl * mWidthT
Definition: ClickRemoval.h:81