1 // Copyright 2014 Emilie Gillet.
2 //
3 // Author: Emilie Gillet (emilie.o.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.
28 
29 #ifndef CLOUDS_DSP_FX_REVERB_H_
30 #define CLOUDS_DSP_FX_REVERB_H_
31 
32 #include "stmlib/stmlib.h"
33 
34 #include "clouds/dsp/fx/fx_engine.h"
35 
36 namespace clouds {
37 
38 class Reverb {
39  public:
Reverb()40   Reverb() { }
~Reverb()41   ~Reverb() { }
42 
Init(uint16_t * buffer)43   void Init(uint16_t* buffer) {
44     engine_.Init(buffer);
45     engine_.SetLFOFrequency(LFO_1, 0.5f / 32000.0f);
46     engine_.SetLFOFrequency(LFO_2, 0.3f / 32000.0f);
47     lp_ = 0.7f;
48     diffusion_ = 0.625f;
49   }
50 
Process(FloatFrame * in_out,size_t size)51   void Process(FloatFrame* in_out, size_t size) {
52     // This is the Griesinger topology described in the Dattorro paper
53     // (4 AP diffusers on the input, then a loop of 2x 2AP+1Delay).
54     // Modulation is applied in the loop of the first diffuser AP for additional
55     // smearing; and to the two long delays for a slow shimmer/chorus effect.
56     typedef E::Reserve<113,
57       E::Reserve<162,
58       E::Reserve<241,
59       E::Reserve<399,
60       E::Reserve<1653,
61       E::Reserve<2038,
62       E::Reserve<3411,
63       E::Reserve<1913,
64       E::Reserve<1663,
65       E::Reserve<4782> > > > > > > > > > Memory;
66     E::DelayLine<Memory, 0> ap1;
67     E::DelayLine<Memory, 1> ap2;
68     E::DelayLine<Memory, 2> ap3;
69     E::DelayLine<Memory, 3> ap4;
70     E::DelayLine<Memory, 4> dap1a;
71     E::DelayLine<Memory, 5> dap1b;
72     E::DelayLine<Memory, 6> del1;
73     E::DelayLine<Memory, 7> dap2a;
74     E::DelayLine<Memory, 8> dap2b;
75     E::DelayLine<Memory, 9> del2;
76     E::Context c;
77 
78     const float kap = diffusion_;
79     const float klp = lp_;
80     const float krt = reverb_time_;
81     const float amount = amount_;
82     const float gain = input_gain_;
83 
84     float lp_1 = lp_decay_1_;
85     float lp_2 = lp_decay_2_;
86 
87     while (size--) {
88       float wet;
89       float apout = 0.0f;
90       engine_.Start(&c);
91 
92       // Smear AP1 inside the loop.
93       c.Interpolate(ap1, 10.0f, LFO_1, 60.0f, 1.0f);
94       c.Write(ap1, 100, 0.0f);
95 
96       c.Read(in_out->l + in_out->r, gain);
97 
98       // Diffuse through 4 allpasses.
99       c.Read(ap1 TAIL, kap);
100       c.WriteAllPass(ap1, -kap);
101       c.Read(ap2 TAIL, kap);
102       c.WriteAllPass(ap2, -kap);
103       c.Read(ap3 TAIL, kap);
104       c.WriteAllPass(ap3, -kap);
105       c.Read(ap4 TAIL, kap);
106       c.WriteAllPass(ap4, -kap);
107       c.Write(apout);
108 
109       // Main reverb loop.
110       c.Load(apout);
111       c.Interpolate(del2, 4680.0f, LFO_2, 100.0f, krt);
112       c.Lp(lp_1, klp);
113       c.Read(dap1a TAIL, -kap);
114       c.WriteAllPass(dap1a, kap);
115       c.Read(dap1b TAIL, kap);
116       c.WriteAllPass(dap1b, -kap);
117       c.Write(del1, 2.0f);
118       c.Write(wet, 0.0f);
119 
120       in_out->l += (wet - in_out->l) * amount;
121 
122       c.Load(apout);
123       // c.Interpolate(del1, 4450.0f, LFO_1, 50.0f, krt);
124       c.Read(del1 TAIL, krt);
125       c.Lp(lp_2, klp);
126       c.Read(dap2a TAIL, kap);
127       c.WriteAllPass(dap2a, -kap);
128       c.Read(dap2b TAIL, -kap);
129       c.WriteAllPass(dap2b, kap);
130       c.Write(del2, 2.0f);
131       c.Write(wet, 0.0f);
132 
133       in_out->r += (wet - in_out->r) * amount;
134 
135       ++in_out;
136     }
137 
138     lp_decay_1_ = lp_1;
139     lp_decay_2_ = lp_2;
140   }
141 
set_amount(float amount)142   inline void set_amount(float amount) {
143     amount_ = amount;
144   }
145 
set_input_gain(float input_gain)146   inline void set_input_gain(float input_gain) {
147     input_gain_ = input_gain;
148   }
149 
set_time(float reverb_time)150   inline void set_time(float reverb_time) {
151     reverb_time_ = reverb_time;
152   }
153 
set_diffusion(float diffusion)154   inline void set_diffusion(float diffusion) {
155     diffusion_ = diffusion;
156   }
157 
set_lp(float lp)158   inline void set_lp(float lp) {
159     lp_ = lp;
160   }
161 
162  private:
163   typedef FxEngine<16384, FORMAT_12_BIT> E;
164   E engine_;
165 
166   float amount_;
167   float input_gain_;
168   float reverb_time_;
169   float diffusion_;
170   float lp_;
171 
172   float lp_decay_1_;
173   float lp_decay_2_;
174 
175   DISALLOW_COPY_AND_ASSIGN(Reverb);
176 };
177 
178 }  // namespace clouds
179 
180 #endif  // CLOUDS_DSP_FX_REVERB_H_
181