1 // Copyright 2015 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 // Resonator.
28 
29 #include "elements/dsp/string.h"
30 
31 #include <cmath>
32 
33 #include "stmlib/dsp/dsp.h"
34 #include "stmlib/dsp/parameter_interpolator.h"
35 #include "stmlib/dsp/units.h"
36 #include "stmlib/utils/random.h"
37 
38 #include "elements/dsp/dsp.h"
39 #include "elements/resources.h"
40 
41 namespace elements {
42 
43 using namespace std;
44 using namespace stmlib;
45 
Init(bool enable_dispersion)46 void String::Init(bool enable_dispersion) {
47   enable_dispersion_ = enable_dispersion;
48 
49   string_.Init();
50   stretch_.Init();
51   fir_damping_filter_.Init();
52   iir_damping_filter_.Init();
53 
54   set_frequency(220.0f / kSampleRate);
55   set_dispersion(0.25f);
56   set_brightness(0.5f);
57   set_damping(0.3f);
58   set_position(0.8f);
59 
60   delay_ = 1.0f / frequency_;
61   clamped_position_ = 0.0f;
62   previous_dispersion_ = 0.0f;
63   dispersion_noise_ = 0.0f;
64   curved_bridge_ = 0.0f;
65   previous_damping_compensation_ = 0.0f;
66 
67   out_sample_[0] = out_sample_[1] = 0.0f;
68   aux_sample_[0] = aux_sample_[1] = 0.0f;
69 
70   dc_blocker_.Init(1.0f - 20.0f / kSampleRate);
71 }
72 
73 template<bool enable_dispersion>
ProcessInternal(const float * in,float * out,float * aux,size_t size)74 void String::ProcessInternal(
75     const float* in,
76     float* out,
77     float* aux,
78     size_t size) {
79   float delay = 1.0f / frequency_;
80   CONSTRAIN(delay, 4.0f, kDelayLineSize - 4.0f);
81 
82   // If there is not enough delay time in the delay line, we play at the
83   // lowest possible note and we upsample on the fly with a shitty linear
84   // interpolator. We don't care because it's a corner case (f0 < 11.7Hz)
85   float src_ratio = delay * frequency_;
86   if (src_ratio >= 0.9999f) {
87     // When we are above 11.7 Hz, we make sure that the linear interpolator
88     // does not get in the way.
89     src_phase_ = 1.0f;
90     src_ratio = 1.0f;
91   }
92 
93   float clamped_position = 0.5f - 0.98f * fabs(position_ - 0.5f);
94 
95   // Linearly interpolate all comb-related CV parameters for each sample.
96   ParameterInterpolator delay_modulation(
97       &delay_, delay, size);
98   ParameterInterpolator position_modulation(
99       &clamped_position_, clamped_position, size);
100   ParameterInterpolator dispersion_modulation(
101       &previous_dispersion_, dispersion_, size);
102 
103   // For damping/absorption, the interpolation is done in the filter code.
104   float lf_damping = damping_ * (2.0f - damping_);
105   float rt60 = 0.07f * SemitonesToRatio(lf_damping * 96.0f) * kSampleRate;
106   float rt60_base_2_12 = max(-120.0f * delay / src_ratio / rt60, -127.0f);
107   float damping_coefficient = SemitonesToRatio(rt60_base_2_12);
108   float brightness = brightness_ * brightness_;
109   float noise_filter = SemitonesToRatio((brightness_ - 1.0f) * 48.0f);
110   float damping_cutoff = min(
111       24.0f + damping_ * damping_ * 48.0f + brightness_ * brightness_ * 24.0f,
112       84.0f);
113   float damping_f = min(frequency_ * SemitonesToRatio(damping_cutoff), 0.499f);
114 
115   // Crossfade to infinite decay.
116   if (damping_ >= 0.95f) {
117     float to_infinite = 20.0f * (damping_ - 0.95f);
118     damping_coefficient += to_infinite * (1.0f - damping_coefficient);
119     brightness += to_infinite * (1.0f - brightness);
120     damping_f += to_infinite * (0.4999f - damping_f);
121     damping_cutoff += to_infinite * (128.0f - damping_cutoff);
122   }
123 
124   fir_damping_filter_.Configure(damping_coefficient, brightness, size);
125   iir_damping_filter_.set_f_q<FREQUENCY_ACCURATE>(damping_f, 0.5f);
126   ParameterInterpolator damping_compensation_modulation(
127       &previous_damping_compensation_,
128       1.0f - Interpolate(lut_svf_shift, damping_cutoff, 1.0f),
129       size);
130 
131   while (size--) {
132     src_phase_ += src_ratio;
133     if (src_phase_ > 1.0f) {
134       src_phase_ -= 1.0f;
135 
136       float delay = delay_modulation.Next();
137       float comb_delay = delay * position_modulation.Next();
138 
139 #ifndef MIC_W
140       delay *= damping_compensation_modulation.Next();  // IIR delay.
141 #endif  // MIC_W
142       delay -= 1.0f; // FIR delay.
143 
144       float s = 0.0f;
145 
146       if (enable_dispersion) {
147         float noise = 2.0f * Random::GetFloat() - 1.0f;
148         noise *= 1.0f / (0.2f + noise_filter);
149         dispersion_noise_ += noise_filter * (noise - dispersion_noise_);
150 
151         float dispersion = dispersion_modulation.Next();
152         float stretch_point = dispersion <= 0.0f
153             ? 0.0f
154             : dispersion * (2.0f - dispersion) * 0.475f;
155         float noise_amount = dispersion > 0.75f
156             ? 4.0f * (dispersion - 0.75f)
157             : 0.0f;
158         float bridge_curving = dispersion < 0.0f
159             ? -dispersion
160             : 0.0f;
161 
162         noise_amount = noise_amount * noise_amount * 0.025f;
163         float ac_blocking_amount = bridge_curving;
164 
165         bridge_curving = bridge_curving * bridge_curving * 0.01f;
166         float ap_gain = -0.618f * dispersion / (0.15f + fabs(dispersion));
167 
168         float delay_fm = 1.0f;
169         delay_fm += dispersion_noise_ * noise_amount;
170         delay_fm -= curved_bridge_ * bridge_curving;
171         delay *= delay_fm;
172 
173         float ap_delay = delay * stretch_point;
174         float main_delay = delay - ap_delay;
175         if (ap_delay >= 4.0f && main_delay >= 4.0f) {
176           s = string_.ReadHermite(main_delay);
177           s = stretch_.Allpass(s, ap_delay, ap_gain);
178         } else {
179           s = string_.ReadHermite(delay);
180         }
181         float s_ac = s;
182         dc_blocker_.Process(&s_ac, 1);
183         s += ac_blocking_amount * (s_ac - s);
184 
185         float value = fabs(s) - 0.025f;
186         float sign = s > 0.0f ? 1.0f : -1.5f;
187         curved_bridge_ = (fabs(value) + value) * sign;
188       } else {
189         s = string_.ReadHermite(delay);
190       }
191 
192       s += *in;  // When f0 < 11.7 Hz, causes ugly bitcrushing on the input!
193       s = fir_damping_filter_.Process(s);
194 #ifndef MIC_W
195       s = iir_damping_filter_.Process<FILTER_MODE_LOW_PASS>(s);
196 #endif  // MIC_W
197       string_.Write(s);
198 
199       out_sample_[1] = out_sample_[0];
200       aux_sample_[1] = aux_sample_[0];
201 
202       out_sample_[0] = s;
203       aux_sample_[0] = string_.Read(comb_delay);
204     }
205     *out++ += Crossfade(out_sample_[1], out_sample_[0], src_phase_);
206     *aux++ += Crossfade(aux_sample_[1], aux_sample_[0], src_phase_);
207     in++;
208   }
209 }
210 
Process(const float * in,float * out,float * aux,size_t size)211 void String::Process(const float* in, float* out, float* aux, size_t size) {
212   if (enable_dispersion_) {
213     ProcessInternal<true>(in, out, aux, size);
214   } else {
215     ProcessInternal<false>(in, out, aux, size);
216   }
217 }
218 
219 }  // namespace elements
220