1 /* Calf DSP Library
2  * Basic "inertia" (parameter smoothing) classes.
3  * Copyright (C) 2001-2007 Krzysztof Foltman
4  *
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.
9  *
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.
14  *
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.
19  */
20 #ifndef __CALF_INERTIA_H
21 #define __CALF_INERTIA_H
22 
23 #include "primitives.h"
24 
25 namespace dsp {
26 
27 /// Algorithm for a constant time linear ramp
28 class linear_ramp
29 {
30 public:
31     int ramp_len;
32     float mul, delta;
33 public:
34     /// Construct for given ramp length
linear_ramp(int _ramp_len)35     linear_ramp(int _ramp_len) {
36         ramp_len = _ramp_len;
37         mul = (float)(1.0f / ramp_len);
38         delta = 0.f;
39     }
40     /// Change ramp length
set_length(int _ramp_len)41     inline void set_length(int _ramp_len) {
42         ramp_len = _ramp_len;
43         mul = (float)(1.0f / ramp_len);
44     }
length()45     inline int length()
46     {
47         return ramp_len;
48     }
start_ramp(float start,float end)49     inline void start_ramp(float start, float end)
50     {
51         delta = mul * (end - start);
52     }
53     /// Return value after single step
ramp(float value)54     inline float ramp(float value)
55     {
56         return value + delta;
57     }
58     /// Return value after many steps
ramp_many(float value,int count)59     inline float ramp_many(float value, int count)
60     {
61         return value + delta * count;
62     }
63 };
64 
65 /// Algorithm for a constant time linear ramp
66 class exponential_ramp
67 {
68 public:
69     int ramp_len;
70     float root, delta;
71 public:
exponential_ramp(int _ramp_len)72     exponential_ramp(int _ramp_len) {
73         ramp_len = _ramp_len;
74         root = (float)(1.0f / ramp_len);
75         delta = 1.0;
76     }
set_length(int _ramp_len)77     inline void set_length(int _ramp_len) {
78         ramp_len = _ramp_len;
79         root = (float)(1.0f / ramp_len);
80     }
length()81     inline int length()
82     {
83         return ramp_len;
84     }
start_ramp(float start,float end)85     inline void start_ramp(float start, float end)
86     {
87         delta = pow(end / start, root);
88     }
89     /// Return value after single step
ramp(float value)90     inline float ramp(float value)
91     {
92         return value * delta;
93     }
94     /// Return value after many steps
ramp_many(float value,float count)95     inline float ramp_many(float value, float count)
96     {
97         return value * pow(delta, count);
98     }
99 };
100 
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
108 {
109 public:
110     float old_value;
111     float value;
112     unsigned int count;
113     Ramp ramp;
114 
115 public:
116     inertia(const Ramp &_ramp, float init_value = 0.f)
ramp(_ramp)117     : ramp(_ramp)
118     {
119         value = old_value = init_value;
120         count = 0;
121     }
122     /// Set value immediately (no inertia)
set_now(float _value)123     void set_now(float _value)
124     {
125         value = old_value = _value;
126         count = 0;
127     }
128     /// Set with inertia
set_inertia(float source)129     void set_inertia(float source)
130     {
131         if (source != old_value) {
132             ramp.start_ramp(value, source);
133             count = ramp.length();
134             old_value = source;
135         }
136     }
137     /// Get smoothed value of given source value
get(float source)138     inline float get(float source)
139     {
140         if (source != old_value) {
141             ramp.start_ramp(value, source);
142             count = ramp.length();
143             old_value = source;
144         }
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;
152     }
153     /// Get smoothed value assuming no new input
get()154     inline float get()
155     {
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;
163     }
164     /// Do one inertia step, without returning the new value and without changing destination value
step()165     inline void step()
166     {
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;
172         }
173     }
174     /// Do many inertia steps, without returning the new value and without changing destination value
step_many(unsigned int steps)175     inline void step_many(unsigned int steps)
176     {
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;
183         }
184         else
185         {
186             // The whole ramping period has been skipped, just go to destination
187             value = old_value;
188             count = 0;
189         }
190     }
191     /// Get last smoothed value, without affecting anything
get_last()192     inline float get_last() const
193     {
194         return value;
195     }
196     /// Is it still ramping?
active()197     inline bool active() const
198     {
199         return count > 0;
200     }
201 };
202 
203 class once_per_n
204 {
205 public:
206     unsigned int frequency;
207     unsigned int left;
208 public:
once_per_n(unsigned int _frequency)209     once_per_n(unsigned int _frequency)
210     : frequency(_frequency), left(_frequency)
211     {}
start()212     inline void start()
213     {
214         left = frequency;
215     }
216     /// Set timer to "elapsed" state (elapsed() will return true during next call)
signal()217     inline void signal()
218     {
219         left = 0;
220     }
get(unsigned int desired)221     inline unsigned int get(unsigned int desired)
222     {
223         if (desired > left) {
224             desired = left;
225             left = 0;
226             return desired;
227         }
228         left -= desired;
229         return desired;
230     }
elapsed()231     inline bool elapsed()
232     {
233         if (!left) {
234             left = frequency;
235             return true;
236         }
237         return false;
238     }
239 };
240 
241 class gain_smoothing: public inertia<linear_ramp>
242 {
243 public:
gain_smoothing()244     gain_smoothing()
245     : inertia<linear_ramp>(linear_ramp(64))
246     {
247     }
set_sample_rate(int sr)248     void set_sample_rate(int sr)
249     {
250         ramp = linear_ramp(sr / 100);
251     }
252     // to change param, use set_inertia(value)
253     // to read param, use get()
254 };
255 
256 template < typename T >
257 class switcher
258 {
259 private:
260     T cont_val_cur;
261     T cont_val_prev;
262     bool is_active;
263     double step;
264     double acc;
265 public:
switcher(unsigned int samples)266     switcher(unsigned int samples) : cont_val_cur(), cont_val_prev()
267     {
268         step = 1.0/samples;
269         acc = 0;
270         is_active = false;
271     }
272 
set(T ctr)273     void set(T ctr)
274     {
275         cont_val_cur = ctr;
276         is_active = true;
277     }
278 
set_previous(T ctr)279     void set_previous(T ctr)
280     {
281         cont_val_prev = ctr;
282     }
283 
get_state()284     T get_state()
285     {
286         return cont_val_prev;
287     }
288 
get_ramp()289     double get_ramp()
290     {
291         if(is_active) {
292             if(acc < 0.5) {
293                 /// Decrease value to zero
294                 acc+= step;
295                 return 1 - 2*acc;
296             }
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;
302             }
303             else if(acc > 1) {
304                 /// Switching finished
305                 acc = 0;
306                 is_active = false;
307                 return 1;
308             }
309             else
310                 return 1;
311         }
312         else
313             return 1;
314     }
315 };
316 
317 }
318 
319 #endif
320