Audacity 3.2.0
Public Member Functions | Private Member Functions | Private Attributes | List of all members
DanielRudrich::LookAheadGainReduction Class Reference

#include <LookAheadGainReduction.h>

Collaboration diagram for DanielRudrich::LookAheadGainReduction:
[legend]

Public Member Functions

 LookAheadGainReduction ()
 
 ~LookAheadGainReduction ()
 
void setDelayTime (float delayTimeInSeconds)
 
const int getDelayInSamples ()
 
void prepare (const double sampleRate, const int blockSize)
 
void pushSamples (const float *src, const int numSamples)
 
void process ()
 
void readSamples (float *dest, const int numSamples)
 

Private Member Functions

void getProcessPositions (int startIndex, int numSamples, int &blockSize1, int &blockSize2)
 
void getWritePositions (int numSamples, int &startIndex, int &blockSize1, int &blockSize2)
 
void getReadPositions (int numSamples, int &startIndex, int &blockSize1, int &blockSize2)
 

Private Attributes

double sampleRate
 
int blockSize
 
float delay
 
int delayInSamples = 0
 
int writePosition = 0
 
int lastPushedSamples = 0
 
std::vector< float > buffer
 

Detailed Description

This class acts as a delay line for gain-reduction samples, which additionally fades in high gain-reduction values in order to avoid distortion when limiting an audio signal.

Definition at line 26 of file LookAheadGainReduction.h.

Constructor & Destructor Documentation

◆ LookAheadGainReduction()

DanielRudrich::LookAheadGainReduction::LookAheadGainReduction ( )
inline

Definition at line 29 of file LookAheadGainReduction.h.

◆ ~LookAheadGainReduction()

DanielRudrich::LookAheadGainReduction::~LookAheadGainReduction ( )
inline

Definition at line 30 of file LookAheadGainReduction.h.

30{}

Member Function Documentation

◆ getDelayInSamples()

const int DanielRudrich::LookAheadGainReduction::getDelayInSamples ( )
inline

Definition at line 34 of file LookAheadGainReduction.h.

References delayInSamples.

◆ getProcessPositions()

void DanielRudrich::LookAheadGainReduction::getProcessPositions ( int  startIndex,
int  numSamples,
int &  blockSize1,
int &  blockSize2 
)
inlineprivate

A little helper-function which calulcates how many samples we should process in a first step before we have to wrap around, as our buffer is a ring-buffer.

Definition at line 213 of file LookAheadGainReduction.cpp.

214{
215 if (numSamples <= 0)
216 {
217 blockSize1 = 0;
218 blockSize2 = 0;
219 }
220 else
221 {
222 blockSize1 = std::min (startIndex + 1, numSamples);
223 numSamples -= blockSize1;
224 blockSize2 = numSamples <= 0 ? 0 : numSamples;
225 }
226}
int min(int a, int b)

References min().

Referenced by process().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getReadPositions()

void DanielRudrich::LookAheadGainReduction::getReadPositions ( int  numSamples,
int &  startIndex,
int &  blockSize1,
int &  blockSize2 
)
inlineprivate

Definition at line 252 of file LookAheadGainReduction.cpp.

253{
254 const int L = static_cast<int> (buffer.size());
256
257 if (pos < 0)
258 pos = pos + L;
259 pos = pos % L;
260
261 if (numSamples <= 0)
262 {
263 startIndex = 0;
264 blockSize1 = 0;
265 blockSize2 = 0;
266 }
267 else
268 {
269 startIndex = pos;
270 blockSize1 = std::min (L - pos, numSamples);
271 numSamples -= blockSize1;
272 blockSize2 = numSamples <= 0 ? 0 : numSamples;
273 }
274}

References buffer, delayInSamples, lastPushedSamples, min(), and writePosition.

Referenced by readSamples().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ getWritePositions()

void DanielRudrich::LookAheadGainReduction::getWritePositions ( int  numSamples,
int &  startIndex,
int &  blockSize1,
int &  blockSize2 
)
inlineprivate

Definition at line 228 of file LookAheadGainReduction.cpp.

229{
230 const int L = static_cast<int> (buffer.size());
231 int pos = writePosition;
232
233 if (pos < 0)
234 pos = pos + L;
235 pos = pos % L;
236
237 if (numSamples <= 0)
238 {
239 startIndex = 0;
240 blockSize1 = 0;
241 blockSize2 = 0;
242 }
243 else
244 {
245 startIndex = pos;
246 blockSize1 = std::min (L - pos, numSamples);
247 numSamples -= blockSize1;
248 blockSize2 = numSamples <= 0 ? 0 : numSamples;
249 }
250}

References buffer, min(), and writePosition.

Referenced by pushSamples().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ prepare()

void DanielRudrich::LookAheadGainReduction::prepare ( const double  sampleRate,
const int  blockSize 
)

Prepares the processor so it can resize the buffers depending on samplerate and the expected buffersize.

