SynthLab SDK
limiter.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "synthbase.h"
4 #include "synthconstants.h"
5 
6 // -----------------------------
7 // --- SynthLab SDK File --- //
8 // ----------------------------
16 // -----------------------------------------------------------------------------
17 namespace SynthLab
18 {
31  class SimpleLPF
32  {
33  public:
34  SimpleLPF(void) {} /* C-TOR */
35  ~SimpleLPF(void) {} /* D-TOR */
36 
37  public:
39  virtual bool reset(double _sampleRate)
40  {
41  state = 0.0;
42  return true;
43  }
44 
49  void setLPF_g(double _lpf_g) { lpf_g = _lpf_g; }
50 
56  virtual double processAudioSample(double xn)
57  {
58  double yn = (1.0 - lpf_g)*xn + lpf_g*state;
59  state = yn;
60  return yn;
61  }
62 
63  private:
64  double lpf_g = 0.8;
65  double state = 0.0;
66  };
67 
83  {
84  public:
85  LogPeakDetector(void) {} /* C-TOR */
86  ~LogPeakDetector(void) {} /* D-TOR */
87 
88  public:
91  virtual bool reset(double _sampleRate)
92  {
93  // --- store the sample rate
94  sampleRate = _sampleRate;
95  samplePeriod_T = 1.0 / sampleRate;
96 
97  // --- storage to hold peak value
98  peakStore = 0.0;
99 
100  // --- setup simple one-pole LPF to smooth detector output
101  lpf.reset(sampleRate);
102 
103  return true;
104  }
105 
111  virtual double processAudioSample(double xn)
112  {
113  // --- all modes do Full Wave Rectification
114  double input = fabs(xn);
115 
116  // --- output variable is output of peak storage
117  double rn = peakStore;
118 
119  // --- we are recursive so need to check underflow
121 
122  // --- can not be (-)
123  rn = fmax(rn, 0.0);
124 
125  // --- setup inputs to the peak store register
126  double attackValue = 0.0;
127  double releaseValue = 0.0;
128 
129  // --- form difference
130  double diff = input - peakStore;
131  if (diff > 0.0)
132  attackValue = attackTime;
133 
134  // --- attack branch accumultes with peak store
135  attackValue += peakStore;
136 
137  // --- release branch multiplies with output
138  releaseValue = rn * releaseTime;
139 
140  // --- sum of branches
141  peakStore = attackValue - releaseValue;
142 
143  // --- filtering to smooth response
144  double yn = lpf.processAudioSample(rn);
145 
146  // --- setup for log( )
147  if (yn <= 0)
148  return -96.0; // should only get called for string of 0's
149 
150  // --- true log output in dB, can go above 0dBFS!
151  return 20.0 * log10(yn);
152  }
153 
160  inline void setAttackReleaseTimes(double attack_in_ms, double release_in_ms)
161  {
162  // --- cook parameters here
163  setAttackTime(attack_in_ms);
164  setReleaseTime(release_in_ms);
165  }
166 
167  const double kSmallestPositiveFloatValue = 1.175494351e-38; /* min positive value */
168  const double kSmallestNegativeFloatValue = -1.175494351e-38; /* min negative value */
169 
174  inline bool checkFloatUnderflow(double& value)
175  {
176  bool retValue = false;
177  if (value > 0.0 && value < kSmallestPositiveFloatValue)
178  {
179  value = 0;
180  retValue = true;
181  }
182  else if (value < 0.0 && value > kSmallestNegativeFloatValue)
183  {
184  value = 0;
185  retValue = true;
186  }
187  return retValue;
188  }
189  private:
190  SimpleLPF lpf;
191  double attackTime = 0.0;
192  double releaseTime = 0.0;
193  double sampleRate = 44100;
194  double samplePeriod_T = 1.0;
195  double lastEnvelope = 0.0;
196  double peakStore = 0.0;
197 
200  void setAttackTime(double attack_in_ms)
201  {
202  // --- calculate 10% - 90% risetime
203  attackTime = 1.0 - exp((-2.2*samplePeriod_T) / (attack_in_ms * 0.001));
204  }
205 
208  void setReleaseTime(double release_in_ms)
209  {
210  // --- calculate 10% - 90% falltime
211  releaseTime = 1.0 - exp((-2.2*samplePeriod_T) / (release_in_ms * 0.001));
212  }
213  };
214 
215 
231  {
232  public:
233  LinPeakDetector(void) {} /* C-TOR */
234  ~LinPeakDetector(void) {} /* D-TOR */
235 
236  public:
239  virtual bool reset(double _sampleRate)
240  {
241  // --- store the sample rate
242  sampleRate = _sampleRate;
243  samplePeriod_T = 1.0 / sampleRate;
244 
245  // --- storage to hold peak value
246  peakStore = 0.0;
247 
248  // --- setup simple one-pole LPF to smooth detector output
249  lpf.reset(sampleRate);
250 
251  return true;
252  }
253 
259  virtual double processAudioSample(double xn)
260  {
261  // --- all modes do Full Wave Rectification
262  double input = fabs(xn);
263 
264  // --- output variable is output of peak storage
265  double rn = peakStore;
266 
267  // --- we are recursive so need to check underflow
269 
270  // --- can not be (-)
271  rn = fmax(rn, 0.0);
272 
273  // --- setup inputs to the peak store register
274  double attackValue = 0.0;
275  double releaseValue = 0.0;
276 
277  // --- form difference
278  double diff = input - peakStore;
279  if (diff > 0.0)
280  attackValue = attackTime;
281 
282  // --- attack branch accumultes with peak store
283  attackValue += peakStore;
284 
285  // --- release branch multiplies with output
286  releaseValue = rn * releaseTime;
287 
288  // --- sum of branches
289  peakStore = attackValue - releaseValue;
290 
291  // --- filtering to smooth response
292  double yn = lpf.processAudioSample(rn);
293 
294  return yn;
295  }
296 
303  inline void setAttackReleaseTimes(double attack_in_ms, double release_in_ms)
304  {
305  // --- cook parameters here
306  setAttackTime(attack_in_ms);
307  setReleaseTime(release_in_ms);
308  }
309 
310  const double kSmallestPositiveFloatValue = 1.175494351e-38; /* min positive value */
311  const double kSmallestNegativeFloatValue = -1.175494351e-38; /* min negative value */
312 
317  inline bool checkFloatUnderflow(double& value)
318  {
319  bool retValue = false;
320  if (value > 0.0 && value < kSmallestPositiveFloatValue)
321  {
322  value = 0;
323  retValue = true;
324  }
325  else if (value < 0.0 && value > kSmallestNegativeFloatValue)
326  {
327  value = 0;
328  retValue = true;
329  }
330  return retValue;
331  }
332  private:
333  SimpleLPF lpf;
334  double attackTime = 0.0;
335  double releaseTime = 0.0;
336  double sampleRate = 44100;
337  double samplePeriod_T = 1.0;
338  double lastEnvelope = 0.0;
339  double peakStore = 0.0;
340 
343  void setAttackTime(double attack_in_ms)
344  {
345  // --- calculate 10% - 90% risetime
346  attackTime = 1.0 - exp((-2.2*samplePeriod_T) / (attack_in_ms * 0.001));
347  }
348 
351  void setReleaseTime(double release_in_ms)
352  {
353  // --- calculate 10% - 90% falltime
354  releaseTime = 1.0 - exp((-2.2*samplePeriod_T) / (release_in_ms * 0.001));
355  }
356  };
357 
358  // ---
359 
373  class Limiter
374  {
375  public:
376  Limiter() {}
377  ~Limiter() {}
378 
381  void reset(double _sampleRate)
382  {
383  // --- reset detector (always first)
384  linDetector.reset(_sampleRate);
385  logDetector.reset(_sampleRate);
386 
387  // --- init;
389  10.0, /* attack time mSec*/
390  200.0 /* release time mSec*/);
392  10.0, /* attack time mSec*/
393  200.0 /* release time mSec*/);
394  }
395 
398  void setThreshold_dB(double _threshold_dB) { threshold_dB = _threshold_dB; }
399 
402  void setThreshold(double _threshold) { threshold = _threshold; }
403 
410  double calcLimiterGaindB(float fDetectorValue, float fThreshold)
411  {
412  // --- slope variable // limiting is infinite ratio thus CS->1.0
413  // you can play with this for compression CS < 1.0
414  double CS = 1.0; // 1.0;
415 
416  // --- compute gain; threshold and detection values are in dB
417  double yG = CS*(fThreshold - fDetectorValue); // [Eq. 13.1]
418 
419  // --- clamp; this allows ratios of 1:1 to still operate
420  yG = fmin(0.0, yG);
421 
422  // --- convert back to linear
423  return pow(10.0, yG / 20.0);
424  }
425 
432  double calcLimiterGain(float fDetectorValue, float fThreshold)
433  {
434  // --- slope variable // limiting is infinite ratio thus CS->1.0
435  // you can play with this for compression CS < 1.0
436  double CS = 1.0; // 1.0;
437 
438  // --- compute gain; threshold and detection values are in raw form
439  double yG = (fThreshold - fDetectorValue); // [Eq. 13.1]
440 
441  // --- clamp; this allows ratios of 1:1 to still operate
442  yG = yG > 0.0 ? 1.0 : CS*(fThreshold / fDetectorValue);
443 
444  // --- already linear
445  return yG;
446  }
447 
453  double process(double input)
454  {
455  //double detectedValue_dB = logDetector.processAudioSample(input);
456  //double gain = calcLimiterGaindB(detectedValue_dB, threshold_dB);
457  double detectedValue = linDetector.processAudioSample(input);
458  double gain = calcLimiterGain(detectedValue, threshold);
459  return input*gain;
460  }
461 
462  protected:
464  double threshold_dB = -1.5;
465 
467  double threshold = 0.8413;
468  };
469 
470 } // namespace
Encapsulates McNally&#39;s peak detector with added LPF smoothing (optional)
Definition: limiter.h:82
LogPeakDetector logDetector
the peak detector
Definition: limiter.h:463
double threshold_dB
hardcoded threshold in dB
Definition: limiter.h:464
Encapsulates a tiny first order, all-pole LPF object.
Definition: limiter.h:31
virtual double processAudioSample(double xn)
Definition: limiter.h:56
void setThreshold(double _threshold)
set threshold in dB
Definition: limiter.h:402
void setLPF_g(double _lpf_g)
Definition: limiter.h:49
double process(double input)
process one sample through limiter
Definition: limiter.h:453
bool checkFloatUnderflow(double &value)
set check for underflow
Definition: limiter.h:317
LinPeakDetector linDetector
the peak detector
Definition: limiter.h:466
Definition: addosccore.cpp:4
virtual bool reset(double _sampleRate)
reset members to initialized state
Definition: limiter.h:239
void setAttackReleaseTimes(double attack_in_ms, double release_in_ms)
set attack/release times
Definition: limiter.h:160
void reset(double _sampleRate)
reset internal detector
Definition: limiter.h:381
double calcLimiterGain(float fDetectorValue, float fThreshold)
calculate limiter gain using dB values for threshold and detection
Definition: limiter.h:432
virtual double processAudioSample(double xn)
process MONO input
Definition: limiter.h:259
bool checkFloatUnderflow(double &value)
set check for underflow
Definition: limiter.h:174
virtual bool reset(double _sampleRate)
reset members to initialized state
Definition: limiter.h:91
Encapsulates McNally&#39;s peak detector with added LPF smoothing (optional)
Definition: limiter.h:230
Implements a custom peak limiter designed especially for self oscillating filters whose outputs are >...
Definition: limiter.h:373
double threshold
hardcoded threshold for -1.5dB
Definition: limiter.h:467
virtual bool reset(double _sampleRate)
Definition: limiter.h:39
virtual double processAudioSample(double xn)
process MONO input
Definition: limiter.h:111
See also Designing Software Synthesizers in C++ 2nd Ed. by Will Pirkle.
double calcLimiterGaindB(float fDetectorValue, float fThreshold)
calculate limiter gain using dB values for threshold and detection
Definition: limiter.h:410
void setThreshold_dB(double _threshold_dB)
set threshold in dB
Definition: limiter.h:398
void setAttackReleaseTimes(double attack_in_ms, double release_in_ms)
set attack/release times
Definition: limiter.h:303
See also Designing Software Synthesizers in C++ 2nd Ed. by Will Pirkle.