1 /* 2 * DspEffectLibrary.h - library with template-based inline-effects 3 * 4 * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net> 5 * 6 * This file is part of LMMS - https://lmms.io 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public 19 * License along with this program (see COPYING); if not, write to the 20 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301 USA. 22 * 23 */ 24 25 26 #ifndef DSP_EFFECT_LIBRARY_H 27 #define DSP_EFFECT_LIBRARY_H 28 29 #include "lmms_math.h" 30 #include "templates.h" 31 #include "lmms_constants.h" 32 #include "lmms_basics.h" 33 34 35 namespace DspEffectLibrary 36 { 37 38 template<typename T> 39 class MonoBase 40 { 41 public: 42 typedef class MonoBypass bypassType; 43 process(sample_t ** _buf,const f_cnt_t _frames)44 static void process( sample_t * * _buf, const f_cnt_t _frames ) 45 { 46 for( f_cnt_t f = 0; f < _frames; ++f ) 47 { 48 _buf[f][0] = T::nextSample( _buf[f][0] ); 49 } 50 } 51 } ; 52 53 template<typename T> 54 class StereoBase 55 { 56 public: 57 typedef class StereoBypass bypassType; 58 process(sample_t ** _buf,const f_cnt_t _frames)59 static void process( sample_t * * _buf, const f_cnt_t _frames ) 60 { 61 for( f_cnt_t f = 0; f < _frames; ++f ) 62 { 63 T::nextSample( _buf[f][0], _buf[f][1] ); 64 } 65 } 66 } ; 67 68 69 template<class FXL, class FXR = FXL> 70 class MonoToStereoAdaptor : public StereoBase<MonoToStereoAdaptor<FXL, FXR> > 71 { 72 public: MonoToStereoAdaptor(const FXL & monoFX)73 MonoToStereoAdaptor( const FXL& monoFX ) : 74 m_leftFX( monoFX ), 75 m_rightFX( monoFX ) 76 { 77 } 78 MonoToStereoAdaptor(const FXL & leftFX,const FXR & rightFX)79 MonoToStereoAdaptor( const FXL& leftFX, const FXR& rightFX ) : 80 m_leftFX( leftFX ), 81 m_rightFX( rightFX ) 82 { 83 } 84 nextSample(sample_t & inLeft,sample_t & inRight)85 void nextSample( sample_t& inLeft, sample_t& inRight ) 86 { 87 inLeft = m_leftFX.nextSample( inLeft ); 88 inRight = m_rightFX.nextSample( inRight ); 89 } 90 leftFX()91 FXL& leftFX() 92 { 93 return( m_leftFX ); 94 } 95 rightFX()96 FXR& rightFX() 97 { 98 return( m_rightFX ); 99 } 100 101 private: 102 FXL m_leftFX; 103 FXR m_rightFX; 104 } ; 105 106 107 template<class FX> 108 class StereoToMonoAdaptor : public MonoBase<StereoToMonoAdaptor<FX> > 109 { 110 public: StereoToMonoAdaptor(const FX & fx)111 StereoToMonoAdaptor( const FX& fx ) : 112 m_FX( fx ) 113 { 114 } 115 nextSample(sample_t in)116 sample_t nextSample( sample_t in ) 117 { 118 sample_t s[2] = { in, in }; 119 m_FX.nextSample( s[0], s[1] ); 120 121 return ( s[0] + s[1] ) / 2.0f; 122 } 123 124 private: 125 FX m_FX; 126 127 } ; 128 129 class MonoBypass : public MonoBase<MonoBypass> 130 { 131 public: nextSample(sample_t in)132 sample_t nextSample( sample_t in ) 133 { 134 return in; 135 } 136 } ; 137 138 139 class StereoBypass : public StereoBase<StereoBypass> 140 { 141 public: nextSample(sample_t &,sample_t &)142 void nextSample( sample_t&, sample_t& ) 143 { 144 } 145 } ; 146 147 /* convenient class to build up static FX chains, for example 148 149 using namespace DspEffectLib; 150 chain<MonoToStereoAdaptor<bassBoost<> >, 151 chain<StereoEnhancer<>, 152 MonoToStereoAdaptor<FoldbackDistortion<> > > > 153 fxchain( bassBoost<>( 60.0, 1.0, 4.0f ), 154 chain<StereoEnhancer<>, 155 MonoToStereoAdaptor<FoldbackDistortion<> > >( 156 StereoEnhancer<>( 1.0 ), 157 FoldbackDistortion<>( 1.0f, 1.0f ) ) ); 158 159 // now you can do simple calls such as which will process a bass-boost-, 160 // stereo enhancer- and foldback distortion effect on your buffer 161 fx_chain.process( (sample_t * *) buf, frames ); 162 */ 163 164 template<class FX0, class FX1 = typename FX0::bypassType> 165 class Chain : public FX0::bypassType 166 { 167 public: 168 typedef typename FX0::sample_t sample_t; 169 Chain( const FX0& fx0, const FX1& fx1 = FX1() ) : m_FX0(fx0)170 m_FX0( fx0 ), 171 m_FX1( fx1 ) 172 { 173 } 174 process(sample_t ** buf,const f_cnt_t frames)175 void process( sample_t** buf, const f_cnt_t frames ) 176 { 177 m_FX0.process( buf, frames ); 178 m_FX1.process( buf, frames ); 179 } 180 181 private: 182 FX0 m_FX0; 183 FX1 m_FX1; 184 185 } ; 186 187 188 189 template<typename sample_t> saturate(sample_t x)190 inline sample_t saturate( sample_t x ) 191 { 192 return qMin<sample_t>( qMax<sample_t>( -1.0f, x ), 1.0f ); 193 } 194 195 196 class FastBassBoost : public MonoBase<FastBassBoost> 197 { 198 public: 199 FastBassBoost( const sample_t _frequency, 200 const sample_t _gain, 201 const sample_t _ratio, 202 const FastBassBoost & _orig = FastBassBoost() ) : 203 m_frequency( qMax<sample_t>( _frequency, 10.0 ) ), 204 m_gain1( 1.0 / ( m_frequency + 1.0 ) ), 205 m_gain2( _gain ), 206 m_ratio( _ratio ), 207 m_cap( _orig.m_cap ) 208 { 209 } 210 nextSample(sample_t _in)211 inline sample_t nextSample( sample_t _in ) 212 { 213 // TODO: somehow remove these horrible aliases... 214 m_cap = ( _in + m_cap*m_frequency ) * m_gain1; 215 return( ( _in + m_cap*m_ratio ) * m_gain2 ); 216 } 217 setFrequency(const sample_t _frequency)218 void setFrequency( const sample_t _frequency ) 219 { 220 m_frequency = _frequency; 221 m_gain1 = 1.0 / ( m_frequency + 1.0 ); 222 } 223 setGain(const sample_t _gain)224 void setGain( const sample_t _gain ) 225 { 226 m_gain2 = _gain; 227 } 228 setRatio(const sample_t _ratio)229 void setRatio( const sample_t _ratio ) 230 { 231 m_ratio = _ratio; 232 } 233 234 private: FastBassBoost()235 FastBassBoost() : 236 m_cap( 0.0 ) 237 { 238 } 239 240 sample_t m_frequency; 241 sample_t m_gain1; 242 sample_t m_gain2; 243 sample_t m_ratio; 244 sample_t m_cap; 245 } ; 246 247 248 class FoldbackDistortion : public MonoBase<FoldbackDistortion> 249 { 250 public: FoldbackDistortion(float threshold,float gain)251 FoldbackDistortion( float threshold, float gain ) : 252 m_threshold( threshold ), 253 m_gain( gain ) 254 { 255 } 256 nextSample(sample_t in)257 sample_t nextSample( sample_t in ) 258 { 259 if( in >= m_threshold || in < -m_threshold ) 260 { 261 return ( fabsf( fabsf( fmodf( in - m_threshold, m_threshold*4 ) ) - m_threshold*2 ) - m_threshold ) * m_gain; 262 } 263 return in * m_gain; 264 } 265 setThreshold(float threshold)266 void setThreshold( float threshold ) 267 { 268 m_threshold = threshold; 269 } 270 setGain(float gain)271 void setGain( float gain ) 272 { 273 m_gain = gain; 274 } 275 276 277 private: 278 float m_threshold; 279 float m_gain; 280 281 } ; 282 283 284 class Distortion : public MonoBase<Distortion> 285 { 286 public: Distortion(float threshold,float gain)287 Distortion( float threshold, float gain ) : 288 m_threshold( threshold ), 289 m_gain( gain ) 290 { 291 } 292 nextSample(sample_t in)293 sample_t nextSample( sample_t in ) 294 { 295 return m_gain * ( in * ( fabsf( in )+m_threshold ) / ( in*in +( m_threshold-1 )* fabsf( in ) + 1 ) ); 296 } 297 setThreshold(float threshold)298 void setThreshold( float threshold ) 299 { 300 m_threshold = threshold; 301 } 302 setGain(float gain)303 void setGain( float gain ) 304 { 305 m_gain = gain; 306 } 307 308 309 private: 310 float m_threshold; 311 float m_gain; 312 313 } ; 314 315 316 class StereoEnhancer : public StereoBase<StereoEnhancer> 317 { 318 public: StereoEnhancer(float wideCoeff)319 StereoEnhancer( float wideCoeff ) : 320 m_wideCoeff( wideCoeff ) 321 { 322 } 323 setWideCoeff(float wideCoeff)324 void setWideCoeff( float wideCoeff ) 325 { 326 m_wideCoeff = wideCoeff; 327 } 328 wideCoeff()329 float wideCoeff() 330 { 331 return m_wideCoeff; 332 } 333 nextSample(sample_t & inLeft,sample_t & inRight)334 void nextSample( sample_t& inLeft, sample_t& inRight ) 335 { 336 const float toRad = F_PI / 180; 337 const sample_t tmp = inLeft; 338 inLeft += inRight * sinf( m_wideCoeff * ( .5 * toRad ) ); 339 inRight -= tmp * sinf( m_wideCoeff * ( .5 * toRad ) ); 340 } 341 342 private: 343 float m_wideCoeff; 344 345 } ; 346 347 } ; 348 349 350 #endif 351