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