WE Core
Loading...
Searching...
No Matches
SimpleCompressor.h
Go to the documentation of this file.
1/*
2 * File: SimpleCompressor.h
3 *
4 * Created: 03/12/2020
5 *
6 * This file is part of WECore.
7 *
8 * WECore is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * WECore is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with WECore. If not, see <http://www.gnu.org/licenses/>.
20 */
21
22#pragma once
23
24#include "General/CoreMath.h"
25#include "EffectsProcessor.h"
27
29
30 enum class Direction {
31 UPWARD,
33 };
34
39 template <typename SampleType>
40 class SimpleCompressor : public EffectsProcessor1in1out<SampleType> {
41 public:
43 virtual ~SimpleCompressor() override = default;
44
53 inline void setSampleRate(double val);
54
60 inline void setDirection(Direction val);
61
69 inline void setAttack(double val);
70
78 inline void setRelease(double val);
79
87 void setThreshold(double val) { _threshold = Parameters::THRESHOLD.BoundsCheck(val); }
88
96 void setRatio(double val) { _ratio = Parameters::RATIO.BoundsCheck(val); }
97
105 void setKneeWidth(double val) { _kneeWidth = Parameters::KNEE_WIDTH.BoundsCheck(val); }
106
116
120 double getAttack() const { return _attackMs; }
121
125 double getRelease() const { return _releaseMs; }
126
130 double getThreshold() const { return _threshold; }
131
135 double getRatio() const { return _ratio; }
136
140 double getKneeWidth() const { return _kneeWidth; }
141
145 SampleType getGainApplied() const { return _gainApplied; }
146
149 inline virtual void process1in1out(SampleType* inSamples,
150 size_t numSamples) override;
151
152 inline virtual void reset() override;
153
154 private:
156
158
159 double _attackMs;
164 double _ratio;
166
168 SampleType _gainApplied;
169
170 double _calcCoef(double timeMs) { return exp(log(0.01) / (timeMs * _sampleRate * 0.001)); }
171
172 inline SampleType _computeLevel(SampleType inSample);
173
174 inline SampleType _computeGain(SampleType inSample);
175 };
176
177 template <typename SampleType>
179 _attackMs(Parameters::ATTACK_MS.defaultValue),
180 _releaseMs(Parameters::RELEASE_MS.defaultValue),
181 _threshold(Parameters::THRESHOLD.defaultValue),
182 _ratio(Parameters::RATIO.defaultValue),
183 _kneeWidth(Parameters::KNEE_WIDTH.defaultValue),
184 _levelDetectorState(0),
185 _gainApplied(0) {
186 // Call this here rather than setting it in initialiser list so that the coefficients get
187 // setup
188 setSampleRate(44100);
189 }
190
191 template <typename SampleType>
193 if (!WECore::CoreMath::compareFloatsEqual(_sampleRate, val)) {
194 _sampleRate = val;
195
196 _attackCoef = _calcCoef(_attackMs);
197 _releaseCoef = _calcCoef(_releaseMs);
198
199 reset();
200 }
201 }
202
203 template <typename SampleType>
205 if (_direction != val) {
206 _direction = val;
207 reset();
208 }
209 }
210
211 template <typename SampleType>
213 _attackMs = Parameters::ATTACK_MS.BoundsCheck(val);
214 _attackCoef = _calcCoef(_attackMs);
215 }
216
217 template <typename SampleType>
219 _releaseMs = Parameters::RELEASE_MS.BoundsCheck(val);
220 _releaseCoef = _calcCoef(_releaseMs);
221 }
222
223 template <typename SampleType>
225 size_t numSamples) {
226
227 for (size_t index {0}; index < numSamples; index++) {
228
229 // Rectify the input and convert to dB
230 const SampleType absdB {static_cast<SampleType>(
231 CoreMath::linearTodB(std::abs(inSamples[index]))
232 )};
233
234 // Compute gain
235 const SampleType gainComp {_computeGain(absdB)};
236
237 // Take the difference and pass to the level detection
238 const SampleType level {_computeLevel(absdB - gainComp)};
239
240 // Convert to linear and apply the gain
241 _gainApplied = static_cast<SampleType>(CoreMath::dBToLinear(-level));
242
243 inSamples[index] = inSamples[index] * _gainApplied;
244 }
245 }
246
247 template <typename SampleType>
249 _levelDetectorState = 0;
250 _gainApplied = 0;
251 }
252
253 template <typename SampleType>
254 SampleType SimpleCompressor<SampleType>::_computeLevel(SampleType inSample) {
255
256 if (inSample > _levelDetectorState) {
257 _levelDetectorState = _attackCoef * _levelDetectorState + (1 - _attackCoef) * inSample;
258 } else {
259 _levelDetectorState = _releaseCoef * _levelDetectorState + (1 - _releaseCoef) * inSample;
260 }
261
262 return _levelDetectorState;
263 }
264
265 template <typename SampleType>
266 SampleType SimpleCompressor<SampleType>::_computeGain(SampleType inSample) {
267
268 SampleType retVal {inSample};
269
270 if (_direction == Direction::DOWNWARD) {
271
272 // Downward compression
273 if (inSample - _threshold < -_kneeWidth / 2) {
274 // Level is below threshold and knee - do nothing
275
276 } else if (std::abs(inSample - _threshold) <= (_kneeWidth / 2)) {
277 // Level is within the range of the knee
278 retVal = inSample + (1 / _ratio - 1)
279 * std::pow(inSample - _threshold + (_kneeWidth / 2), 2);
280
281 } else {
282 // Level is above threshold and knee
283 retVal = _threshold + (inSample - _threshold) / _ratio;
284 }
285
286 } else {
287
288 // Upward compression
289 if (inSample - _threshold < -_kneeWidth / 2) {
290 // Level is below theshold and knee
291 retVal = _threshold - (_threshold - inSample) / _ratio;
292
293 } else if (std::abs(inSample - _threshold) <= (_kneeWidth / 2)) {
294 // Level is within the range of the knee
295 retVal = inSample + (1 - 1 / _ratio) \
296 * std::pow(inSample - _threshold - _kneeWidth / 2, 2) / (2 * _kneeWidth);
297
298 } else {
299 // Level is above threshold and knee - do nothing
300 }
301
302 }
303
304 return retVal;
305 }
306}
virtual ~SimpleCompressor() override=default
SampleType _computeLevel(SampleType inSample)
virtual void process1in1out(SampleType *inSamples, size_t numSamples) override
SampleType _computeGain(SampleType inSample)
double linearTodB(double val)
Definition CoreMath.h:50
bool compareFloatsEqual(T x, T y, T tolerance=std::numeric_limits< T >::epsilon())
Definition CoreMath.h:45
double dBToLinear(double val)
Definition CoreMath.h:51
const ParameterDefinition::RangedParameter< double > THRESHOLD(-60, 0, 0)
const ParameterDefinition::RangedParameter< double > RELEASE_MS(1, 5000, 100)
const ParameterDefinition::RangedParameter< double > RATIO(1, 30, 2)
const ParameterDefinition::RangedParameter< double > KNEE_WIDTH(1, 10, 2)
const ParameterDefinition::RangedParameter< double > ATTACK_MS(0.1, 500, 10)