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/intl.h>
34#include <wx/slider.h>
35#include <wx/valgen.h>
36
37#include "Prefs.h"
38#include "../ShuttleGui.h"
39#include "../widgets/AudacityMessageBox.h"
40#include "../widgets/valnum.h"
41
42#include "../WaveTrack.h"
43
44enum
45{
46 ID_Thresh = 10000,
48};
49
51{
54 > parameters;
55 return parameters;
56}
57
59{ XO("Click Removal") };
60
62
63BEGIN_EVENT_TABLE(EffectClickRemoval, wxEvtHandler)
69
71{
72 Parameters().Reset(*this);
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// Effect implementation
109
111{
112 return ((mClickWidth == 0) || (mThresholdLevel == 0));
113}
114
116{
117 this->CopyInputTracks(); // Set up mOutputTracks.
118 bool bGoodResult = true;
119 mbDidSomething = false;
120
121 int count = 0;
122 for( auto track : mOutputTracks->Selected< WaveTrack >() ) {
123 double trackStart = track->GetStartTime();
124 double trackEnd = track->GetEndTime();
125 double t0 = mT0 < trackStart? trackStart: mT0;
126 double t1 = mT1 > trackEnd? trackEnd: mT1;
127
128 if (t1 > t0) {
129 auto start = track->TimeToLongSamples(t0);
130 auto end = track->TimeToLongSamples(t1);
131 auto len = end - start;
132
133 if (!ProcessOne(count, track, start, len))
134 {
135 bGoodResult = false;
136 break;
137 }
138 }
139
140 count++;
141 }
142 if (bGoodResult && !mbDidSomething) // Processing successful, but ineffective.
144 XO("Algorithm not effective on this audio. Nothing changed."),
145 wxOK | wxICON_ERROR );
146
147 this->ReplaceProcessedTracks(bGoodResult && mbDidSomething);
148 return bGoodResult && mbDidSomething;
149}
150
152{
153 if (len <= windowSize / 2)
154 {
156 XO("Selection must be larger than %d samples.")
157 .Format(windowSize / 2),
158 wxOK | wxICON_ERROR );
159 return false;
160 }
161
162 auto idealBlockLen = track->GetMaxBlockSize() * 4;
163 if (idealBlockLen % windowSize != 0)
164 idealBlockLen += (windowSize - (idealBlockLen % windowSize));
165
166 bool bResult = true;
167 decltype(len) s = 0;
168 Floats buffer{ idealBlockLen };
169 Floats datawindow{ windowSize };
170 while ((len - s) > windowSize / 2)
171 {
172 auto block = limitSampleBufferSize( idealBlockLen, len - s );
173
174 track->GetFloats(buffer.get(), start + s, block);
175
176 for (decltype(block) i = 0; i + windowSize / 2 < block; i += windowSize / 2)
177 {
178 auto wcopy = std::min( windowSize, block - i );
179
180 for(decltype(wcopy) j = 0; j < wcopy; j++)
181 datawindow[j] = buffer[i+j];
182 for(auto j = wcopy; j < windowSize; j++)
183 datawindow[j] = 0;
184
185 mbDidSomething |= RemoveClicks(windowSize, datawindow.get());
186
187 for(decltype(wcopy) j = 0; j < wcopy; j++)
188 buffer[i+j] = datawindow[j];
189 }
190
191 if (mbDidSomething) // RemoveClicks() actually did something.
192 track->Set((samplePtr) buffer.get(), floatSample, start + s, block);
193
194 s += block;
195
196 if (TrackProgress(count, s.as_double() /
197 len.as_double())) {
198 bResult = false;
199 break;
200 }
201 }
202
203 return bResult;
204}
205
206bool EffectClickRemoval::RemoveClicks(size_t len, float *buffer)
207{
208 bool bResult = false; // This effect usually does nothing.
209 size_t i;
210 size_t j;
211 int left = 0;
212
213 float msw;
214 int ww;
215 int s2 = sep/2;
216 Floats ms_seq{ len };
217 Floats b2{ len };
218
219 for( i=0; i<len; i++)
220 b2[i] = buffer[i]*buffer[i];
221
222 /* Shortcut for rms - multiple passes through b2, accumulating
223 * as we go.
224 */
225 for(i=0;i<len;i++)
226 ms_seq[i]=b2[i];
227
228 for(i=1; (int)i < sep; i *= 2) {
229 for(j=0;j<len-i; j++)
230 ms_seq[j] += ms_seq[j+i];
231 }
232
233 /* Cheat by truncating sep to next-lower power of two... */
234 sep = i;
235
236 for( i=0; i<len-sep; i++ ) {
237 ms_seq[i] /= sep;
238 }
239 /* ww runs from about 4 to mClickWidth. wrc is the reciprocal;
240 * chosen so that integer roundoff doesn't clobber us.
241 */
242 int wrc;
243 for(wrc=mClickWidth/4; wrc>=1; wrc /= 2) {
244 ww = mClickWidth/wrc;
245
246 for( i=0; i<len-sep; i++ ){
247 msw = 0;
248 for( j=0; (int)j<ww; j++) {
249 msw += b2[i+s2+j];
250 }
251 msw /= ww;
252
253 if(msw >= mThresholdLevel * ms_seq[i]/10) {
254 if( left == 0 ) {
255 left = i+s2;
256 }
257 } else {
258 if(left != 0 && ((int)i-left+s2) <= ww*2) {
259 float lv = buffer[left];
260 float rv = buffer[i+ww+s2];
261 for(j=left; j<i+ww+s2; j++) {
262 bResult = true;
263 buffer[j]= (rv*(j-left) + lv*(i+ww+s2-j))/(float)(i+ww+s2-left);
264 b2[j] = buffer[j]*buffer[j];
265 }
266 left=0;
267 } else if(left != 0) {
268 left = 0;
269 }
270 }
271 }
272 }
273 return bResult;
274}
275
276std::unique_ptr<EffectUIValidator> EffectClickRemoval::PopulateOrExchange(
278{
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
321void EffectClickRemoval::OnWidthText(wxCommandEvent & WXUNUSED(evt))
322{
323 mWidthT->GetValidator()->TransferFromWindow();
324 mWidthS->GetValidator()->TransferToWindow();
325}
326
327void EffectClickRemoval::OnThreshText(wxCommandEvent & WXUNUSED(evt))
328{
329 mThreshT->GetValidator()->TransferFromWindow();
330 mThreshS->GetValidator()->TransferToWindow();
331}
332
333void EffectClickRemoval::OnWidthSlider(wxCommandEvent & WXUNUSED(evt))
334{
335 mWidthS->GetValidator()->TransferFromWindow();
336 mWidthT->GetValidator()->TransferToWindow();
337}
338
339void EffectClickRemoval::OnThreshSlider(wxCommandEvent & WXUNUSED(evt))
340{
341 mThreshS->GetValidator()->TransferFromWindow();
342 mThreshT->GetValidator()->TransferToWindow();
343}
END_EVENT_TABLE()
@ ID_Width
@ ID_Thresh
int min(int a, int b)
EffectType
@ EffectTypeProcess
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:23
@ floatSample
Definition: SampleFormat.h:34
char * samplePtr
Definition: SampleFormat.h:49
#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:107
std::shared_ptr< TrackList > mOutputTracks
Definition: EffectBase.h:105
double mT0
Definition: EffectBase.h:106
void ReplaceProcessedTracks(const bool bGoodResult)
Definition: EffectBase.cpp:236
An Effect for removing clicks.
Definition: ClickRemoval.h:28
bool RemoveClicks(size_t len, float *buffer)
wxTextCtrl * mThreshT
Definition: ClickRemoval.h:78
static constexpr EffectParameter Width
Definition: ClickRemoval.h:85
const EffectParameterMethods & Parameters() const override
void OnWidthSlider(wxCommandEvent &evt)
std::unique_ptr< EffectUIValidator > PopulateOrExchange(ShuttleGui &S, EffectInstance &instance, EffectSettingsAccess &access) override
Add controls to effect panel; always succeeds.
bool CheckWhetherSkipEffect(const EffectSettings &settings) const override
After Init(), tell whether Process() should be skipped.
void OnThreshSlider(wxCommandEvent &evt)
EffectType GetType() const override
Type determines how it behaves.
wxTextCtrl * mWidthT
Definition: ClickRemoval.h:77
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
virtual ~EffectClickRemoval()
static const ComponentInterfaceSymbol Symbol
Definition: ClickRemoval.h:32
wxSlider * mThreshS
Definition: ClickRemoval.h:76
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
Actually do the effect here.
static constexpr EffectParameter Threshold
Definition: ClickRemoval.h:83
wxSlider * mWidthS
Definition: ClickRemoval.h:75
void OnThreshText(wxCommandEvent &evt)
void CopyInputTracks(bool allSyncLockSelected=false)
Definition: Effect.cpp:741
int MessageBox(const TranslatableString &message, long style=DefaultMessageBoxStyle, const TranslatableString &titleStr={}) const
Definition: Effect.cpp:871
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:693
Performs effect computation.
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:67
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:1806
void Set(constSamplePtr buffer, sampleFormat format, sampleCount start, size_t len)
Definition: WaveTrack.cpp:2195
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:18
double as_double() const
Definition: SampleCount.h:45
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.