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