Definition at line 36 of file LookAheadGainReduction.cpp.

37{
38 sampleRate = newSampleRate;
39 blockSize = newBlockSize;
40
41 delayInSamples = static_cast<int> (delay * sampleRate);
42
44 std::fill (buffer.begin(), buffer.end(), 0.0f);
45 writePosition = 0;
46}

References blockSize, buffer, delay, delayInSamples, sampleRate, and writePosition.

Referenced by setDelayTime().

Here is the caller graph for this function:

◆ process()

void DanielRudrich::LookAheadGainReduction::process ( )

Processes the data within the delay line, i.e. fades-in high gain-reduction, in order to reduce distortions.

The basic idea here is to look for high gain-reduction values in the signal, and apply a fade which starts exactly delayInSamples many samples before that value appears. Depending on the value itself, the slope of the fade will vary.

Some things to note:

  • as the samples are gain-reduction values in decibel, we actually look for negative peaks or local minima.
  • it's easier for us to time-reverse our search, so we start with the last sample
  • once we find a minimum, we calculate the slope, which will be called step
  • with that slope, we can calculate the next value of our fade-in nextGainReductionValue
  • once a value in the buffer is below our fade-in value, we found a new minimum, which might not be as deep as the previous one, but as it comes in earlier, it needs more attention, so we update our fade-in slope
  • our buffer is a ring-buffer which makes things a little bit messy

Definition at line 68 of file LookAheadGainReduction.cpp.

69{
82 // As we don't know any samples of the future, yet, we assume we don't have to apply a fade-in right now, and initialize both `nextGainReductionValue` and step (slope of the fade-in) with zero.
83 float nextGainReductionValue = 0.0f;
84 float step = 0.0f;
85
86
87 // Get the position of the last sample in the buffer, which is the sample right before our new write position.
88 int index = writePosition - 1;
89 if (index < 0) // in case it's negative...
90 index += static_cast<int> (buffer.size()); // ... add the buffersize so we wrap around.
91
92 // == FIRST STEP: Process all recently pushed samples.
93
94 // We want to process all `lastPushedSamples` many samples, so let's find out, how many we can process in a first run before we have to wrap around our `index` variable (ring-buffer).
95 int size1, size2;
96 getProcessPositions (index, lastPushedSamples, size1, size2);
97
98 // first run
99 for (int i = 0; i < size1; ++i)
100 {
101 const float smpl = buffer[index];
102
103 if (smpl > nextGainReductionValue) // in case the sample is above our ramp...
104 {
105 buffer[index] = nextGainReductionValue; // ... replace it with the current ramp value
106 nextGainReductionValue += step; // and update the next ramp value
107 }
108 else // otherwise... (new peak)
109 {
110 step = - smpl / delayInSamples; // calculate the new slope
111 nextGainReductionValue = smpl + step; // and also the new ramp value
112 }
113 --index;
114 }
115
116 // second run
117 if (size2 > 0) // in case we have some samples left for the second run
118 {
119 index = static_cast<int> (buffer.size()) - 1; // wrap around: start from the last sample of the buffer
120
121 // exactly the same procedure as before... I guess I could have written that better...
122 for (int i = 0; i < size2; ++i)
123 {
124 const float smpl = buffer[index];
125
126 if (smpl > nextGainReductionValue)
127 {
128 buffer[index] = nextGainReductionValue;
129 nextGainReductionValue += step;
130 }
131 else
132 {
133 step = - smpl / delayInSamples;
134 nextGainReductionValue = smpl + step;
135 }
136 --index;
137 }
138 }
139
140 /*
141 At this point, we have processed all the new gain-reduction values. For this, we actually don't need a delay/lookahead at all.
142 !! However, we are not finished, yet !!
143 What if the first pushed sample has such a high gain-reduction value, that itself needs a fade-in? So we have to apply a gain-ramp even further into the past. And that is exactly the reason why we need lookahead, why we need to buffer our signal for a short amount of time: so we can apply that gain ramp for the first handful of gain-reduction samples.
144 */
145
146 if (index < 0) // it's possible the index is exactly -1
147 index = static_cast<int> (buffer.size()) - 1; // so let's take care of that
148
149 /*
150 This time we only need to check `delayInSamples` many samples.
151 And there's another cool thing!
152 We know that the samples have been processed already, so in case one of the samples is below our ramp value, that's the new minimum, which has been faded-in already! So what we do is hit the break, and call it a day!
153 */
154 getProcessPositions (index, delayInSamples, size1, size2);
155 bool breakWasUsed = false;
156
157 // first run
158 for (int i = 0; i < size1; ++i) // we iterate over the first size1 samples
159 {
160 const float smpl = buffer[index];
161
162 if (smpl > nextGainReductionValue) // in case the sample is above our ramp...
163 {
164 buffer[index] = nextGainReductionValue; // ... replace it with the current ramp value
165 nextGainReductionValue += step; // and update the next ramp value
166 }
167 else // otherwise... JACKPOT! Nothing left to do here!
168 {
169 breakWasUsed = true; // let the guys know we are finished
170 break;
171 }
172 --index;
173 }
174
175 // second run
176 if (! breakWasUsed && size2 > 0) // is there still some work to do?
177 {
178 index = static_cast<int> (buffer.size()) - 1; // wrap around (ring-buffer)
179 for (int i = 0; i < size2; ++i)
180 {
181 const float smpl = buffer[index];
182
183 // same as before
184 if (smpl > nextGainReductionValue) // in case the sample is above our ramp...
185 {
186 buffer[index] = nextGainReductionValue; // ... replace it with the current ramp value
187 nextGainReductionValue += step; // and update the next ramp value
188 }
189 else // otherwise... already processed -> byebye!
190 break;
191 --index;
192 }
193 }
194}
void getProcessPositions(int startIndex, int numSamples, int &blockSize1, int &blockSize2)

