1 // Copyright 2014 Olivier Gillet. 2 // 3 // Author: Olivier Gillet (ol.gillet@gmail.com) 4 // 5 // Permission is hereby granted, free of charge, to any person obtaining a copy 6 // of this software and associated documentation files (the "Software"), to deal 7 // in the Software without restriction, including without limitation the rights 8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 // copies of the Software, and to permit persons to whom the Software is 10 // furnished to do so, subject to the following conditions: 11 // 12 // The above copyright notice and this permission notice shall be included in 13 // all copies or substantial portions of the Software. 14 // 15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 // THE SOFTWARE. 22 // 23 // See http://creativecommons.org/licenses/MIT/ for more information. 24 // 25 // ----------------------------------------------------------------------------- 26 // 27 // Reverb (used in the dedicated "Oliverb" playback mode). 28 29 #ifndef CLOUDS_DSP_FX_OLIVERB_H_ 30 #define CLOUDS_DSP_FX_OLIVERB_H_ 31 32 #include "stmlib/stmlib.h" 33 34 #include "clouds/dsp/fx/fx_engine.h" 35 #include "clouds/dsp/random_oscillator.h" 36 37 namespace clouds { 38 39 class Oliverb { 40 public: Oliverb()41 Oliverb() { } ~Oliverb()42 ~Oliverb() { } 43 Init(uint16_t * buffer)44 void Init(uint16_t* buffer) { 45 engine_.Init(buffer); 46 diffusion_ = 0.625f; 47 size_ = 1.0f; 48 mod_amount_ = 0.0f; 49 mod_rate_ = 0.0f; 50 size_ = 0.5f; 51 input_gain_ = 1.0f; 52 decay_ = 0.5f; 53 lp_ = 1.0f; 54 hp_= 0.0f; 55 phase_ = 0.0f; 56 ratio_ = 0.0f; 57 pitch_shift_amount_ = 1.0f; 58 level_ = 0.0f; 59 for (int i=0; i<9; i++) 60 lfo_[i].Init(); 61 } 62 Process(FloatFrame * in_out,size_t size)63 void Process(FloatFrame* in_out, size_t size) { 64 // This is the Griesinger topology described in the Dattorro paper 65 // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay). 66 // Modulation is applied in the loop of the first diffuser AP for additional 67 // smearing; and to the two long delays for a slow shimmer/chorus effect. 68 typedef E::Reserve<113, /* ap1 */ 69 E::Reserve<162, /* ap2 */ 70 E::Reserve<241, /* ap3 */ 71 E::Reserve<399, /* ap4 */ 72 E::Reserve<1253, /* dap1a */ 73 E::Reserve<1738, /* dap1b */ 74 E::Reserve<3411, /* del1 */ 75 E::Reserve<1513, /* dap2a */ 76 E::Reserve<1363, /* dap2b */ 77 E::Reserve<4782> > > > > > > > > > Memory; /* del2 */ 78 E::DelayLine<Memory, 0> ap1; 79 E::DelayLine<Memory, 1> ap2; 80 E::DelayLine<Memory, 2> ap3; 81 E::DelayLine<Memory, 3> ap4; 82 E::DelayLine<Memory, 4> dap1a; 83 E::DelayLine<Memory, 5> dap1b; 84 E::DelayLine<Memory, 6> del1; 85 E::DelayLine<Memory, 7> dap2a; 86 E::DelayLine<Memory, 8> dap2b; 87 E::DelayLine<Memory, 9> del2; 88 E::Context c; 89 90 const float kap = diffusion_; 91 92 float lp_1 = lp_decay_1_; 93 float lp_2 = lp_decay_2_; 94 float hp_1 = hp_decay_1_; 95 float hp_2 = hp_decay_2_; 96 97 /* Set frequency of LFOs */ 98 float slope = mod_rate_ * mod_rate_; 99 slope *= slope * slope; 100 slope /= 200.0f; 101 for (int i=0; i<9; i++) 102 lfo_[i].set_slope(slope); 103 104 while (size--) { 105 engine_.Start(&c); 106 107 // Smooth parameters to avoid delay glitches 108 ONE_POLE(smooth_size_, size_, 0.01f); 109 110 // compute windowing info for the pitch shifter 111 float ps_size = 128.0f + (3410.0f - 128.0f) * smooth_size_; 112 phase_ += (1.0f - ratio_) / ps_size; 113 if (phase_ >= 1.0f) phase_ -= 1.0f; 114 if (phase_ <= 0.0f) phase_ += 1.0f; 115 float tri = 2.0f * (phase_ >= 0.5f ? 1.0f - phase_ : phase_); 116 tri = Interpolate(lut_window, tri, LUT_WINDOW_SIZE-1); 117 float phase = phase_ * ps_size; 118 float half = phase + ps_size * 0.5f; 119 if (half >= ps_size) half -= ps_size; 120 121 #define INTERPOLATE_LFO(del, lfo, gain) \ 122 { \ 123 float offset = (del.length - 1) * smooth_size_; \ 124 offset += lfo.Next() * mod_amount_; \ 125 CONSTRAIN(offset, 1.0f, del.length - 1); \ 126 c.InterpolateHermite(del, offset, gain); \ 127 } 128 129 #define INTERPOLATE(del, gain) \ 130 { \ 131 float offset = (del.length - 1) * smooth_size_; \ 132 CONSTRAIN(offset, 1.0f, del.length - 1); \ 133 c.InterpolateHermite(del, offset, gain); \ 134 } 135 136 // Smear AP1 inside the loop. 137 c.Interpolate(ap1, 10.0f, LFO_1, 60.0f, 1.0f); 138 c.Write(ap1, 100, 0.0f); 139 140 c.Read(in_out->l + in_out->r, input_gain_); 141 // Diffuse through 4 allpasses. 142 INTERPOLATE_LFO(ap1, lfo_[1], kap); 143 c.WriteAllPass(ap1, -kap); 144 INTERPOLATE_LFO(ap2, lfo_[2], kap); 145 c.WriteAllPass(ap2, -kap); 146 INTERPOLATE_LFO(ap3, lfo_[3], kap); 147 c.WriteAllPass(ap3, -kap); 148 INTERPOLATE_LFO(ap4, lfo_[4], kap); 149 c.WriteAllPass(ap4, -kap); 150 151 float apout; 152 c.Write(apout); 153 154 INTERPOLATE_LFO(del2, lfo_[5], decay_ * (1.0f - pitch_shift_amount_)); 155 /* blend in the pitch shifted feedback */ 156 c.InterpolateHermite(del2, phase, tri * decay_ * pitch_shift_amount_); 157 c.InterpolateHermite(del2, half, (1.0f - tri) * decay_ * pitch_shift_amount_); 158 159 c.Lp(lp_1, lp_); 160 c.Hp(hp_1, hp_); 161 c.SoftLimit(); 162 INTERPOLATE_LFO(dap1a, lfo_[6], -kap); 163 c.WriteAllPass(dap1a, kap); 164 INTERPOLATE(dap1b, kap); 165 c.WriteAllPass(dap1b, -kap); 166 c.Write(del1, 2.0f); 167 c.Write(in_out->l, 0.0f); 168 169 c.Load(apout); 170 171 INTERPOLATE_LFO(del1, lfo_[7], decay_ * (1.0f - pitch_shift_amount_)); 172 /* blend in the pitch shifted feedback */ 173 c.InterpolateHermite(del1, phase, tri * decay_ * pitch_shift_amount_); 174 c.InterpolateHermite(del1, half, (1.0f - tri) * decay_ * pitch_shift_amount_); 175 c.Lp(lp_2, lp_); 176 c.Hp(hp_2, hp_); 177 c.SoftLimit(); 178 INTERPOLATE_LFO(dap2a, lfo_[8], kap); 179 c.WriteAllPass(dap2a, -kap); 180 INTERPOLATE(dap2b, -kap); 181 c.WriteAllPass(dap2b, kap); 182 c.Write(del2, 2.0f); 183 c.Write(in_out->r, 0.0f); 184 185 ++in_out; 186 } 187 188 lp_decay_1_ = lp_1; 189 lp_decay_2_ = lp_2; 190 hp_decay_1_ = hp_1; 191 hp_decay_2_ = hp_2; 192 } 193 set_input_gain(float input_gain)194 inline void set_input_gain(float input_gain) { 195 input_gain_ = input_gain; 196 } 197 set_decay(float decay)198 inline void set_decay(float decay) { 199 decay_ = decay; 200 } 201 set_diffusion(float diffusion)202 inline void set_diffusion(float diffusion) { 203 diffusion_ = diffusion; 204 } 205 set_lp(float lp)206 inline void set_lp(float lp) { 207 lp_ = lp; 208 } 209 set_hp(float hp)210 inline void set_hp(float hp) { 211 hp_ = hp; 212 } 213 set_size(float size)214 inline void set_size(float size) { 215 size_ = size; 216 } 217 set_mod_amount(float mod_amount)218 inline void set_mod_amount(float mod_amount) { 219 mod_amount_ = mod_amount; 220 } 221 set_mod_rate(float mod_rate)222 inline void set_mod_rate(float mod_rate) { 223 mod_rate_ = mod_rate; 224 } 225 set_ratio(float ratio)226 inline void set_ratio(float ratio) { 227 ratio_ = ratio; 228 } 229 set_pitch_shift_amount(float pitch_shift)230 inline void set_pitch_shift_amount(float pitch_shift) { 231 pitch_shift_amount_ = pitch_shift; 232 } 233 234 private: 235 typedef FxEngine<16384, FORMAT_16_BIT> E; 236 E engine_; 237 238 float input_gain_; 239 float decay_; 240 float diffusion_; 241 float lp_; 242 float hp_; 243 float size_, smooth_size_; 244 float mod_amount_; 245 float mod_rate_; 246 float pitch_shift_amount_; 247 248 float lp_decay_1_; 249 float lp_decay_2_; 250 float hp_decay_1_; 251 float hp_decay_2_; 252 253 float phase_; 254 float ratio_; 255 float level_; 256 257 RandomOscillator lfo_[9]; 258 259 DISALLOW_COPY_AND_ASSIGN(Oliverb); 260 }; 261 262 } // namespace clouds 263 264 #endif // CLOUDS_DSP_FX_OLIVERB_H_ 265