Audacity 3.2.0
CompressorProcessor.cpp
Go to the documentation of this file.
1/* SPDX-License-Identifier: GPL-2.0-or-later */
2/*!********************************************************************
3
4 Audacity: A Digital Audio Editor
5
6 CompressorProcessor.cpp
7
8 Matthieu Hodgkinson
9
10**********************************************************************/
11
12#include "CompressorProcessor.h"
13#include "MathApprox.h"
16#include <algorithm>
17#include <cassert>
18
21{
22 return settings.outCompressionThreshDb - settings.inCompressionThreshDb;
23}
24
27{
28 const auto tfEval = EvaluateTransferFunction(settings, 0);
29 const auto netGain =
30 settings.outCompressionThreshDb - settings.inCompressionThreshDb;
31 return netGain - tfEval;
32}
33
35 const DynamicRangeProcessorSettings& settings, float inputDb)
36{
38 inputDb, settings.kneeWidthDb, settings.inCompressionThreshDb,
39 settings.compressionRatio, GetMakeupGainDb(settings));
40}
41
44 : mGainReductionComputer { std::make_unique<
45 DanielRudrich::GainReductionComputer>() }
46 , mLookAheadGainReduction { std::make_unique<
47 DanielRudrich::LookAheadGainReduction>() }
48 , mSettings { settings }
49{
50}
51
53
56{
57 if (settings == mSettings)
58 return;
59
60 const auto lookaheadNeedsReinit =
61 settings.lookaheadMs != mSettings.lookaheadMs;
63
64 mGainReductionComputer->setThreshold(settings.inCompressionThreshDb);
65 mGainReductionComputer->setKnee(settings.kneeWidthDb);
66 mGainReductionComputer->setAttackTime(settings.attackMs / 1000);
67 mGainReductionComputer->setReleaseTime(settings.releaseMs / 1000);
68 mGainReductionComputer->setRatio(settings.compressionRatio);
70
71 if (lookaheadNeedsReinit)
72 {
73 mLookAheadGainReduction->setDelayTime(settings.lookaheadMs / 1000);
74 Reinit();
75 }
76}
77
78void CompressorProcessor::Init(int sampleRate, int numChannels, int blockSize)
79{
81 mNumChannels = numChannels;
82 mBlockSize = std::min(blockSize, maxBlockSize);
83 Reinit();
84}
85
87{
88 return mSettings;
89}
90
92 const float* const* inBlock, float* const* outBlock, int blockLen)
93{
94 assert(Initialized());
95 if (!Initialized())
96 return;
97
98 auto processed = 0;
99 mLastFrameStats = {};
100 std::vector<const float*> in(mNumChannels);
101 std::vector<float*> out(mNumChannels);
102 while (processed < blockLen)
103 {
104 for (auto i = 0; i < mNumChannels; ++i)
105 {
106 in[i] = inBlock[i] + processed;
107 out[i] = outBlock[i] + processed;
108 }
109 const auto toProcess = std::min(blockLen - processed, mBlockSize);
110 UpdateEnvelope(in.data(), toProcess);
111 CopyWithDelay(in.data(), toProcess);
112
113 float delayedInputAbsMax = 0;
114 int delayedInputAbsMaxIndex = 0;
116 out.data(), toProcess, delayedInputAbsMax, delayedInputAbsMaxIndex);
117
118 const auto blockMaxDb = log2ToDb * FastLog2(delayedInputAbsMax);
119 if (mLastFrameStats.maxInputSampleDb < blockMaxDb)
120 {
123 mEnvelope[delayedInputAbsMaxIndex];
124 }
125
126 processed += toProcess;
127 }
128}
129
130const std::vector<std::vector<float>>&
132{
133 return mDelayedInput;
134}
135
138{
139 return mLastFrameStats;
140}
141
143{
144 return mGainReductionComputer->getCharacteristicSample(inputDb);
145}
146
147void CompressorProcessor::UpdateEnvelope(const float* const* in, int blockLen)
148{
149 // Fill mEnvelope with max of all in channels;
150 for (auto i = 0; i < blockLen; ++i)
151 {
152 auto max = 0.f;
153 for (auto j = 0; j < mNumChannels; ++j)
154 {
155 const auto x = std::abs(in[j][i]);
156 if (x > max)
157 max = x;
158 }
159 mEnvelope[i] = max;
160 }
161
162 // TODO: uses std::log10 ; use log2 optimization instead.
163 mGainReductionComputer->computeGainInDecibelsFromSidechainSignal(
164 mEnvelope.data(), mEnvelope.data(), blockLen);
165
166 if (mSettings.lookaheadMs <= 0)
167 return;
168
169 mLookAheadGainReduction->pushSamples(mEnvelope.data(), blockLen);
170 mLookAheadGainReduction->process();
171 mLookAheadGainReduction->readSamples(mEnvelope.data(), blockLen);
172}
173
174void CompressorProcessor::CopyWithDelay(const float* const* in, int blockLen)
175{
176 const auto d = mLookAheadGainReduction->getDelayInSamples();
177 for (auto i = 0; i < mNumChannels; ++i)
178 std::copy(in[i], in[i] + blockLen, mDelayedInput[i].data() + d);
179}
180
182 float* const* out, int blockLen, float& delayedInputAbsMax,
183 int& delayedInputAbsMaxIndex)
184{
185 const auto makeupGainDb = mGainReductionComputer->getMakeUpGain();
186 const auto d = mLookAheadGainReduction->getDelayInSamples();
187 std::array<float, 2> chanAbsMax { 0.f, 0.f };
188 std::array<int, 2> chanAbsMaxIndex { 0, 0 };
189 for (auto i = 0; i < mNumChannels; ++i)
190 {
191 const auto in = mDelayedInput[i].data();
192 for (auto j = 0; j < blockLen; ++j)
193 {
194 if (std::abs(in[j]) > chanAbsMax[i])
195 {
196 chanAbsMax[i] = std::abs(in[j]);
197 chanAbsMaxIndex[i] = j;
198 }
199 out[i][j] =
200 in[j] * std::pow(10.f, 0.05f * (mEnvelope[j] + makeupGainDb));
201 }
202 std::move(in + blockLen, in + blockLen + d, in);
203 }
204 const auto i = chanAbsMax[0] > chanAbsMax[1] ? 0 : 1;
205 delayedInputAbsMax = chanAbsMax[i];
206 delayedInputAbsMaxIndex = chanAbsMaxIndex[i];
207}
208
210{
211 if (!Initialized())
212 // Not there yet.
213 return;
215 // In this order: setDelayTime, then prepare:
216 mLookAheadGainReduction->setDelayTime(mSettings.lookaheadMs / 1000);
218 const auto maxDelay =
220 1000;
221 const auto d = mLookAheadGainReduction->getDelayInSamples();
222 assert(d <= maxDelay);
224 std::for_each(mDelayedInput.begin(), mDelayedInput.end(), [&](auto& v) {
225 v.reserve(maxDelay + mBlockSize);
226 v.resize(d + mBlockSize);
227 std::fill(v.begin(), v.end(), 0.f);
228 });
229 std::fill(mEnvelope.begin(), mEnvelope.end(), 0.f);
230}
231
233{
234 return mSampleRate != 0 && mNumChannels != 0 && mBlockSize != 0;
235}
int min(int a, int b)
constexpr double compressorMaxLookaheadMs
constexpr double limiterMaxLookaheadMs
constexpr float FastLog2(float x)
Approximates the base-2 logarithm of a float to two decimal places, adapted from https://stackoverflo...
Definition: MathApprox.h:32
static constexpr float log2ToDb
Definition: MathApprox.h:46
static Settings & settings()
Definition: TrackInfo.cpp:51
std::array< float, maxBlockSize > mEnvelope
void Init(int sampleRate, int numChannels, int blockSize)
const std::vector< std::vector< float > > & GetDelayedInput() const
void ApplyEnvelope(float *const *outBlock, int blockLen, float &delayedInputMax, int &delayedInputMaxIndex)
void ApplySettingsIfNeeded(const DynamicRangeProcessorSettings &settings)
void CopyWithDelay(const float *const *inBlock, int blockLen)
DynamicRangeProcessorSettings mSettings
void UpdateEnvelope(const float *const *inBlock, int blockLen)
void Process(const float *const *inBlock, float *const *outBlock, int blockLen)
const FrameStats & GetLastFrameStats() const
const std::unique_ptr< DanielRudrich::GainReductionComputer > mGainReductionComputer
std::vector< std::vector< float > > mDelayedInput
static constexpr auto maxBlockSize
const DynamicRangeProcessorSettings & GetSettings() const
static float GetMakeupGainDb(const DynamicRangeProcessorSettings &settings)
static float EvaluateTransferFunction(const DynamicRangeProcessorSettings &settings, float inputDb)
const std::unique_ptr< DanielRudrich::LookAheadGainReduction > mLookAheadGainReduction
CompressorProcessor(const DynamicRangeProcessorSettings &settings={ CompressorSettings {} })
static float GetMaxCompressionDb(const DynamicRangeProcessorSettings &settings)
static float getCharacteristicSample(float inputLevelInDecibels, float kneeInDecibels, float thresholdInDecibels, float ratio, float makeUpGainInDecibels)
void copy(const T *src, T *dst, int32_t n)
Definition: VectorOps.h:40
STL namespace.