DISTRHO Plugin Framework
ValueSmoother.hpp
1 /*
2  * DISTRHO Plugin Framework (DPF)
3  * Copyright (C) 2021 Jean Pierre Cimalando <jp-dev@inbox.ru>
4  * Copyright (C) 2021-2023 Filipe Coelho <falktx@falktx.com>
5  *
6  * Permission to use, copy, modify, and/or distribute this software for any purpose with
7  * or without fee is hereby granted, provided that the above copyright notice and this
8  * permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
11  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
12  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
13  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
14  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
15  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #ifndef DISTRHO_VALUE_SMOOTHER_HPP_INCLUDED
19 #define DISTRHO_VALUE_SMOOTHER_HPP_INCLUDED
20 
21 #include "../DistrhoUtils.hpp"
22 
24 
25 // --------------------------------------------------------------------------------------------------------------------
26 
27 /**
28  * @brief An exponential smoother for control values
29  *
30  * This continually smooths a value towards a defined target,
31  * using a low-pass filter of the 1st order, which creates an exponential curve.
32  *
33  * The length of the curve is defined by a T60 constant,
34  * which is the time it takes for a 1-to-0 smoothing to fall to -60dB.
35  *
36  * Note that this smoother has asymptotical behavior,
37  * and it must not be assumed that the final target is ever reached.
38  */
40  float coef;
41  float target;
42  float mem;
43  float tau;
44  float sampleRate;
45 
46 public:
48  : coef(0.f),
49  target(0.f),
50  mem(0.f),
51  tau(0.f),
52  sampleRate(0.f) {}
53 
54  void setSampleRate(const float newSampleRate) noexcept
55  {
56  if (d_isNotEqual(sampleRate, newSampleRate))
57  {
58  sampleRate = newSampleRate;
59  updateCoef();
60  }
61  }
62 
63  void setTimeConstant(const float newT60) noexcept
64  {
65  const float newTau = newT60 * (float)(1.0 / 6.91);
66 
67  if (d_isNotEqual(tau, newTau))
68  {
69  tau = newTau;
70  updateCoef();
71  }
72  }
73 
74  float getCurrentValue() const noexcept
75  {
76  return mem;
77  }
78 
79  float getTargetValue() const noexcept
80  {
81  return target;
82  }
83 
84  void setTargetValue(const float newTarget) noexcept
85  {
86  target = newTarget;
87  }
88 
89  void clearToTargetValue() noexcept
90  {
91  mem = target;
92  }
93 
94  inline float peek() const noexcept
95  {
96  return mem * coef + target * (1.f - coef);
97  }
98 
99  inline float next() noexcept
100  {
101  return (mem = mem * coef + target * (1.f - coef));
102  }
103 
104 private:
105  void updateCoef() noexcept
106  {
107  coef = std::exp(-1.f / (tau * sampleRate));
108  }
109 };
110 
111 // --------------------------------------------------------------------------------------------------------------------
112 
113 /**
114  * @brief A linear smoother for control values
115  *
116  * This continually smooths a value towards a defined target, using linear segments.
117  *
118  * The duration of the smoothing segment is defined by the given time constant.
119  * Every time the target changes, a new segment restarts for the whole duration of the time constant.
120  *
121  * Note that this smoother, unlike an exponential smoother, eventually should converge to its target value.
122  */
124  float step;
125  float target;
126  float mem;
127  float tau;
128  float sampleRate;
129 
130 public:
132  : step(0.f),
133  target(0.f),
134  mem(0.f),
135  tau(0.f),
136  sampleRate(0.f) {}
137 
138  void setSampleRate(const float newSampleRate) noexcept
139  {
140  if (d_isNotEqual(sampleRate, newSampleRate))
141  {
142  sampleRate = newSampleRate;
143  updateStep();
144  }
145  }
146 
147  void setTimeConstant(const float newTau) noexcept
148  {
149  if (d_isNotEqual(tau, newTau))
150  {
151  tau = newTau;
152  updateStep();
153  }
154  }
155 
156  float getCurrentValue() const noexcept
157  {
158  return mem;
159  }
160 
161  float getTargetValue() const noexcept
162  {
163  return target;
164  }
165 
166  void setTargetValue(const float newTarget) noexcept
167  {
168  if (d_isNotEqual(target, newTarget))
169  {
170  target = newTarget;
171  updateStep();
172  }
173  }
174 
175  void clearToTargetValue() noexcept
176  {
177  mem = target;
178  }
179 
180  inline float peek() const noexcept
181  {
182  const float dy = target - mem;
183  return mem + std::copysign(std::fmin(std::abs(dy), std::abs(step)), dy);
184  }
185 
186  inline float next() noexcept
187  {
188  const float y0 = mem;
189  const float dy = target - y0;
190  return (mem = y0 + std::copysign(std::fmin(std::abs(dy), std::abs(step)), dy));
191  }
192 
193 private:
194  void updateStep() noexcept
195  {
196  step = (target - mem) / (tau * sampleRate);
197  }
198 };
199 
200 // --------------------------------------------------------------------------------------------------------------------
201 
203 
204 #endif // DISTRHO_VALUE_SMOOTHER_HPP_INCLUDED
An exponential smoother for control values.
Definition: ValueSmoother.hpp:39
A linear smoother for control values.
Definition: ValueSmoother.hpp:123
static constexpr bool d_isNotEqual(const T &v1, const T &v2)
Definition: DistrhoUtils.hpp:279
#define END_NAMESPACE_DISTRHO
Definition: DistrhoInfo.hpp:949
#define START_NAMESPACE_DISTRHO
Definition: DistrhoInfo.hpp:943