Audacity 3.2.0
CompressorInstance.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 CompressorInstance.cpp
7
8 Matthieu Hodgkinson
9
10**********************************************************************/
11#include "CompressorInstance.h"
12#include "CompressorProcessor.h"
13#include "MathApprox.h"
14#include <numeric>
15
17 : PerTrackEffect::Instance { effect }
18 , mCompressor { std::make_unique<CompressorProcessor>() }
19{
20}
21
23 : PerTrackEffect::Instance { other }
24 , mCompressor { std::move(other.mCompressor) }
25 , mSlaves { std::move(other.mSlaves) }
26 , mSampleCounter { std::move(other.mSampleCounter) }
27 , mSampleRate { std::move(other.mSampleRate) }
28 , mOutputQueue { std::move(other.mOutputQueue) }
29 , mCompressionValueQueue { std::move(other.mCompressionValueQueue) }
30{
31}
32
33const std::optional<double>& CompressorInstance::GetSampleRate() const
34{
35 return mSampleRate;
36}
37
39{
40 return mSlaves.empty() ?
41 mCompressor->GetSettings().lookaheadMs :
42 mSlaves.front().mCompressor->GetSettings().lookaheadMs;
43}
44
46 std::weak_ptr<DynamicRangeProcessorOutputPacketQueue> outputQueue)
47{
48 mOutputQueue = outputQueue;
49 for (auto& slave : mSlaves)
50 slave.mOutputQueue = outputQueue;
51}
52
54 std::weak_ptr<DynamicRangeProcessorMeterValuesQueue> queue)
55{
57 for (auto& slave : mSlaves)
58 slave.mCompressionValueQueue = queue;
59}
60
63{
66 return true;
67}
68
70{
71 mSampleRate.reset();
72 return true;
73}
74
75namespace
76{
79{
80 if (auto pSettings = settings.cast<CompressorSettings>())
81 return *pSettings;
82 return *settings.cast<LimiterSettings>();
83}
84
85auto GetMaxDbIncrease(const float* in, const float* out, size_t blockLen)
86{
87 auto leastRatio = 0.f;
88 for (size_t i = 0; i < blockLen; ++i)
89 {
90 const auto absIn = std::abs(in[i]);
91 if (absIn < 1e-6)
92 continue;
93 leastRatio = std::max(std::abs(out[i]) / absIn, leastRatio);
94 }
95 return leastRatio == 0 ? -std::numeric_limits<float>::infinity() :
96 log2ToDb * FastLog2(leastRatio);
97}
98} // namespace
99
101 EffectSettings& settings, const float* const* inBlock,
102 float* const* outBlock, size_t blockLen)
103{
104 return InstanceProcess(settings, *mCompressor, inBlock, outBlock, blockLen);
105}
106
108{
109 SetBlockSize(512);
110 mSlaves.clear();
111 mSampleCounter = 0;
114 std::make_optional(InitializeProcessingSettings { sampleRate }));
115 return true;
116}
117
119{
120 for (auto& slave : mSlaves)
121 // Neither block size nore sample rate or any other parameter has changed,
122 // so `Reinit()` should not reallocate memory.
123 slave.mCompressor->Reinit();
125 return true;
126}
127
129 EffectSettings& settings, EffectOutputs* pOutputs, unsigned numChannels,
130 float sampleRate)
131{
132 mSlaves.emplace_back(mProcessor);
133 InstanceInit(settings, mSlaves.back(), numChannels, sampleRate);
134 return true;
135}
136
138{
139 mSlaves.clear();
140 mSampleRate.reset();
142 return true;
143}
144
145namespace
146{
150{
151 return stats.maxInputSampleDb + stats.dbGainOfMaxInputSample +
153}
154} // namespace
155
157 size_t group, EffectSettings& settings, const float* const* inbuf,
158 float* const* outbuf, size_t numSamples)
159{
160 if (group >= mSlaves.size())
161 return 0;
162 auto& slave = mSlaves[group];
163 auto& compressor = *slave.mCompressor;
164 const auto numProcessedSamples =
165 InstanceProcess(settings, compressor, inbuf, outbuf, numSamples);
166 if (const auto queue = slave.mOutputQueue.lock())
167 {
168 const auto& frameStats = compressor.GetLastFrameStats();
169 const auto& compressorSettings = compressor.GetSettings();
170 const float netGain = compressorSettings.outCompressionThreshDb -
171 compressorSettings.inCompressionThreshDb;
172 const auto targetCompressionDb =
173 compressor.EvaluateTransferFunction(frameStats.maxInputSampleDb) -
174 frameStats.maxInputSampleDb - netGain;
176 newPacket.indexOfFirstSample = slave.mSampleCounter;
177 newPacket.numSamples = numProcessedSamples;
178 newPacket.targetCompressionDb = targetCompressionDb;
179 newPacket.actualCompressionDb = frameStats.dbGainOfMaxInputSample;
180 newPacket.inputDb = frameStats.maxInputSampleDb;
181 newPacket.outputDb = GetOutputDb(frameStats, compressorSettings);
182 queue->Put(newPacket);
183 }
184
185 if (const auto queue = slave.mCompressionValueQueue.lock())
186 queue->Put(MeterValues {
187 compressor.GetLastFrameStats().dbGainOfMaxInputSample,
189 compressor.GetLastFrameStats(), compressor.GetSettings()) });
190
191 slave.mSampleCounter += numProcessedSamples;
192 return numProcessedSamples;
193}
194
196 size_t group, EffectSettings& settings, const float* const* inbuf,
197 size_t numSamples)
198{
199 // Keep track of the amount of samples that passed by, so that when
200 // processing resumes, the visualization reflects the elapsed time while
201 // bypassed.
202 if (group < mSlaves.size())
203 mSlaves[group].mSampleCounter += numSamples;
204}
205
207 EffectSettings& settings, CompressorInstance& instance, int numChannels,
208 float sampleRate)
209{
210 instance.mOutputQueue = mOutputQueue;
212 instance.mCompressor->ApplySettingsIfNeeded(
214 instance.mCompressor->Init(sampleRate, numChannels, GetBlockSize());
215}
216
219 const float* const* inBlock, float* const* outBlock, size_t blockLen)
220{
222 instance.Process(inBlock, outBlock, blockLen);
223 return blockLen;
224}
225
227 const EffectSettings& settings, double sampleRate) const
228{
230 1000;
231}
232
234{
235 return 2;
236}
237
239{
240 return 2;
241}
ChannelName
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
void SetMeterValuesQueue(std::weak_ptr< DynamicRangeProcessorMeterValuesQueue > queue)
size_t InstanceProcess(EffectSettings &settings, CompressorProcessor &instance, const float *const *inBlock, float *const *outBlock, size_t blockLen)
bool ProcessInitialize(EffectSettings &settings, double sampleRate, ChannelNames chanMap) override
std::weak_ptr< DynamicRangeProcessorMeterValuesQueue > mCompressionValueQueue
float GetLatencyMs() const
EffectInstance::SampleCount GetLatency(const EffectSettings &settings, double sampleRate) const override
bool RealtimeInitialize(EffectSettings &settings, double sampleRate) override
std::optional< double > mSampleRate
const std::optional< double > & GetSampleRate() const
std::weak_ptr< DynamicRangeProcessorOutputPacketQueue > mOutputQueue
CompressorInstance(const PerTrackEffect &effect)
bool RealtimeFinalize(EffectSettings &settings) noexcept override
void RealtimePassThrough(size_t group, EffectSettings &settings, const float *const *inbuf, size_t numSamples) override
Called instead of RealtimeProcess when the effect is bypassed. Default implementation does nothing.
size_t ProcessBlock(EffectSettings &settings, const float *const *inBlock, float *const *outBlock, size_t blockLen) override
Called for destructive effect computation.
void InstanceInit(EffectSettings &settings, CompressorInstance &instance, int numChannels, float sampleRate)
size_t RealtimeProcess(size_t group, EffectSettings &settings, const float *const *inbuf, float *const *outbuf, size_t numSamples) override
unsigned GetAudioInCount() const override
How many input buffers to allocate at once.
std::unique_ptr< CompressorProcessor > mCompressor
unsigned GetAudioOutCount() const override
How many output buffers to allocate at once.
std::vector< CompressorInstance > mSlaves
void SetOutputQueue(std::weak_ptr< DynamicRangeProcessorOutputPacketQueue >)
bool ProcessFinalize() noexcept override
bool RealtimeResume() override
bool RealtimeAddProcessor(EffectSettings &settings, EffectOutputs *pOutputs, unsigned numChannels, float sampleRate) override
void ApplySettingsIfNeeded(const DynamicRangeProcessorSettings &settings)
void Process(const float *const *inBlock, float *const *outBlock, int blockLen)
static float GetMakeupGainDb(const DynamicRangeProcessorSettings &settings)
uint64_t SampleCount
size_t SetBlockSize(size_t maxBlockSize) override
size_t GetBlockSize() const override
Hold values to send to effect output meters.
CallbackReturn Publish(const Message &message)
Send a message to connected callbacks.
Definition: Observer.h:207
const PerTrackEffect & mProcessor
Base class for many of the effects in Audacity.
DynamicRangeProcessorSettings GetDynamicRangeProcessorSettings(const EffectSettings &settings)
auto GetMaxDbIncrease(const float *in, const float *out, size_t blockLen)
float GetOutputDb(const CompressorProcessor::FrameStats &stats, const DynamicRangeProcessorSettings &settings)
STL namespace.
Externalized state of a plug-in.