References buffer, delayInSamples, getProcessPositions(), lastPushedSamples, and writePosition.

Here is the call graph for this function:

◆ pushSamples()

void DanielRudrich::LookAheadGainReduction::pushSamples ( const float *  src,
const int  numSamples 
)

Writes gain-reduction samples into the delay-line. Make sure you call process() afterwards, and read the same amount of samples with the readSamples method. Make also sure the pushed samples are decibel values.

Definition at line 48 of file LookAheadGainReduction.cpp.

49{
50 int startIndex, blockSize1, blockSize2;
51
52 // write in delay line
53 getWritePositions (numSamples, startIndex, blockSize1, blockSize2);
54
55 for (int i = 0; i < blockSize1; ++i)
56 buffer[startIndex + i] = src[i];
57
58 if (blockSize2 > 0)
59 for (int i = 0; i < blockSize2; ++i)
60 buffer[i] = src[blockSize1 + i];
61
62 writePosition += numSamples;
64
65 lastPushedSamples = numSamples;
66}
void getWritePositions(int numSamples, int &startIndex, int &blockSize1, int &blockSize2)

References buffer, getWritePositions(), lastPushedSamples, and writePosition.

Here is the call graph for this function:

◆ readSamples()

void DanielRudrich::LookAheadGainReduction::readSamples ( float *  dest,
const int  numSamples 
)

Reads smoothed gain-reduction samples back to the destination. Make sure you read as many samples as you've pushed before!

Definition at line 197 of file LookAheadGainReduction.cpp.

198{
199 int startIndex, blockSize1, blockSize2;
200
201 // read from delay line
202 getReadPositions (numSamples, startIndex, blockSize1, blockSize2);
203
204 for (int i = 0; i < blockSize1; ++i)
205 dest[i] = buffer[startIndex + i];
206
207 if (blockSize2 > 0)
208 for (int i = 0; i < blockSize2; ++i)
209 dest[blockSize1 + i] = buffer[i];
210}
void getReadPositions(int numSamples, int &startIndex, int &blockSize1, int &blockSize2)

References buffer, and getReadPositions().

Here is the call graph for this function:

◆ setDelayTime()

void DanielRudrich::LookAheadGainReduction::setDelayTime ( float  delayTimeInSeconds)

Definition at line 25 of file LookAheadGainReduction.cpp.

26{
27 if (delayTimeInSeconds <= 0.0f)
28 delay = 0.0f;
29 else
30 delay = delayTimeInSeconds;
31
32 if (sampleRate != 0.0)
34}
void prepare(const double sampleRate, const int blockSize)

References blockSize, delay, prepare(), and sampleRate.

Here is the call graph for this function:

Member Data Documentation

◆ blockSize

int DanielRudrich::LookAheadGainReduction::blockSize
private

Definition at line 66 of file LookAheadGainReduction.h.

Referenced by prepare(), and setDelayTime().

◆ buffer

std::vector<float> DanielRudrich::LookAheadGainReduction::buffer
private

◆ delay

float DanielRudrich::LookAheadGainReduction::delay
private

Definition at line 68 of file LookAheadGainReduction.h.

Referenced by prepare(), and setDelayTime().

◆ delayInSamples

int DanielRudrich::LookAheadGainReduction::delayInSamples = 0
private

Definition at line 69 of file LookAheadGainReduction.h.

Referenced by getDelayInSamples(), getReadPositions(), prepare(), and process().

◆ lastPushedSamples

int DanielRudrich::LookAheadGainReduction::lastPushedSamples = 0
private

Definition at line 71 of file LookAheadGainReduction.h.

Referenced by getReadPositions(), process(), and pushSamples().

◆ sampleRate

double DanielRudrich::LookAheadGainReduction::sampleRate
private

Definition at line 65 of file LookAheadGainReduction.h.

Referenced by prepare(), and setDelayTime().

◆ writePosition

int DanielRudrich::LookAheadGainReduction::writePosition = 0
private

The documentation for this class was generated from the following files: