1 #include "RingModulatorEffect.h"
2 #include "Tunings.h"
3 #include "SineOscillator.h"
4 #include "DebugHelpers.h"
5 #include "FastMath.h"
6 
7 // http://recherche.ircam.fr/pub/dafx11/Papers/66_e.pdf
8 
RingModulatorEffect(SurgeStorage * storage,FxStorage * fxdata,pdata * pd)9 RingModulatorEffect::RingModulatorEffect(SurgeStorage *storage, FxStorage *fxdata, pdata *pd)
10     : Effect(storage, fxdata, pd), halfbandIN(6, true), halfbandOUT(6, true), lp(storage),
11       hp(storage)
12 {
13     mix.set_blocksize(BLOCK_SIZE);
14 }
15 
~RingModulatorEffect()16 RingModulatorEffect::~RingModulatorEffect() {}
17 
init()18 void RingModulatorEffect::init() { setvars(true); }
19 
setvars(bool init)20 void RingModulatorEffect::setvars(bool init)
21 {
22     if (init)
23     {
24         last_unison = -1;
25         halfbandOUT.reset();
26         halfbandIN.reset();
27 
28         lp.suspend();
29         hp.suspend();
30 
31         hp.coeff_HP(hp.calc_omega(*f[rm_lowcut] / 12.0), 0.707);
32         hp.coeff_instantize();
33 
34         lp.coeff_LP2B(lp.calc_omega(*f[rm_highcut] / 12.0), 0.707);
35         lp.coeff_instantize();
36 
37         mix.instantize();
38     }
39 }
40 
41 #define OVERSAMPLE 1
42 
process(float * dataL,float * dataR)43 void RingModulatorEffect::process(float *dataL, float *dataR)
44 {
45     mix.set_target_smoothed(clamp01(*f[rm_mix]));
46 
47     float wetL alignas(16)[BLOCK_SIZE], wetR alignas(16)[BLOCK_SIZE];
48     float dphase[MAX_UNISON];
49     auto uni = std::max(1, *pdata_ival[rm_unison_voices]);
50 
51     // Has unison reset? If so modify settings
52     if (uni != last_unison)
53     {
54         last_unison = uni;
55         if (uni == 1)
56         {
57             detune_offset[0] = 0;
58             panL[0] = 1.f;
59             panR[0] = 1.f;
60             phase[0] = 0.f;
61         }
62         else
63         {
64             float detune_bias = (float)2.f / (uni - 1.f);
65 
66             for (auto u = 0; u < uni; ++u)
67             {
68                 phase[u] = u * 1.f / (uni);
69                 detune_offset[u] = -1.f + detune_bias * u;
70 
71                 panL[u] = u / (uni - 1.f);
72                 panR[u] = (uni - 1.f - u) / (uni - 1.f);
73             }
74         }
75     }
76 
77     // gain scale based on unison
78     float gscale = 0.4 + 0.6 * (1.f / sqrtf(uni));
79     double sri = dsamplerate_inv;
80     int ub = BLOCK_SIZE;
81 
82 #if OVERSAMPLE
83     // Now upsample
84     float dataOS alignas(16)[2][BLOCK_SIZE_OS];
85     halfbandIN.process_block_U2(dataL, dataR, dataOS[0], dataOS[1]);
86     sri = dsamplerate_os_inv;
87     ub = BLOCK_SIZE_OS;
88 #else
89     float *dataOS[2];
90     dataOS[0] = dataL;
91     dataOS[1] = dataR;
92 #endif
93 
94     for (int u = 0; u < uni; ++u)
95     {
96         // need to calc this every time since carrier freq could change
97         if (fxdata->p[rm_unison_detune].absolute)
98         {
99             dphase[u] = (storage->note_to_pitch(*f[rm_carrier_freq]) * Tunings::MIDI_0_FREQ +
100                          fxdata->p[rm_unison_detune].get_extended(
101                              fxdata->p[rm_unison_detune].val.f * detune_offset[u])) *
102                         sri;
103         }
104         else
105         {
106             dphase[u] =
107                 storage->note_to_pitch(*f[rm_carrier_freq] +
108                                        fxdata->p[rm_unison_detune].get_extended(
109                                            fxdata->p[rm_unison_detune].val.f * detune_offset[u])) *
110                 Tunings::MIDI_0_FREQ * sri;
111         }
112     }
113 
114     for (int i = 0; i < ub; ++i)
115     {
116         float resL = 0, resR = 0;
117         for (int u = 0; u < uni; ++u)
118         {
119             // TODO efficiency of course
120             auto vc = SineOscillator::valueFromSinAndCos(
121                 Surge::DSP::fastsin(2.0 * M_PI * (phase[u] - 0.5)),
122                 Surge::DSP::fastcos(2.0 * M_PI * (phase[u] - 0.5)), *pdata_ival[rm_carrier_shape]);
123             phase[u] += dphase[u];
124 
125             if (phase[u] > 1)
126             {
127                 phase[u] -= (int)phase[u];
128             }
129 
130             for (int c = 0; c < 2; ++c)
131             {
132                 auto vin = (c == 0 ? dataOS[0][i] : dataOS[1][i]);
133                 auto wd = 1.0;
134 
135                 auto A = 0.5 * vin + vc;
136                 auto B = vc - 0.5 * vin;
137 
138                 float dPA = diode_sim(A);
139                 float dMA = diode_sim(-A);
140                 float dPB = diode_sim(B);
141                 float dMB = diode_sim(-B);
142 
143                 float res = dPA + dMA - dPB - dMB;
144                 resL += res * panL[u];
145                 resR += res * panR[u];
146             }
147         }
148 
149         auto outl = gscale * resL;
150         auto outr = gscale * resR;
151 
152         outl = 1.5 * outl - 0.5 * outl * outl * outl;
153         outr = 1.5 * outr - 0.5 * outr * outr * outr;
154 
155         dataOS[0][i] = outl;
156         dataOS[1][i] = outr;
157     }
158 
159 #if OVERSAMPLE
160     halfbandOUT.process_block_D2(dataOS[0], dataOS[1]);
161     copy_block(dataOS[0], wetL, BLOCK_SIZE_QUAD);
162     copy_block(dataOS[1], wetR, BLOCK_SIZE_QUAD);
163 #endif
164 
165     // Apply the filters
166     hp.coeff_HP(hp.calc_omega(*f[rm_lowcut] / 12.0), 0.707);
167     lp.coeff_LP2B(lp.calc_omega(*f[rm_highcut] / 12.0), 0.707);
168 
169     if (!fxdata->p[rm_highcut].deactivated)
170     {
171         lp.process_block(wetL, wetR);
172     }
173 
174     if (!fxdata->p[rm_lowcut].deactivated)
175     {
176         hp.process_block(wetL, wetR);
177     }
178 
179     mix.fade_2_blocks_to(dataL, wetL, dataR, wetR, dataL, dataR, BLOCK_SIZE_QUAD);
180 }
181 
suspend()182 void RingModulatorEffect::suspend() { init(); }
183 
group_label(int id)184 const char *RingModulatorEffect::group_label(int id)
185 {
186     switch (id)
187     {
188     case 0:
189         return "Carrier";
190     case 1:
191         return "Diode";
192     case 2:
193         return "EQ";
194     case 3:
195         return "Output";
196     }
197     return 0;
198 }
199 
group_label_ypos(int id)200 int RingModulatorEffect::group_label_ypos(int id)
201 {
202     switch (id)
203     {
204     case 0:
205         return 1;
206     case 1:
207         return 11;
208     case 2:
209         return 17;
210     case 3:
211         return 23;
212     }
213     return 0;
214 }
215 
init_ctrltypes()216 void RingModulatorEffect::init_ctrltypes()
217 {
218     Effect::init_ctrltypes();
219 
220     fxdata->p[rm_carrier_shape].set_name("Shape");
221     fxdata->p[rm_carrier_shape].set_type(ct_sineoscmode);
222     fxdata->p[rm_carrier_freq].set_name("Frequency");
223     fxdata->p[rm_carrier_freq].set_type(ct_freq_ringmod);
224     fxdata->p[rm_unison_detune].set_name("Unison Detune");
225     fxdata->p[rm_unison_detune].set_type(ct_oscspread);
226     fxdata->p[rm_unison_voices].set_name("Unison Voices");
227     fxdata->p[rm_unison_voices].set_type(ct_osccount);
228 
229     fxdata->p[rm_diode_fwdbias].set_name("Forward Bias");
230     fxdata->p[rm_diode_fwdbias].set_type(ct_percent);
231     fxdata->p[rm_diode_linregion].set_name("Linear Region");
232     fxdata->p[rm_diode_linregion].set_type(ct_percent);
233 
234     fxdata->p[rm_lowcut].set_name("Low Cut");
235     fxdata->p[rm_lowcut].set_type(ct_freq_audible_deactivatable);
236     fxdata->p[rm_highcut].set_name("High Cut");
237     fxdata->p[rm_highcut].set_type(ct_freq_audible_deactivatable);
238 
239     fxdata->p[rm_mix].set_name("Mix");
240     fxdata->p[rm_mix].set_type(ct_percent);
241 
242     for (int i = rm_carrier_shape; i < rm_num_params; ++i)
243     {
244         auto a = 1;
245         if (i >= rm_diode_fwdbias)
246             a += 2;
247         if (i >= rm_lowcut)
248             a += 2;
249         if (i >= rm_mix)
250             a += 2;
251         fxdata->p[i].posy_offset = a;
252     }
253 }
254 
init_default_values()255 void RingModulatorEffect::init_default_values()
256 {
257     fxdata->p[rm_carrier_freq].val.f = 60;
258     fxdata->p[rm_carrier_shape].val.i = 0;
259     fxdata->p[rm_diode_fwdbias].val.f = 0.3;
260     fxdata->p[rm_diode_linregion].val.f = 0.7;
261     fxdata->p[rm_unison_detune].val.f = 0.2;
262     fxdata->p[rm_unison_voices].val.i = 1;
263 
264     // FIX THIS
265     fxdata->p[rm_lowcut].val.f = fxdata->p[rm_lowcut].val_min.f;
266     fxdata->p[rm_lowcut].deactivated = false;
267     fxdata->p[rm_highcut].val.f = fxdata->p[rm_highcut].val_max.f;
268     fxdata->p[rm_highcut].deactivated = false;
269     fxdata->p[rm_mix].val.f = 1.0;
270 }
271 
handleStreamingMismatches(int streamingRevision,int currentSynthStreamingRevision)272 void RingModulatorEffect::handleStreamingMismatches(int streamingRevision,
273                                                     int currentSynthStreamingRevision)
274 {
275     if (streamingRevision <= 15)
276     {
277         fxdata->p[rm_lowcut].deactivated = false;
278         fxdata->p[rm_highcut].deactivated = false;
279     }
280 }
281 
diode_sim(float v)282 float RingModulatorEffect::diode_sim(float v)
283 {
284     auto vb = *(f[rm_diode_fwdbias]);
285     auto vl = *(f[rm_diode_linregion]);
286     auto h = 1.f;
287     vl = std::max(vl, vb + 0.02f);
288     if (v < vb)
289     {
290         return 0;
291     }
292     if (v < vl)
293     {
294         auto vvb = v - vb;
295         return h * vvb * vvb / (2.f * vl - 2.f * vb);
296     }
297     auto vlvb = vl - vb;
298     return h * v - h * vl + h * vlvb * vlvb / (2.f * vl - 2.f * vb);
299 }
300