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