Audacity 3.2.0
LegacyCompressorBase.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 LegacyCompressorBase.cpp
6
7 Dominic Mazzoni
8 Martyn Shaw
9 Steve Jolly
10
11*******************************************************************//*******************************************************************/
22#include "Prefs.h"
23#include "WaveTrack.h"
24#include <cmath>
25
27{
28 static CapturedParameters<
30 Ratio, // positive number > 1.0
31 AttackTime, // seconds
32 ReleaseTime, // seconds
34 parameters;
35 return parameters;
36}
37
38//----------------------------------------------------------------------------
39// LegacyCompressorBase
40//----------------------------------------------------------------------------
41
43 "Legacy Compressor") };
44
46{
47 Parameters().Reset(*this);
48
49 mThreshold = 0.25;
50 mNoiseFloor = 0.01;
51 mCompression = 0.5;
52 mFollowLen = 0;
53
55}
56
58{
59}
60
61// ComponentInterface implementation
62
64{
65 return Symbol;
66}
67
69{
70 return XO("Compresses the dynamic range of audio");
71}
72
74{
75 return L"Compressor";
76}
77
78// EffectDefinitionInterface implementation
79
81{
82 return EffectTypeProcess;
83}
84
85// EffectTwoPassSimpleMono implementation
86
88{
91 mNoiseCounter = 100;
92
95 mDecayFactor = exp(log(mThreshold) / (mCurRate * mDecayTime + 0.5));
96
97 if (mRatio > 1)
98 mCompression = 1.0 - 1.0 / mRatio;
99 else
100 mCompression = 0.0;
101
103
104 mCircleSize = 100;
106 mCirclePos = 0;
107 mRMSSum = 0.0;
108
109 return true;
110}
111
113{
114 mMax = 0.0;
115 if (!mNormalize)
117
118 // Find the maximum block length required for any track
119 size_t maxlen = inputTracks()->Selected<const WaveTrack>().max(
121 mFollow1.reset();
122 mFollow2.reset();
123 // Allocate buffers for the envelope
124 if (maxlen > 0)
125 {
126 mFollow1.reinit(maxlen);
127 mFollow2.reinit(maxlen);
128 }
129 mFollowLen = maxlen;
130
131 return true;
132}
133
135{
136 // Actually, this should not even be called, because we call
137 // DisableSecondPass() before, if mNormalize is false.
138 return mNormalize;
139}
140
141// Process the input with 2 buffers available at a time
142// buffer1 will be written upon return
143// buffer2 will be passed as buffer1 on the next call
145 float* buffer1, size_t len1, float* buffer2, size_t len2)
146{
147 // If buffers are bigger than allocated, then abort
148 // (this should never happen, but if it does, we don't want to crash)
149 if ((len1 > mFollowLen) || (len2 > mFollowLen))
150 return false;
151
152 // This makes sure that the initial value is well-chosen
153 // buffer1 == NULL on the first and only the first call
154 if (buffer1 == NULL)
155 {
156 // Initialize the mLastLevel to the peak level in the first buffer
157 // This avoids problems with large spike events near the beginning of the
158 // track
160 for (size_t i = 0; i < len2; i++)
161 {
162 if (mLastLevel < fabs(buffer2[i]))
163 mLastLevel = fabs(buffer2[i]);
164 }
165 }
166
167 // buffer2 is NULL on the last and only the last call
168 if (buffer2 != NULL)
169 {
170 Follow(buffer2, mFollow2.get(), len2, mFollow1.get(), len1);
171 }
172
173 if (buffer1 != NULL)
174 {
175 for (size_t i = 0; i < len1; i++)
176 {
177 buffer1[i] = DoCompression(buffer1[i], mFollow1[i]);
178 }
179 }
180
181#if 0
182 // Copy the envelope over the track data (for debug purposes)
183 memcpy(buffer1, mFollow1, len1*sizeof(float));
184#endif
185
186 // Rotate the buffer pointers
187 mFollow1.swap(mFollow2);
188
189 return true;
190}
191
192bool LegacyCompressorBase::ProcessPass2(float* buffer, size_t len)
193{
194 if (mMax != 0)
195 {
196 for (size_t i = 0; i < len; i++)
197 buffer[i] /= mMax;
198 }
199
200 return true;
201}
202
204{
205 // Recompute the RMS sum periodically to prevent accumulation of rounding
206 // errors during long waveforms
207 mRMSSum = 0;
208 for (size_t i = 0; i < mCircleSize; i++)
209 mRMSSum += mCircle[i];
210}
211
213{
214 float level;
215
216 // Calculate current level from root-mean-squared of
217 // circular buffer ("RMS")
219 mCircle[mCirclePos] = value * value;
221 level = sqrt(mRMSSum / mCircleSize);
223
224 return level;
225}
226
228 float* buffer, float* env, size_t len, float* previous, size_t previous_len)
229{
230 /*
231
232 "Follow"ing algorithm by Roger B. Dannenberg, taken from
233 Nyquist. His description follows. -DMM
234
235 Description: this is a sophisticated envelope follower.
236 The input is an envelope, e.g. something produced with
237 the AVG function. The purpose of this function is to
238 generate a smooth envelope that is generally not less
239 than the input signal. In other words, we want to "ride"
240 the peaks of the signal with a smooth function. The
241 algorithm is as follows: keep a current output value
242 (called the "value"). The value is allowed to increase
243 by at most rise_factor and decrease by at most fall_factor.
244 Therefore, the next value should be between
245 value * rise_factor and value * fall_factor. If the input
246 is in this range, then the next value is simply the input.
247 If the input is less than value * fall_factor, then the
248 next value is just value * fall_factor, which will be greater
249 than the input signal. If the input is greater than value *
250 rise_factor, then we compute a rising envelope that meets
251 the input value by working bacwards in time, changing the
252 previous values to input / rise_factor, input / rise_factor^2,
253 input / rise_factor^3, etc. until this NEW envelope intersects
254 the previously computed values. There is only a limited buffer
255 in which we can work backwards, so if the NEW envelope does not
256 intersect the old one, then make yet another pass, this time
257 from the oldest buffered value forward, increasing on each
258 sample by rise_factor to produce a maximal envelope. This will
259 still be less than the input.
260
261 The value has a lower limit of floor to make sure value has a
262 reasonable positive value from which to begin an attack.
263 */
264 double level, last;
265
266 if (!mUsePeak)
267 {
268 // Update RMS sum directly from the circle buffer
269 // to avoid accumulation of rounding errors
271 }
272 // First apply a peak detect with the requested decay rate
273 last = mLastLevel;
274 for (size_t i = 0; i < len; i++)
275 {
276 if (mUsePeak)
277 level = fabs(buffer[i]);
278 else // use RMS
279 level = AvgCircle(buffer[i]);
280 // Don't increase gain when signal is continuously below the noise floor
281 if (level < mNoiseFloor)
282 {
284 }
285 else
286 {
287 mNoiseCounter = 0;
288 }
289 if (mNoiseCounter < 100)
290 {
291 last *= mDecayFactor;
292 if (last < mThreshold)
293 last = mThreshold;
294 if (level > last)
295 last = level;
296 }
297 env[i] = last;
298 }
299 mLastLevel = last;
300
301 // Next do the same process in reverse direction to get the requested attack
302 // rate
303 last = mLastLevel;
304 for (size_t i = len; i--;)
305 {
306 last *= mAttackInverseFactor;
307 if (last < mThreshold)
308 last = mThreshold;
309 if (env[i] < last)
310 env[i] = last;
311 else
312 last = env[i];
313 }
314
315 if ((previous != NULL) && (previous_len > 0))
316 {
317 // If the previous envelope was passed, propagate the rise back until we
318 // intersect
319 for (size_t i = previous_len; i--;)
320 {
321 last *= mAttackInverseFactor;
322 if (last < mThreshold)
323 last = mThreshold;
324 if (previous[i] < last)
325 previous[i] = last;
326 else // Intersected the previous envelope buffer, so we are finished
327 return;
328 }
329 // If we can't back up far enough, project the starting level forward
330 // until we intersect the desired envelope
331 last = previous[0];
332 for (size_t i = 1; i < previous_len; i++)
333 {
334 last *= mAttackFactor;
335 if (previous[i] > last)
336 previous[i] = last;
337 else // Intersected the desired envelope, so we are finished
338 return;
339 }
340 // If we still didn't intersect, then continue ramp up into current buffer
341 for (size_t i = 0; i < len; i++)
342 {
343 last *= mAttackFactor;
344 if (buffer[i] > last)
345 buffer[i] = last;
346 else // Finally got an intersect
347 return;
348 }
349 // If we still didn't intersect, then reset mLastLevel
350 mLastLevel = last;
351 }
352}
353
354float LegacyCompressorBase::DoCompression(float value, double env)
355{
356 float out;
357 if (mUsePeak)
358 {
359 // Peak values map 1.0 to 1.0 - 'upward' compression
360 out = value * pow(1.0 / env, mCompression);
361 }
362 else
363 {
364 // With RMS-based compression don't change values below mThreshold -
365 // 'downward' compression
366 out = value * pow(mThreshold / env, mCompression);
367 }
368
369 // Retain the maximum value for use in the normalization pass
370 if (mMax < fabs(out))
371 mMax = fabs(out);
372
373 return out;
374}
EffectType
@ EffectTypeProcess
XO("Cut/Copy/Paste")
#define DB_TO_LINEAR(x)
Definition: MemoryX.h:338
void reinit(Integral count, bool initialize=false)
Definition: MemoryX.h:59
Generates EffectParameterMethods overrides from variadic template arguments.
ComponentInterfaceSymbol pairs a persistent string identifier used internally with an optional,...
void SetLinearEffectFlag(bool linearEffectFlag)
Definition: EffectBase.cpp:210
const TrackList * inputTracks() const
Definition: EffectBase.h:102
Interface for manipulations of an Effect's settings.
virtual void Reset(Effect &effect) const =0
void DisableSecondPass()
Call this if you know in advance that no second pass will be needed.
EffectType GetType() const override
Type determines how it behaves.
static constexpr EffectParameter ReleaseTime
static constexpr EffectParameter Ratio
void Follow(float *buffer, float *env, size_t len, float *previous, size_t previous_len)
static constexpr EffectParameter Normalize
static constexpr EffectParameter AttackTime
float DoCompression(float x, double env)
ComponentInterfaceSymbol GetSymbol() const override
static constexpr EffectParameter NoiseFloor
static constexpr EffectParameter UsePeak
bool TwoBufferProcessPass1(float *buffer1, size_t len1, float *buffer2, size_t len2) override
bool ProcessPass2(float *buffer, size_t len) override
static constexpr EffectParameter Threshold
static const ComponentInterfaceSymbol Symbol
const EffectParameterMethods & Parameters() const override
TranslatableString GetDescription() const override
ManualPageID ManualPage() const override
Name of a page in the Audacity alpha manual, default is empty.
auto Selected() -> TrackIterRange< TrackType >
Definition: Track.h:967
Holds a msgid for the translation catalog; may also bind format arguments.
A Track that contains audio waveform data.
Definition: WaveTrack.h:203
size_t GetMaxBlockSize() const
Definition: WaveTrack.cpp:2279
__finl float_x4 __vecc sqrt(const float_x4 &a)