Audacity 3.2.0
ClickRemovalBase.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ClickRemovalBase.cpp
6
7 Craig DeForest
8
9*******************************************************************//*******************************************************************/
26#include "ClickRemovalBase.h"
27#include "BasicUI.h"
28#include "EffectOutputTracks.h"
29#include "Prefs.h"
30#include "ShuttleAutomation.h"
31#include "WaveTrack.h"
32#include <cmath>
33
35{
37 return parameters;
38}
39
41
43{
44 Parameters().Reset(*this);
45
47
48 windowSize = 8192;
49 sep = 2049;
50}
51
53{
54}
55
56// ComponentInterface implementation
57
59{
60 return Symbol;
61}
62
64{
65 return XO("Click Removal is designed to remove clicks on audio tracks");
66}
67
69{
70 return L"Click_Removal";
71}
72
73// EffectDefinitionInterface implementation
74
76{
77 return EffectTypeProcess;
78}
79
80// Effect implementation
81
83{
84 return ((mClickWidth == 0) || (mThresholdLevel == 0));
85}
86
88{
89 EffectOutputTracks outputs { *mTracks, GetType(), { { mT0, mT1 } } };
90 bool bGoodResult = true;
91 mbDidSomething = false;
92
93 int count = 0;
94 for (auto track : outputs.Get().Selected<WaveTrack>())
95 {
96 double trackStart = track->GetStartTime();
97 double trackEnd = track->GetEndTime();
98 double t0 = std::max(mT0, trackStart);
99 double t1 = std::min(trackEnd, mT1);
100
101 if (t1 > t0)
102 {
103 auto start = track->TimeToLongSamples(t0);
104 auto end = track->TimeToLongSamples(t1);
105 auto len = end - start;
106 for (const auto pChannel : track->Channels())
107 if (!ProcessOne(count++, *pChannel, start, len))
108 {
109 bGoodResult = false;
110 goto done;
111 }
112 }
113 }
114done:
115 if (bGoodResult && !mbDidSomething) // Processing successful, but
116 // ineffective.
117 {
118 using namespace BasicUI;
120 XO("Algorithm not effective on this audio. Nothing changed."),
121 MessageBoxOptions {}.IconStyle(Icon::Error));
122 }
123
124 if (bGoodResult && mbDidSomething)
125 outputs.Commit();
126
127 return bGoodResult && mbDidSomething;
128}
129
131 int count, WaveChannel& track, sampleCount start, sampleCount len)
132{
133 if (len <= windowSize / 2)
134 {
135 using namespace BasicUI;
137 XO("Selection must be larger than %d samples.").Format(windowSize / 2),
138 MessageBoxOptions {}.IconStyle(Icon::Error));
139 return false;
140 }
141
142 auto idealBlockLen = track.GetMaxBlockSize() * 4;
143 if (idealBlockLen % windowSize != 0)
144 idealBlockLen += (windowSize - (idealBlockLen % windowSize));
145
146 bool bResult = true;
147 decltype(len) s = 0;
148 Floats buffer { idealBlockLen };
149 Floats datawindow { windowSize };
150 while ((len - s) > windowSize / 2)
151 {
152 auto block = limitSampleBufferSize(idealBlockLen, len - s);
153 track.GetFloats(buffer.get(), start + s, block);
154 for (decltype(block) i = 0; i + windowSize / 2 < block;
155 i += windowSize / 2)
156 {
157 auto wcopy = std::min(windowSize, block - i);
158 for (decltype(wcopy) j = 0; j < wcopy; ++j)
159 datawindow[j] = buffer[i + j];
160 for (auto j = wcopy; j < windowSize; ++j)
161 datawindow[j] = 0;
162 mbDidSomething |= RemoveClicks(windowSize, datawindow.get());
163 for (decltype(wcopy) j = 0; j < wcopy; ++j)
164 buffer[i + j] = datawindow[j];
165 }
166
167 if (mbDidSomething)
168 {
169 // RemoveClicks() actually did something.
170 if (!track.SetFloats(buffer.get(), start + s, block))
171 {
172 bResult = false;
173 break;
174 }
175 }
176 s += block;
177 if (TrackProgress(count, s.as_double() / len.as_double()))
178 {
179 bResult = false;
180 break;
181 }
182 }
183 return bResult;
184}
185
186bool ClickRemovalBase::RemoveClicks(size_t len, float* buffer)
187{
188 bool bResult = false; // This effect usually does nothing.
189 size_t i;
190 size_t j;
191 int left = 0;
192
193 float msw;
194 int ww;
195 int s2 = sep / 2;
196 Floats ms_seq { len };
197 Floats b2 { len };
198
199 for (i = 0; i < len; i++)
200 b2[i] = buffer[i] * buffer[i];
201
202 /* Shortcut for rms - multiple passes through b2, accumulating
203 * as we go.
204 */
205 for (i = 0; i < len; i++)
206 ms_seq[i] = b2[i];
207
208 for (i = 1; (int)i < sep; i *= 2)
209 {
210 for (j = 0; j < len - i; j++)
211 ms_seq[j] += ms_seq[j + i];
212 }
213
214 /* Cheat by truncating sep to next-lower power of two... */
215 sep = i;
216
217 for (i = 0; i < len - sep; i++)
218 {
219 ms_seq[i] /= sep;
220 }
221 /* ww runs from about 4 to mClickWidth. wrc is the reciprocal;
222 * chosen so that integer roundoff doesn't clobber us.
223 */
224 int wrc;
225 for (wrc = mClickWidth / 4; wrc >= 1; wrc /= 2)
226 {
227 ww = mClickWidth / wrc;
228
229 for (i = 0; i < len - sep; i++)
230 {
231 msw = 0;
232 for (j = 0; (int)j < ww; j++)
233 {
234 msw += b2[i + s2 + j];
235 }
236 msw /= ww;
237
238 if (msw >= mThresholdLevel * ms_seq[i] / 10)
239 {
240 if (left == 0)
241 {
242 left = i + s2;
243 }
244 }
245 else
246 {
247 if (left != 0 && ((int)i - left + s2) <= ww * 2)
248 {
249 float lv = buffer[left];
250 float rv = buffer[i + ww + s2];
251 for (j = left; j < i + ww + s2; j++)
252 {
253 bResult = true;
254 buffer[j] = (rv * (j - left) + lv * (i + ww + s2 - j)) /
255 (float)(i + ww + s2 - left);
256 b2[j] = buffer[j] * buffer[j];
257 }
258 left = 0;
259 }
260 else if (left != 0)
261 {
262 left = 0;
263 }
264 }
265 }
266 }
267 return bResult;
268}
Toolkit-neutral facade for basic user interface services.
int min(int a, int b)
EffectType
@ EffectTypeProcess
XO("Cut/Copy/Paste")
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
Generates EffectParameterMethods overrides from variadic template arguments.
bool CheckWhetherSkipEffect(const EffectSettings &settings) const override
After Init(), tell whether Process() should be skipped.
static const ComponentInterfaceSymbol Symbol
virtual ~ClickRemovalBase()
bool ProcessOne(int count, WaveChannel &track, sampleCount start, sampleCount len)
bool RemoveClicks(size_t len, float *buffer)
EffectType GetType() const override
Type determines how it behaves.
const EffectParameterMethods & Parameters() const override
TranslatableString GetDescription() const override
bool Process(EffectInstance &instance, EffectSettings &settings) override
ComponentInterfaceSymbol GetSymbol() const override
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
double mT1
Definition: EffectBase.h:123
void SetLinearEffectFlag(bool linearEffectFlag)
Definition: EffectBase.cpp:210
std::shared_ptr< TrackList > mTracks
Definition: EffectBase.h:116
double mT0
Definition: EffectBase.h:122
bool TrackProgress(int whichTrack, double frac, const TranslatableString &={}) const
Definition: Effect.cpp:343
Performs effect computation.
Use this object to copy the input tracks to tentative outputTracks.
Interface for manipulations of an Effect's settings.
virtual void Reset(Effect &effect) const =0
Abstract base class used in importing a file.
Holds a msgid for the translation catalog; may also bind format arguments.
bool SetFloats(const float *buffer, sampleCount start, size_t len, sampleFormat effectiveFormat=widestSampleFormat)
Random-access assignment of a range of samples.
Definition: WaveTrack.h:162
bool GetFloats(float *buffer, sampleCount start, size_t len, fillFormat fill=FillFormat::fillZero, bool mayThrow=true, sampleCount *pNumWithinClips=nullptr) const
"narrow" overload fetches from the unique channel
Definition: WaveTrack.h:129
size_t GetMaxBlockSize() const
Definition: WaveTrack.h:859
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
double as_double() const
Definition: SampleCount.h:46
MessageBoxResult ShowMessageBox(const TranslatableString &message, MessageBoxOptions options={})
Show a modal message box with either Ok or Yes and No, and optionally Cancel.
Definition: BasicUI.h:287
const char * end(const char *str) noexcept
Definition: StringUtils.h:106
MessageBoxOptions && IconStyle(Icon style) &&
Definition: BasicUI.h:104
Externalized state of a plug-in.