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