1 /* 2 * Delay.h - Delay effect objects to use as building blocks in DSP 3 * 4 * Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi> 5 * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net> 6 * 7 * This file is part of LMMS - https://lmms.io 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public 20 * License along with this program (see COPYING); if not, write to the 21 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301 USA. 23 * 24 */ 25 26 27 #ifndef DELAY_H 28 #define DELAY_H 29 30 #include "lmms_basics.h" 31 #include "lmms_math.h" 32 #include "interpolation.h" 33 #include "MemoryManager.h" 34 35 // brief usage 36 37 // Classes: 38 39 // CombFeedback: a feedback comb filter - basically a simple delay line, makes a comb shape in the freq response 40 // CombFeedfwd: a feed-forward comb filter - an "inverted" comb filter, can be combined with CombFeedback to create a net allpass if negative gain is used 41 // CombFeedbackDualtap: same as CombFeedback but takes two delay values 42 // AllpassDelay: an allpass delay - combines feedback and feed-forward - has flat frequency response 43 44 // all classes are templated with channel count, any arbitrary channel count can be used for each fx 45 46 // Methods (for all classes): 47 48 // setDelay sets delay amount in frames. It's up to you to make this samplerate-agnostic. 49 // Fractions are allowed - linear interpolation is used to deal with them 50 // CombFeedbackDualTap is a special case: it requires 2 delay times 51 52 // setMaxDelay (re)sets the maximum allowed delay, in frames 53 // NOTE: for performance reasons, there's no bounds checking at setDelay, so make sure you set maxDelay >= delay! 54 55 // clearHistory clears the delay buffer 56 57 // setGain sets the feedback/feed-forward gain, in linear amplitude, negative values are allowed 58 // 1.0 is full feedback/feed-forward, -1.0 is full negative feedback/feed-forward 59 60 // update runs the fx for one frame - takes as arguments input and number of channel to run, returns output 61 62 template<ch_cnt_t CHANNELS> 63 class CombFeedback 64 { 65 public: 66 typedef double frame[CHANNELS]; 67 CombFeedback(int maxDelay)68 CombFeedback( int maxDelay ) : 69 m_size( maxDelay ), 70 m_position( 0 ), 71 m_feedBack( 0.0 ), 72 m_delay( 0 ), 73 m_fraction( 0.0 ) 74 { 75 m_buffer = MM_ALLOC( frame, maxDelay ); 76 memset( m_buffer, 0, sizeof( frame ) * maxDelay ); 77 } ~CombFeedback()78 virtual ~CombFeedback() 79 { 80 MM_FREE( m_buffer ); 81 } 82 setMaxDelay(int maxDelay)83 inline void setMaxDelay( int maxDelay ) 84 { 85 if( maxDelay > m_size ) 86 { 87 MM_FREE( m_buffer ); 88 m_buffer = MM_ALLOC( frame, maxDelay ); 89 memset( m_buffer, 0, sizeof( frame ) * maxDelay ); 90 } 91 m_size = maxDelay; 92 m_position %= m_size; 93 } 94 clearHistory()95 inline void clearHistory() 96 { 97 memset( m_buffer, 0, sizeof( frame ) * m_size ); 98 } 99 setDelay(double delay)100 inline void setDelay( double delay ) 101 { 102 m_delay = static_cast<int>( ceil( delay ) ); 103 m_fraction = 1.0 - ( delay - floor( delay ) ); 104 } 105 setGain(double gain)106 inline void setGain( double gain ) 107 { 108 m_gain = gain; 109 } 110 update(double in,ch_cnt_t ch)111 inline double update( double in, ch_cnt_t ch ) 112 { 113 int readPos = m_position - m_delay; 114 if( readPos < 0 ) { readPos += m_size; } 115 116 const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ); 117 118 ++m_position %= m_size; 119 120 m_buffer[m_position][ch] = in + m_gain * y; 121 return y; 122 } 123 124 private: 125 frame * m_buffer; 126 int m_size; 127 int m_position; 128 double m_gain; 129 int m_delay; 130 double m_fraction; 131 }; 132 133 134 template<ch_cnt_t CHANNELS> 135 class CombFeedfwd 136 { 137 typedef double frame[CHANNELS]; 138 CombFeedfwd(int maxDelay)139 CombFeedfwd( int maxDelay ) : 140 m_size( maxDelay ), 141 m_position( 0 ), 142 m_feedBack( 0.0 ), 143 m_delay( 0 ), 144 m_fraction( 0.0 ) 145 { 146 m_buffer = MM_ALLOC( frame, maxDelay ); 147 memset( m_buffer, 0, sizeof( frame ) * maxDelay ); 148 } ~CombFeedfwd()149 virtual ~CombFeedfwd() 150 { 151 MM_FREE( m_buffer ); 152 } 153 setMaxDelay(int maxDelay)154 inline void setMaxDelay( int maxDelay ) 155 { 156 if( maxDelay > m_size ) 157 { 158 MM_FREE( m_buffer ); 159 m_buffer = MM_ALLOC( frame, maxDelay ); 160 memset( m_buffer, 0, sizeof( frame ) * maxDelay ); 161 } 162 m_size = maxDelay; 163 m_position %= m_size; 164 } 165 clearHistory()166 inline void clearHistory() 167 { 168 memset( m_buffer, 0, sizeof( frame ) * m_size ); 169 } 170 setDelay(double delay)171 inline void setDelay( double delay ) 172 { 173 m_delay = static_cast<int>( ceil( delay ) ); 174 m_fraction = 1.0 - ( delay - floor( delay ) ); 175 } 176 setGain(double gain)177 inline void setGain( double gain ) 178 { 179 m_gain = gain; 180 } 181 update(double in,ch_cnt_t ch)182 inline double update( double in, ch_cnt_t ch ) 183 { 184 int readPos = m_position - m_delay; 185 if( readPos < 0 ) { readPos += m_size; } 186 187 const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * m_gain; 188 189 ++m_position %= m_size; 190 191 m_buffer[m_position][ch] = in; 192 return y; 193 } 194 195 private: 196 frame * m_buffer; 197 int m_size; 198 int m_position; 199 double m_gain; 200 int m_delay; 201 double m_fraction; 202 }; 203 204 205 template<ch_cnt_t CHANNELS> 206 class CombFeedbackDualtap 207 { 208 typedef double frame[CHANNELS]; 209 CombFeedbackDualtap(int maxDelay)210 CombFeedbackDualtap( int maxDelay ) : 211 m_size( maxDelay ), 212 m_position( 0 ), 213 m_feedBack( 0.0 ), 214 m_delay( 0 ), 215 m_fraction( 0.0 ) 216 { 217 m_buffer = MM_ALLOC( frame, maxDelay ); 218 memset( m_buffer, 0, sizeof( frame ) * maxDelay ); 219 } ~CombFeedbackDualtap()220 virtual ~CombFeedbackDualtap() 221 { 222 MM_FREE( m_buffer ); 223 } 224 setMaxDelay(int maxDelay)225 inline void setMaxDelay( int maxDelay ) 226 { 227 if( maxDelay > m_size ) 228 { 229 MM_FREE( m_buffer ); 230 m_buffer = MM_ALLOC( frame, maxDelay ); 231 memset( m_buffer, 0, sizeof( frame ) * maxDelay ); 232 } 233 m_size = maxDelay; 234 m_position %= m_size; 235 } 236 clearHistory()237 inline void clearHistory() 238 { 239 memset( m_buffer, 0, sizeof( frame ) * m_size ); 240 } 241 setDelays(double delay1,double delay2)242 inline void setDelays( double delay1, double delay2 ) 243 { 244 m_delay1 = static_cast<int>( ceil( delay1 ) ); 245 m_fraction1 = 1.0 - ( delay1 - floor( delay1 ) ); 246 247 m_delay2 = static_cast<int>( ceil( delay2 ) ); 248 m_fraction2 = 1.0 - ( delay2 - floor( delay2 ) ); 249 } 250 setGain(double gain)251 inline void setGain( double gain ) 252 { 253 m_gain = gain; 254 } 255 update(double in,ch_cnt_t ch)256 inline double update( double in, ch_cnt_t ch ) 257 { 258 int readPos1 = m_position - m_delay1; 259 if( readPos1 < 0 ) { readPos1 += m_size; } 260 261 int readPos2 = m_position - m_delay2; 262 if( readPos2 < 0 ) { readPos2 += m_size; } 263 264 const double y = linearInterpolate( m_buffer[readPos1][ch], m_buffer[( readPos1 + 1 ) % m_size][ch], m_fraction1 ) + 265 linearInterpolate( m_buffer[readPos2][ch], m_buffer[( readPos2 + 1 ) % m_size][ch], m_fraction2 ); 266 267 ++m_position %= m_size; 268 269 m_buffer[m_position][ch] = in + m_gain * y; 270 return y; 271 } 272 273 private: 274 frame * m_buffer; 275 int m_size; 276 int m_position; 277 double m_gain; 278 int m_delay1; 279 int m_delay2; 280 double m_fraction1; 281 double m_fraction2; 282 }; 283 284 285 template<ch_cnt_t CHANNELS> 286 class AllpassDelay 287 { 288 public: 289 typedef double frame[CHANNELS]; 290 AllpassDelay(int maxDelay)291 AllpassDelay( int maxDelay ) : 292 m_size( maxDelay ), 293 m_position( 0 ), 294 m_feedBack( 0.0 ), 295 m_delay( 0 ), 296 m_fraction( 0.0 ) 297 { 298 m_buffer = MM_ALLOC( frame, maxDelay ); 299 memset( m_buffer, 0, sizeof( frame ) * maxDelay ); 300 } ~AllpassDelay()301 virtual ~AllpassDelay() 302 { 303 MM_FREE( m_buffer ); 304 } 305 setMaxDelay(int maxDelay)306 inline void setMaxDelay( int maxDelay ) 307 { 308 if( maxDelay > m_size ) 309 { 310 MM_FREE( m_buffer ); 311 m_buffer = MM_ALLOC( frame, maxDelay ); 312 memset( m_buffer, 0, sizeof( frame ) * maxDelay ); 313 } 314 m_size = maxDelay; 315 m_position %= m_size; 316 } 317 clearHistory()318 inline void clearHistory() 319 { 320 memset( m_buffer, 0, sizeof( frame ) * m_size ); 321 } 322 setDelay(double delay)323 inline void setDelay( double delay ) 324 { 325 m_delay = static_cast<int>( ceil( delay ) ); 326 m_fraction = 1.0 - ( delay - floor( delay ) ); 327 } 328 setGain(double gain)329 inline void setGain( double gain ) 330 { 331 m_gain = gain; 332 } 333 update(double in,ch_cnt_t ch)334 inline double update( double in, ch_cnt_t ch ) 335 { 336 int readPos = m_position - m_delay; 337 if( readPos < 0 ) { readPos += m_size; } 338 339 const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * -m_gain; 340 const double x = in + m_gain * y; 341 342 ++m_position %= m_size; 343 344 m_buffer[m_position][ch] = x; 345 return y; 346 } 347 348 private: 349 frame * m_buffer; 350 int m_size; 351 int m_position; 352 double m_gain; 353 int m_delay; 354 double m_fraction; 355 }; 356 357 // convenience typedefs for stereo effects 358 typedef CombFeedback<2> StereoCombFeedback; 359 typedef CombFeedfwd<2> StereoCombFeedfwd; 360 typedef CombFeedbackDualtap<2> StereoCombFeedbackDualtap; 361 typedef AllpassDelay<2> StereoAllpassDelay; 362 363 #endif 364