1 /*
2 ** Surge Synthesizer is Free and Open Source Software
3 **
4 ** Surge is made available under the Gnu General Public License, v3.0
5 ** https://www.gnu.org/licenses/gpl-3.0.en.html
6 **
7 ** Copyright 2004-2021 by various individuals as described by the Git transaction log
8 **
9 ** All source at: https://github.com/surge-synthesizer/surge.git
10 **
11 ** Surge was a commercial product from 2004-2018, with Copyright and ownership
12 ** in that period held by Claes Johanson at Vember Audio. Claes made Surge
13 ** open source in September 2018.
14 */
15 
16 #include "TreemonsterEffect.h"
17 #include "DebugHelpers.h"
18 
19 TreemonsterEffect::TreemonsterEffect(SurgeStorage *storage, FxStorage *fxdata, pdata *pd)
20     : Effect(storage, fxdata, pd), lp(storage), hp(storage)
21 {
22     rm.set_blocksize(BLOCK_SIZE);
23     width.set_blocksize(BLOCK_SIZE);
24     mix.set_blocksize(BLOCK_SIZE);
25 }
26 
27 TreemonsterEffect::~TreemonsterEffect() {}
28 
29 void TreemonsterEffect::init()
30 {
31     setvars(true);
32     bi = 0;
33 }
get_effectname()34 
35 void TreemonsterEffect::setvars(bool init)
36 {
37     if (init)
38     {
39         lp.suspend();
40         hp.suspend();
41 
42         hp.coeff_HP(hp.calc_omega(*f[tm_hp] / 12.0), 0.707);
43         hp.coeff_instantize();
44 
45         lp.coeff_LP2B(lp.calc_omega(*f[tm_lp] / 12.0), 0.707);
46         lp.coeff_instantize();
47 
48         oscL.set_rate(0.f);
49         oscR.set_rate(0.f);
50 
51         rm.set_target(1.f);
52         width.set_target(0.f);
53         mix.set_target(1.f);
54 
55         rm.instantize();
56         width.instantize();
57         mix.instantize();
58 
59         // envelope follower times: 5 ms attack, 500 ms release
60         envA = pow(0.01, 1.0 / (5 * dsamplerate * 0.001));
61         envR = pow(0.01, 1.0 / (500 * dsamplerate * 0.001));
62         envV[0] = 0.f;
63         envV[1] = 0.f;
64 
65         length[0] = 100;
66         length[1] = 100;
67         length_target[0] = 100;
68         length_target[1] = 100;
69         length_smooth[0] = 100;
70         length_smooth[1] = 100;
71         first_thresh[0] = true;
72         first_thresh[1] = true;
73         oscL.set_phase(0);
74         oscR.set_phase(M_PI / 2.0);
75     }
76 }
77 
78 void TreemonsterEffect::process(float *dataL, float *dataR)
79 {
80     float tbuf alignas(16)[2][BLOCK_SIZE];
81     float envscaledSineWave alignas(16)[2][BLOCK_SIZE];
82 
83     auto thres = db_to_linear(limit_range(*f[tm_threshold], fxdata->p[tm_threshold].val_min.f,
84                                           fxdata->p[tm_threshold].val_max.f));
85 
86     // copy dry signal (dataL, dataR) to wet signal (L, R)
87     copy_block(dataL, L, BLOCK_SIZE_QUAD);
88     copy_block(dataR, R, BLOCK_SIZE_QUAD);
89 
90     // copy it to pitch detection buffer (tbuf) as well
91     // in case filters are not activated
92     copy_block(dataL, tbuf[0], BLOCK_SIZE_QUAD);
93     copy_block(dataR, tbuf[1], BLOCK_SIZE_QUAD);
94 
95     // apply filters to the pitch detection buffer
96     if (!fxdata->p[tm_hp].deactivated)
97     {
98         hp.coeff_HP(hp.calc_omega(*f[tm_hp] / 12.0), 0.707);
99         hp.process_block(tbuf[0], tbuf[1]);
100     }
101 
102     if (!fxdata->p[tm_lp].deactivated)
103     {
104         lp.coeff_LP2B(lp.calc_omega(*f[tm_lp] / 12.0), 0.707);
105         lp.process_block(tbuf[0], tbuf[1]);
106     }
107 
108     /*
109      * We assume wavelengths below this are just noisy detection errors. This is used to
110      * clamp when we have a pitch detect basically.
111      */
112     constexpr float smallest_wavelength = 16.0;
113 
114     float qs = clamp01(*f[tm_speed]);
115     qs *= qs * qs * qs;
116     float speed = 0.9999 - qs * 0.0999 / 128;
117     float numberOfSteps = 32 * 48000 * samplerate_inv;
118     for (int i = 0; i < numberOfSteps; ++i)
119     {
120         length_smooth[0] = speed * length_smooth[0] + (1 - speed) * length_target[0];
121         length_smooth[1] = speed * length_smooth[1] + (1 - speed) * length_target[1];
122     }
123 
124     oscL.set_rate((2.0 * M_PI / std::max(2.f, length_smooth[0])) *
125                   powf(2.0, *f[tm_pitch] * (1 / 12.f)));
126     oscR.set_rate((2.0 * M_PI / std::max(2.f, length_smooth[1])) *
127                   powf(2.0, *f[tm_pitch] * (1 / 12.f)));
128 
129     for (int k = 0; k < BLOCK_SIZE; k++)
130     {
131         // envelope detection
132         for (int c = 0; c < 2; ++c)
133         {
134             auto v = (c == 0 ? dataL[k] : dataR[k]);
135             auto e = envV[c];
136 
137             if (v > e)
138             {
139                 e = envA * (e - v) + v;
140             }
141             else
142             {
143                 e = envR * (e - v) + v;
144             }
145 
146             envV[c] = e;
147         }
148 
149         // pitch detection
150         if ((lastval[0] < 0.f) && (tbuf[0][k] >= 0.f))
151         {
152             if (tbuf[0][k] > thres && length[0] > smallest_wavelength)
153             {
154                 length_target[0] =
155                     (length[0] > length_smooth[0] * 10 ? length_smooth[0] : length[0]);
156                 if (first_thresh[0])
157                     length_smooth[0] = length[0];
158                 first_thresh[0] = false;
159             }
160 
161             length[0] = 0.0; // (0.0-lastval[0]) / ( tbuf[0][k] - lastval[0]);
162         }
163 
164         if ((lastval[1] < 0.f) && (tbuf[1][k] >= 0.f))
165         {
166             if (tbuf[1][k] > thres && length[1] > smallest_wavelength)
167             {
168                 length_target[1] =
169                     (length[1] > length_smooth[1] * 10 ? length_smooth[1] : length[1]);
170                 if (first_thresh[1])
171                     length_smooth[1] = length[1];
172                 first_thresh[1] = false;
173             }
174 
175             length[1] = 0.0; // (0.0-lastval[1]) / ( tbuf[1][k] - lastval[1]);
176         }
177 
178         oscL.process();
179         oscR.process();
180 
181         // do not apply followed envelope to sine oscillator - we need full freight sine for RM
182         L[k] = oscL.r;
183         R[k] = oscR.r;
184 
185         // but we need to store the scaled for mix
186         envscaledSineWave[0][k] = oscL.r * envV[0];
187         envscaledSineWave[1][k] = oscR.r * envV[0];
188 
189         // track positive zero crossings
190         length[0] += 1.0f;
191         length[1] += 1.0f;
192 
193         lastval[0] = tbuf[0][k];
194         lastval[1] = tbuf[1][k];
195     }
196 
197     // do dry signal * pitch tracked signal ringmod
198     // store to pitch detection buffer
199     mul_block(L, dataL, tbuf[0], BLOCK_SIZE_QUAD);
200     mul_block(R, dataR, tbuf[1], BLOCK_SIZE_QUAD);
201 
202     // mix pure pitch tracked sine with ring modulated signal
203     rm.set_target_smoothed(clamp01(*f[tm_ring_mix]));
204     rm.fade_2_blocks_to(envscaledSineWave[0], tbuf[0], envscaledSineWave[1], tbuf[1], L, R,
205                         BLOCK_SIZE_QUAD);
206 
207     // scale width
208     width.set_target_smoothed(clamp1bp(*f[tm_width]));
209     float M alignas(16)[BLOCK_SIZE], S alignas(16)[BLOCK_SIZE];
210     encodeMS(L, R, M, S, BLOCK_SIZE_QUAD);
211     width.multiply_block(S, BLOCK_SIZE_QUAD);
212     decodeMS(M, S, L, R, BLOCK_SIZE_QUAD);
213 
214     // main dry-wet mix
215     mix.set_target_smoothed(clamp01(*f[tm_mix]));
216     mix.fade_2_blocks_to(dataL, L, dataR, R, dataL, dataR, BLOCK_SIZE_QUAD);
217 }
218 
219 void TreemonsterEffect::suspend() { init(); }
220 
221 const char *TreemonsterEffect::group_label(int id)
222 {
223     switch (id)
224     {
225     case 0:
226         return "Pitch Detection";
227     case 1:
228         return "Oscillator";
229     case 2:
230         return "Output";
231     }
232     return 0;
233 }
234 int TreemonsterEffect::group_label_ypos(int id)
235 {
236     switch (id)
237     {
238     case 0:
239         return 1;
240     case 1:
241         return 11;
242     case 2:
243         return 17;
244     }
245     return 0;
246 }
247 
248 void TreemonsterEffect::init_ctrltypes()
249 {
250     Effect::init_ctrltypes();
251 
252     fxdata->p[tm_threshold].set_name("Threshold");
253     fxdata->p[tm_threshold].set_type(ct_decibel_attenuation_large);
254     fxdata->p[tm_threshold].val_default.f = -24.f;
255     fxdata->p[tm_threshold].posy_offset = 1;
256     fxdata->p[tm_speed].set_name("Speed");
257     fxdata->p[tm_speed].set_type(ct_percent);
258     fxdata->p[tm_speed].val_default.f = 0.5f;
259     fxdata->p[tm_speed].posy_offset = 1;
260     fxdata->p[tm_hp].set_name("Low Cut");
261     fxdata->p[tm_hp].set_type(ct_freq_audible_deactivatable);
262     fxdata->p[tm_hp].posy_offset = 1;
263     fxdata->p[tm_lp].set_name("High Cut");
264     fxdata->p[tm_lp].set_type(ct_freq_audible_deactivatable);
265     fxdata->p[tm_lp].posy_offset = 1;
266 
267     fxdata->p[tm_pitch].set_name("Pitch");
268     fxdata->p[tm_pitch].set_type(ct_pitch);
269     fxdata->p[tm_pitch].posy_offset = 3;
270     fxdata->p[tm_ring_mix].set_name("Ring Modulation");
271     fxdata->p[tm_ring_mix].set_type(ct_percent);
272     fxdata->p[tm_ring_mix].val_default.f = 0.5f;
273     fxdata->p[tm_ring_mix].posy_offset = 3;
274 
275     fxdata->p[tm_width].set_name("Width");
276     fxdata->p[tm_width].set_type(ct_percent_bipolar);
277     fxdata->p[tm_width].posy_offset = 5;
278     fxdata->p[tm_mix].set_name("Mix");
279     fxdata->p[tm_mix].set_type(ct_percent);
280     fxdata->p[tm_mix].posy_offset = 5;
281     fxdata->p[tm_mix].val_default.f = 1.f;
282 }
283 
284 void TreemonsterEffect::init_default_values()
285 {
286     fxdata->p[tm_threshold].val.f = -24.f;
287     fxdata->p[tm_speed].val.f = 0.5f;
288 
289     fxdata->p[tm_hp].val.f = fxdata->p[tm_hp].val_min.f;
290     fxdata->p[tm_hp].deactivated = false;
291     fxdata->p[tm_lp].val.f = fxdata->p[tm_lp].val_max.f;
292     fxdata->p[tm_lp].deactivated = false;
293 
294     fxdata->p[tm_pitch].val.f = 0;
295     fxdata->p[tm_ring_mix].val.f = 0.5f;
296 
297     fxdata->p[tm_width].val.f = 1.f;
298     fxdata->p[tm_mix].val.f = 1.f;
299 }
300