Make Analyzer UI require instance-access
[calf.git] / src / calf / inertia.h
blobf09e58599cfefc19f092c7f27aebe8b7c802a802
1 /* Calf DSP Library
2 * Basic "inertia" (parameter smoothing) classes.
3 * Copyright (C) 2001-2007 Krzysztof Foltman
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General
16 * Public License along with this program; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18 * Boston, MA 02111-1307, USA.
20 #ifndef __CALF_INERTIA_H
21 #define __CALF_INERTIA_H
23 #include "primitives.h"
25 namespace dsp {
27 /// Algorithm for a constant time linear ramp
28 class linear_ramp
30 public:
31 int ramp_len;
32 float mul, delta;
33 public:
34 /// Construct for given ramp length
35 linear_ramp(int _ramp_len) {
36 ramp_len = _ramp_len;
37 mul = (float)(1.0f / ramp_len);
38 delta = 0.f;
40 /// Change ramp length
41 inline void set_length(int _ramp_len) {
42 ramp_len = _ramp_len;
43 mul = (float)(1.0f / ramp_len);
45 inline int length()
47 return ramp_len;
49 inline void start_ramp(float start, float end)
51 delta = mul * (end - start);
53 /// Return value after single step
54 inline float ramp(float value)
56 return value + delta;
58 /// Return value after many steps
59 inline float ramp_many(float value, int count)
61 return value + delta * count;
65 /// Algorithm for a constant time linear ramp
66 class exponential_ramp
68 public:
69 int ramp_len;
70 float root, delta;
71 public:
72 exponential_ramp(int _ramp_len) {
73 ramp_len = _ramp_len;
74 root = (float)(1.0f / ramp_len);
75 delta = 1.0;
77 inline void set_length(int _ramp_len) {
78 ramp_len = _ramp_len;
79 root = (float)(1.0f / ramp_len);
81 inline int length()
83 return ramp_len;
85 inline void start_ramp(float start, float end)
87 delta = pow(end / start, root);
89 /// Return value after single step
90 inline float ramp(float value)
92 return value * delta;
94 /// Return value after many steps
95 inline float ramp_many(float value, float count)
97 return value * pow(delta, count);
101 /// Generic inertia using ramping algorithm specified as template argument. The basic idea
102 /// is producing smooth(ish) output for discrete input, using specified algorithm to go from
103 /// last output value to input value. It is not the same as classic running average lowpass
104 /// filter, because ramping time is finite and pre-determined (it calls ramp algorithm's length()
105 /// function to obtain the expected ramp length)
106 template<class Ramp>
107 class inertia
109 public:
110 float old_value;
111 float value;
112 unsigned int count;
113 Ramp ramp;
115 public:
116 inertia(const Ramp &_ramp, float init_value = 0.f)
117 : ramp(_ramp)
119 value = old_value = init_value;
120 count = 0;
122 /// Set value immediately (no inertia)
123 void set_now(float _value)
125 value = old_value = _value;
126 count = 0;
128 /// Set with inertia
129 void set_inertia(float source)
131 if (source != old_value) {
132 ramp.start_ramp(value, source);
133 count = ramp.length();
134 old_value = source;
137 /// Get smoothed value of given source value
138 inline float get(float source)
140 if (source != old_value) {
141 ramp.start_ramp(value, source);
142 count = ramp.length();
143 old_value = source;
145 if (!count)
146 return old_value;
147 value = ramp.ramp(value);
148 count--;
149 if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
150 value = old_value;
151 return value;
153 /// Get smoothed value assuming no new input
154 inline float get()
156 if (!count)
157 return old_value;
158 value = ramp.ramp(value);
159 count--;
160 if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
161 value = old_value;
162 return value;
164 /// Do one inertia step, without returning the new value and without changing destination value
165 inline void step()
167 if (count) {
168 value = ramp.ramp(value);
169 count--;
170 if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
171 value = old_value;
174 /// Do many inertia steps, without returning the new value and without changing destination value
175 inline void step_many(unsigned int steps)
177 if (steps < count) {
178 // Skip only a part of the current ramping period
179 value = ramp.ramp_many(value, steps);
180 count -= steps;
181 if (!count) // finished ramping, set to desired value to get rid of accumulated rounding errors
182 value = old_value;
184 else
186 // The whole ramping period has been skipped, just go to destination
187 value = old_value;
188 count = 0;
191 /// Get last smoothed value, without affecting anything
192 inline float get_last() const
194 return value;
196 /// Is it still ramping?
197 inline bool active() const
199 return count > 0;
203 class once_per_n
205 public:
206 unsigned int frequency;
207 unsigned int left;
208 public:
209 once_per_n(unsigned int _frequency)
210 : frequency(_frequency), left(_frequency)
212 inline void start()
214 left = frequency;
216 /// Set timer to "elapsed" state (elapsed() will return true during next call)
217 inline void signal()
219 left = 0;
221 inline unsigned int get(unsigned int desired)
223 if (desired > left) {
224 desired = left;
225 left = 0;
226 return desired;
228 left -= desired;
229 return desired;
231 inline bool elapsed()
233 if (!left) {
234 left = frequency;
235 return true;
237 return false;
241 class gain_smoothing: public inertia<linear_ramp>
243 public:
244 gain_smoothing()
245 : inertia<linear_ramp>(linear_ramp(64))
248 void set_sample_rate(int sr)
250 ramp = linear_ramp(sr / 100);
252 // to change param, use set_inertia(value)
253 // to read param, use get()
256 template < typename T >
257 class switcher
259 private:
260 T cont_val_cur;
261 T cont_val_prev;
262 bool is_active;
263 double step;
264 double acc;
265 public:
266 switcher(unsigned int samples) : cont_val_cur(), cont_val_prev()
268 step = 1.0/samples;
269 acc = 0;
270 is_active = false;
273 void set(T ctr)
275 cont_val_cur = ctr;
276 is_active = true;
279 void set_previous(T ctr)
281 cont_val_prev = ctr;
284 T get_state()
286 return cont_val_prev;
289 double get_ramp()
291 if(is_active) {
292 if(acc < 0.5) {
293 /// Decrease value to zero
294 acc+= step;
295 return 1 - 2*acc;
297 else if(acc >= 0.5 && acc <= 1.0) {
298 /// Switch and increase value to one
299 cont_val_prev = cont_val_cur;
300 acc+= step;
301 return (acc - 0.5)*2;
303 else if(acc > 1) {
304 /// Switching finished
305 acc = 0;
306 is_active = false;
307 return 1;
309 else
310 return 1;
312 else
313 return 1;
319 #endif