Audacity 3.2.0
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/slider.h>
34#include <wx/valgen.h>
35
36#include "Prefs.h"
37#include "../ShuttleGui.h"
38#include "../widgets/AudacityMessageBox.h"
39#include "../widgets/valnum.h"
40
41#include "../WaveTrack.h"
42
43enum
44{
45 ID_Thresh = 10000,
47};
48
50{
53 > parameters;
54 return parameters;
55}
56
58{ XO("Click Removal") };
59
61
62BEGIN_EVENT_TABLE(EffectClickRemoval, wxEvtHandler)
68
70{
71 Parameters().Reset(*this);
72
73 SetLinearEffectFlag(false);
74
75 windowSize = 8192;
76 sep = 2049;
77}
78
80{
81}
82
83// ComponentInterface implementation
84
86{
87 return Symbol;
88}
89
91{
92 return XO("Click Removal is designed to remove clicks on audio tracks");
93}
94
96{
97 return L"Click_Removal";
98}
99
100// EffectDefinitionInterface implementation
101
103{
104 return EffectTypeProcess;
105}
106
107// Effect implementation
108
110{
111 return ((mClickWidth == 0) || (mThresholdLevel == 0));
112}
113
115{
116 this->CopyInputTracks(); // Set up mOutputTracks.
117 bool bGoodResult = true;
118 mbDidSomething = false;
119
120 int count = 0;
121 for( auto track : mOutputTracks->Selected< WaveTrack >() ) {
122 double trackStart = track->GetStartTime();
123 double trackEnd = track->GetEndTime();
124 double t0 = mT0 < trackStart? trackStart: mT0;
125 double t1 = mT1 > trackEnd? trackEnd: mT1;
126
127 if (t1 > t0) {
128 auto start = track->TimeToLongSamples(t0);
129 auto end = track->TimeToLongSamples(t1);
130 auto len = end - start;
131
132 if (!ProcessOne(count, track, start, len))
133 {
134 bGoodResult = false;
135 break;
136 }
137 }
138
139 count++;
140 }
141 if (bGoodResult && !mbDidSomething) // Processing successful, but ineffective.
143 XO("Algorithm not effective on this audio. Nothing changed."),
144 wxOK | wxICON_ERROR );
145
146 this->ReplaceProcessedTracks(bGoodResult && mbDidSomething);
147 return bGoodResult && mbDidSomething;
148}
149
151{
152 if (len <= windowSize / 2)
153 {
155 XO("Selection must be larger than %d samples.")
156 .Format(windowSize / 2),
157 wxOK | wxICON_ERROR );
158 return false;
159 }
160
161 auto idealBlockLen = track->GetMaxBlockSize() * 4;
162 if (idealBlockLen % windowSize != 0)
163 idealBlockLen += (windowSize - (idealBlockLen % windowSize));
164
165 bool bResult = true;
166 decltype(len) s = 0;
167 Floats buffer{ idealBlockLen };
168 Floats datawindow{ windowSize };
169 while ((len - s) > windowSize / 2)
170 {
171 auto block = limitSampleBufferSize( idealBlockLen, len - s );
172
173 track->GetFloats(buffer.get(), start + s, block);
174
175 for (decltype(block) i = 0; i + windowSize / 2 < block; i += windowSize / 2)
176 {
177 auto wcopy = std::min( windowSize, block - i );
178
179 for(decltype(wcopy) j = 0; j < wcopy; j++)
180 datawindow[j] = buffer[i+j];
181 for(auto j = wcopy; j < windowSize; j++)
182 datawindow[j] = 0;
183
184 mbDidSomething |= RemoveClicks(windowSize, datawindow.get());
185
186 for(decltype(wcopy) j = 0; j < wcopy; j++)
187 buffer[i+j] = datawindow[j];
188 }
189
190 if (mbDidSomething) // RemoveClicks() actually did something.
191 track->Set((samplePtr) buffer.get(), floatSample, start + s, block);
192
193 s += block;
194
195 if (TrackProgress(count, s.as_double() /
196 len.as_double())) {
197 bResult = false;
198 break;
199 }
200 }
201
202 return bResult;
203}
204
205bool EffectClickRemoval::RemoveClicks(size_t len, float *buffer)
206{
207 bool bResult = false; // This effect usually does nothing.
208 size_t i;
209 size_t j;
210 int left = 0;
211
212 float msw;
213 int ww;
214 int s2 = sep/2;
215 Floats ms_seq{ len };
216 Floats b2{ len };
217
218 for( i=0; i<len; i++)
219 b2[i] = buffer[i]*buffer[i];
220
221 /* Shortcut for rms - multiple passes through b2, accumulating
222 * as we go.
223 */
224 for(i=0;i<len;i++)
225 ms_seq[i]=b2[i];
226
227 for(i=1; (int)i < sep; i *= 2) {
228 for(j=0;j<len-i; j++)
229 ms_seq[j] += ms_seq[j+i];
230 }
231
232 /* Cheat by truncating sep to next-lower power of two... */
233 sep = i;
234
235 for( i=0; i<len-sep; i++ ) {
236 ms_seq[i] /= sep;
237 }
238 /* ww runs from about 4 to mClickWidth. wrc is the reciprocal;
239 * chosen so that integer roundoff doesn't clobber us.
240 */
241 int wrc;
242 for(wrc=mClickWidth/4; wrc>=1; wrc /= 2) {
243 ww = mClickWidth/wrc;
244
245 for( i=0; i<len-sep; i++ ){
246 msw = 0;
247 for( j=0; (int)j<ww; j++) {
248 msw += b2[i+s2+j];
249 }
250 msw /= ww;
251
252 if(msw >= mThresholdLevel * ms_seq[i]/10) {
253 if( left == 0 ) {
254 left = i+s2;
255 }
256 } else {
257 if(left != 0 && ((int)i-left+s2) <= ww*2) {
258 float lv = buffer[left];
259 float rv = buffer[i+ww+s2];
260 for(j=left; j<i+ww+s2; j++) {
261 bResult = true;
262 buffer[j]= (rv*(j-left) + lv*(i+ww+s2-j))/(float)(i+ww+s2-left);
263 b2[j] = buffer[j]*buffer[j];
264 }
265 left=0;
266 } else if(left != 0) {
267 left = 0;
268 }
269 }
270 }
271 }
272 return bResult;
273}
274
275std::unique_ptr<EffectUIValidator> EffectClickRemoval::PopulateOrExchange(
277 const EffectOutputs *)
278{
279 mUIParent = S.GetParent();
280 S.AddSpace(0, 5);
281 S.SetBorder(10);
282
283 S.StartMultiColumn(3, wxEXPAND);
284 S.SetStretchyCol(2);
285 {
286 // Threshold
287 mThreshT = S.Id(ID_Thresh)
288 .Validator<IntegerValidator<int>>(
289 &mThresholdLevel, NumValidatorStyle::DEFAULT,
291 .AddTextBox(XXO("&Threshold (lower is more sensitive):"),
292 wxT(""),
293 10);
294
295 mThreshS = S.Id(ID_Thresh)
296 .Name(XO("Threshold"))
297 .Style(wxSL_HORIZONTAL)
298 .Validator<wxGenericValidator>(&mThresholdLevel)
299 .MinSize( { 150, -1 } )
300 .AddSlider( {}, mThresholdLevel, Threshold.max, Threshold.min);
301
302 // Click width
303 mWidthT = S.Id(ID_Width)
304 .Validator<IntegerValidator<int>>(
305 &mClickWidth, NumValidatorStyle::DEFAULT, Width.min, Width.max)
306 .AddTextBox(XXO("Max &Spike Width (higher is more sensitive):"),
307 wxT(""),
308 10);
309
310 mWidthS = S.Id(ID_Width)
311 .Name(XO("Max Spike Width"))
312 .Style(wxSL_HORIZONTAL)
313 .Validator<wxGenericValidator>(&mClickWidth)
314 .MinSize( { 150, -1 } )
315 .AddSlider( {}, mClickWidth, Width.max, Width.min);
316 }
317 S.EndMultiColumn();
318
319 return nullptr;
320}
321
323{
324 if (!mUIParent->TransferDataToWindow())
325 {
326 return false;
327 }
328
329 return true;
330}
331
333{
334 if (!mUIParent->Validate() || !mUIParent->TransferDataFromWindow())
335 {
336 return false;
337 }
338
339 return true;
340}
341
342void EffectClickRemoval::OnWidthText(wxCommandEvent & WXUNUSED(evt))
343{
344 mWidthT->GetValidator()->TransferFromWindow();
345 mWidthS->GetValidator()->TransferToWindow();
346}
347
348void EffectClickRemoval::OnThreshText(wxCommandEvent & WXUNUSED(evt))
349{
350 mThreshT->GetValidator()->TransferFromWindow();
351 mThreshS->GetValidator()->TransferToWindow();
352}
353
354void EffectClickRemoval::OnWidthSlider(wxCommandEvent & WXUNUSED(evt))
355{
356 mWidthS->GetValidator()->TransferFromWindow();
357 mWidthT->GetValidator()->TransferToWindow();
358}
359
360void EffectClickRemoval::OnThreshSlider(wxCommandEvent & WXUNUSED(evt))
361{
362 mThreshS->GetValidator()->TransferFromWindow();
363 mThreshT->GetValidator()->TransferToWindow();
364}
wxT("CloseDown"))
END_EVENT_TABLE()
@ ID_Width
@ ID_Thresh
int min(int a, int b)
EffectType
@ EffectTypeProcess
XO("Cut/Copy/Paste")
XXO("&Cut/Copy/Paste Toolbar")
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
char * samplePtr
Definition: SampleFormat.h:55
#define S(N)
Definition: ToChars.cpp:64
Generates EffectParameterMethods overrides from variadic template arguments.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
double mT1
Definition: EffectBase.h:109
std::shared_ptr< TrackList > mOutputTracks
Definition: EffectBase.h:107
double mT0
Definition: EffectBase.h:108
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: EffectBase.cpp:241
An Effect for removing clicks.
Definition: ClickRemoval.h:28
bool RemoveClicks(size_t len, float *buffer)
wxTextCtrl * mThreshT
Definition: ClickRemoval.h:82
static constexpr EffectParameter Width
Definition: ClickRemoval.h:89
const EffectParameterMethods & Parameters() const override
void OnWidthSlider(wxCommandEvent &evt)
bool CheckWhetherSkipEffect(const EffectSettings &settings) const override
After Init(), tell whether Process() should be skipped.
std::unique_ptr< EffectUIValidator > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access, const EffectOutputs *pOutputs) override
Add controls to effect panel; always succeeds.
wxWeakRef< wxWindow > mUIParent
Definition: ClickRemoval.h:69
void OnThreshSlider(wxCommandEvent &evt)
EffectType GetType() const override
Type determines how it behaves.
wxTextCtrl * mWidthT
Definition: ClickRemoval.h:81
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
virtual ~EffectClickRemoval()
bool TransferDataToWindow(const EffectSettings &settings) override
Update controls for the settings.
static const ComponentInterfaceSymbol Symbol
Definition: ClickRemoval.h:32
wxSlider * mThreshS
Definition: ClickRemoval.h:80
bool ProcessOne(int count, WaveTrack *track, sampleCount start, sampleCount len)
TranslatableString GetDescription() const override
ComponentInterfaceSymbol GetSymbol() const override
void OnWidthText(wxCommandEvent &evt)
bool Process(EffectInstance &instance, EffectSettings &settings) override
static constexpr EffectParameter Threshold
Definition: ClickRemoval.h:87
wxSlider * mWidthS
Definition: ClickRemoval.h:79
void OnThreshText(wxCommandEvent &evt)
bool TransferDataFromWindow(EffectSettings &settings) override
Update the given settings from controls.
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:677
int MessageBox(const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={}) const
Definition: Effect.cpp:807
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:627
Performs effect computation.
Hold values to send to effect output meters.
Interface for manipulations of an Effect's settings.
Abstract base class used in importing a file.
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: SampleTrack.h:71
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
Definition: WaveTrack.h:57
size_t GetMaxBlockSize() const override
This returns a nonnegative number of samples meant to size a memory buffer.
Definition: WaveTrack.cpp:1800
void Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Definition: WaveTrack.cpp:2191
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
double as_double() const
Definition: SampleCount.h:46
auto end(const Ptr< Type, BaseDeleter > &p)
Enables range-for.
Definition: PackedArray.h:159
BuiltinEffectsModule::Registration< EffectClickRemoval > reg
const Type min
Minimum value.
Definition: Shuttle.h:30
const Type max
Maximum value.
Definition: Shuttle.h:31
Externalized state of a plug-in.