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-2020 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 "SineOscillator.h"
17 #include "FastMath.h"
18 #include <algorithm>
19
20 /*
21 * Sine Oscilator Optimization Strategy
22 *
23 * With Surge 1.9, we undertook a bunch of work to optimize the sine oscillator runtime at high
24 * unison count with odd shapes. Basicaly at high unison we were doing large numbers of loops,
25 * branches and so forth, and not using any of the advantage you could get by realizing the paralle
26 * structure of unison. So we fixed that.
27 *
28 * There's two core fixes.
29 *
30 * First, and you shouldn't need to touch this, the inner unison loop of ::process is now SSEified
31 * over unison. This means that we use parallel approximations of sine, we use parallel clamps and
32 * feedback application, the whole nine yards. You can see it in process_block_internal.
33 *
34 * But that's not all. The other key trick is that the shape modes added a massive amount of
35 * switching to the execution path. So we eliminated that completely. We did that with two tricks
36 *
37 * 1: Mode is a template applied at block level so there's no ifs inside the block
38 * 2: When possible, shape generation is coded as an SSE instruction.
39 *
40 * So #1 just means that process_block_internal has a <mode> template as do many other operators
41 * and we use template specialization. We are specializing the function
42 *
43 * __m128 valueFromSineAndCosForMode<mode>(__m128 s, __m128 c, int maxc )
44 *
45 * The default impleementation of this function calls
46 *
47 * float valueForSineAndCosForModeAsScalar<mode>(s, c)
48 *
49 * on each SSE point in an unrolled SSE loop. But we also specialize it for many of the modes.
50 *
51 * The the original api point SineOscillator::valueFromSineAndCos (which is now only called by
52 * external APIs) calls into the appropriately templated function. Since our loop doesn't use that
53 * though we end up with our switch compile time inlined.
54 *
55 * So if you want to add a mode what does this mean? Well go update Parameter.cpp to extend the
56 * max of mode of course. But then you come back here and do the following
57 *
58 * 1. Assume that you know how to write your new mode as a scalar function. After the
59 * declaration of valueFromSineAndCosForMode as a template, specialize and put your scalar
60 * implementation in there. Add your mode to th switch statement in valueFromSinAndCos
61 * and into the DOCASE switch statement in process_block. Compile, run, test.
62 *
63 * 2. Then if that works, and you can code up your mode as an SSE function, remove that
64 * specialization and instead specialize the mode in teh SSE-named function.
65 *
66 * Should all be pretty clear.
67 */
68
SineOscillator(SurgeStorage * storage,OscillatorStorage * oscdata,pdata * localcopy)69 SineOscillator::SineOscillator(SurgeStorage *storage, OscillatorStorage *oscdata, pdata *localcopy)
70 : Oscillator(storage, oscdata, localcopy), lp(storage), hp(storage)
71 {
72 }
73
prepare_unison(int voices)74 void SineOscillator::prepare_unison(int voices)
75 {
76 auto us = Surge::Oscillator::UnisonSetup<float>(voices);
77
78 out_attenuation_inv = us.attenuation_inv();
79 ;
80 out_attenuation = 1.0f / out_attenuation_inv;
81
82 detune_bias = us.detuneBias();
83 detune_offset = us.detuneOffset();
84 for (int v = 0; v < voices; ++v)
85 {
86 us.panLaw(v, panL[v], panR[v]);
87 }
88
89 // normalize to be sample rate independent amount of time for 50 44.1k samples
90 dplaying = 1.0 / 50.0 * 44100 / samplerate;
91 playingramp[0] = 1;
92 for (int i = 1; i < voices; ++i)
93 playingramp[i] = 0;
94 }
95
init(float pitch,bool is_display,bool nonzero_init_drift)96 void SineOscillator::init(float pitch, bool is_display, bool nonzero_init_drift)
97 {
98 n_unison = limit_range(oscdata->p[sine_unison_voices].val.i, 1, MAX_UNISON);
99
100 if (is_display)
101 {
102 n_unison = 1;
103 }
104
105 prepare_unison(n_unison);
106
107 for (int i = 0; i < n_unison; i++)
108 {
109 phase[i] = // phase in range -PI to PI
110 (oscdata->retrigger.val.b || is_display) ? 0.f : 2.0 * M_PI * storage->rand_01() - M_PI;
111 lastvalue[i] = 0.f;
112 driftLFO[i].init(nonzero_init_drift);
113 sine[i].set_phase(phase[i]);
114 }
115
116 firstblock = (oscdata->retrigger.val.b || is_display);
117
118 fb_val = 0.f;
119
120 id_mode = oscdata->p[sine_shape].param_id_in_scene;
121 id_fb = oscdata->p[sine_feedback].param_id_in_scene;
122 id_fmlegacy = oscdata->p[sine_FMmode].param_id_in_scene;
123 id_detune = oscdata->p[sine_unison_detune].param_id_in_scene;
124
125 hp.coeff_instantize();
126 lp.coeff_instantize();
127
128 hp.coeff_HP(hp.calc_omega(oscdata->p[sine_lowcut].val.f / 12.0) / OSC_OVERSAMPLING, 0.707);
129 lp.coeff_LP2B(lp.calc_omega(oscdata->p[sine_highcut].val.f / 12.0) / OSC_OVERSAMPLING, 0.707);
130
131 charFilt.init(storage->getPatch().character.val.i);
132 if (storage->getPatch().streamingRevision <= 15)
133 {
134 charFilt.doFilter = false;
135 }
136 }
137
~SineOscillator()138 SineOscillator::~SineOscillator() {}
139
calcquadrant(float sinx,float cosx)140 inline int calcquadrant(float sinx, float cosx)
141 {
142 int sxl0 = (sinx <= 0);
143 int cxl0 = (cosx <= 0);
144
145 // quadrant
146 // 1: sin > cos > so this is 0 + 0 - 0 + 1 = 1
147 // 2: sin > cos < so this is 0 + 1 - 0 + 1 = 2
148 // 3: sin < cos < so this is 3 + 1 - 2 + 1 = 3
149 // 4: sin < cos > so this is 3 + 0 - 0 + 1 = 4
150 int quadrant = 3 * sxl0 + cxl0 - 2 * sxl0 * cxl0 + 1;
151 return quadrant;
152 }
153
calcquadrant0(float sinx,float cosx)154 inline int calcquadrant0(float sinx, float cosx)
155 {
156 int sxl0 = (sinx <= 0);
157 int cxl0 = (cosx <= 0);
158
159 // quadrant
160 // 1: sin > cos > so this is 0 + 0 - 0 = 0
161 // 2: sin > cos < so this is 0 + 1 - 0 = 1
162 // 3: sin < cos < so this is 3 + 1 - 2 = 2
163 // 4: sin < cos > so this is 3 + 0 - 0 = 3
164 int quadrant = 3 * sxl0 + cxl0 - 2 * sxl0 * cxl0;
165 return quadrant;
166 }
167
calcquadrantSSE(__m128 sinx,__m128 cosx)168 inline __m128 calcquadrantSSE(__m128 sinx, __m128 cosx)
169 {
170 const auto mz = _mm_setzero_ps();
171 const auto m1 = _mm_set1_ps(1), m2 = _mm_set1_ps(2), m3 = _mm_set1_ps(3);
172 auto slt = _mm_and_ps(_mm_cmple_ps(sinx, mz), m1);
173 auto clt = _mm_and_ps(_mm_cmple_ps(cosx, mz), m1);
174
175 // quadrant
176 // 1: sin > cos > so this is 0 + 0 - 0 + 1 = 1
177 // 2: sin > cos < so this is 0 + 1 - 0 + 1 = 2
178 // 3: sin < cos < so this is 3 + 1 - 2 + 1 = 3
179 // 4: sin < cos > so this is 3 + 0 - 0 + 1 = 4
180 // int quadrant = 3 * sxl0 + cxl0 - 2 * sxl0 * cxl0 + 1;
181 auto thsx = _mm_mul_ps(m3, slt);
182 auto twsc = _mm_mul_ps(m2, _mm_mul_ps(slt, clt));
183 auto r = _mm_add_ps(_mm_add_ps(thsx, clt), _mm_sub_ps(m1, twsc));
184 return r;
185 }
186 /*
187 * If you have a model you can't write as SSE and only as scalar, you can
188 * specialize this
189 */
valueFromSinAndCosForModeAsScalar(float s,float c)190 template <int mode> inline float valueFromSinAndCosForModeAsScalar(float s, float c) { return 0; }
191
192 /*
193 * Otherwise, specialize this as an SSE operation. We have specialized the
194 * SSE version for every case to date.
195 */
196 template <int mode>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)197 inline __m128 valueFromSinAndCosForMode(__m128 svaluesse, __m128 cvaluesse, int maxc)
198 {
199 float res alignas(16)[4];
200 float sv alignas(16)[4], cv alignas(16)[4];
201 _mm_store_ps(sv, svaluesse);
202 _mm_store_ps(cv, cvaluesse);
203 for (int i = 0; i < maxc; ++i)
204 res[i] = valueFromSinAndCosForModeAsScalar<mode>(sv[i], cv[i]);
205 return _mm_load_ps(res);
206 }
207
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)208 template <> inline __m128 valueFromSinAndCosForMode<0>(__m128 svaluesse, __m128 cvaluesse, int maxc)
209 {
210 // mode zero is just sine obviously
211 return svaluesse;
212 }
213
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)214 template <> inline __m128 valueFromSinAndCosForMode<1>(__m128 svaluesse, __m128 cvaluesse, int maxc)
215 {
216 /*
217 switch (quadrant)
218 {
219 case 1: pvalue = 1 - cosx;
220 case 2: pvalue = 1 + cosx;
221 case 3: pvalue = -1 - cosx;
222 case 4: pvalue = -1 + cosx;
223 }
224 */
225 const auto mz = _mm_setzero_ps();
226 const auto m1 = _mm_set1_ps(1);
227 const auto mm1 = _mm_set1_ps(-1);
228
229 auto h1 = _mm_cmpge_ps(svaluesse, mz);
230 auto sw = _mm_sub_ps(_mm_and_ps(h1, m1), _mm_andnot_ps(h1, m1)); // this is now 1 1 -1 -1
231
232 auto q24 = _mm_cmplt_ps(_mm_mul_ps(svaluesse, cvaluesse), mz);
233 auto pm = _mm_sub_ps(_mm_and_ps(q24, m1), _mm_andnot_ps(q24, m1)); // this is -1 1 -1 1
234 return _mm_add_ps(sw, _mm_mul_ps(pm, cvaluesse));
235 }
236
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)237 template <> inline __m128 valueFromSinAndCosForMode<2>(__m128 svaluesse, __m128 cvaluesse, int maxc)
238 {
239 // First half of sine.
240 const auto mz = _mm_setzero_ps();
241 return _mm_and_ps(svaluesse, _mm_cmpge_ps(svaluesse, mz));
242 }
243
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)244 template <> inline __m128 valueFromSinAndCosForMode<3>(__m128 svaluesse, __m128 cvaluesse, int maxc)
245 {
246 // 1 -/+ cosx in first half only
247 const auto mz = _mm_setzero_ps();
248 const auto m1 = _mm_set1_ps(1);
249 const auto mm1 = _mm_set1_ps(-1);
250 const auto m2 = _mm_set1_ps(2);
251
252 auto h1 = _mm_cmpge_ps(svaluesse, mz);
253 auto q2 = _mm_and_ps(h1, _mm_cmple_ps(cvaluesse, mz));
254
255 auto fh = _mm_and_ps(h1, m1);
256 auto cx =
257 _mm_mul_ps(fh, _mm_mul_ps(cvaluesse, _mm_add_ps(mm1, _mm_mul_ps(m2, _mm_and_ps(q2, m1)))));
258
259 // return _mm_and_ps(svaluesse, _mm_cmpge_ps(svaluesse, mz));
260 return _mm_add_ps(fh, cx);
261 }
262
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)263 template <> inline __m128 valueFromSinAndCosForMode<4>(__m128 svaluesse, __m128 cvaluesse, int maxc)
264 {
265 // Sine 2x in first half
266 const auto mz = _mm_setzero_ps();
267 const auto m2 = _mm_set1_ps(2);
268
269 auto s2x = _mm_mul_ps(m2, _mm_mul_ps(svaluesse, cvaluesse));
270 return _mm_and_ps(s2x, _mm_cmpge_ps(svaluesse, mz));
271 }
272
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)273 template <> inline __m128 valueFromSinAndCosForMode<5>(__m128 svaluesse, __m128 cvaluesse, int maxc)
274 {
275 // This is basically a double frequency shape 0 in the first half only
276 const auto mz = _mm_setzero_ps();
277 const auto m1 = _mm_set1_ps(1);
278 const auto m2 = _mm_set1_ps(2);
279
280 auto s2x = _mm_mul_ps(m2, _mm_mul_ps(svaluesse, cvaluesse));
281 auto c2x = _mm_sub_ps(m1, _mm_mul_ps(m2, _mm_mul_ps(svaluesse, svaluesse)));
282
283 auto v1 = valueFromSinAndCosForMode<1>(s2x, c2x, maxc);
284 return _mm_and_ps(_mm_cmpge_ps(svaluesse, mz), v1);
285 }
286
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)287 template <> inline __m128 valueFromSinAndCosForMode<6>(__m128 svaluesse, __m128 cvaluesse, int maxc)
288 {
289 // abs(Sine 2x in first half
290 const auto mz = _mm_setzero_ps();
291 const auto m2 = _mm_set1_ps(2);
292
293 auto s2x = _mm_mul_ps(m2, _mm_mul_ps(svaluesse, cvaluesse));
294 auto s2fh = _mm_and_ps(s2x, _mm_cmpge_ps(svaluesse, mz));
295 return abs_ps(s2fh);
296 }
297
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)298 template <> inline __m128 valueFromSinAndCosForMode<7>(__m128 svaluesse, __m128 cvaluesse, int maxc)
299 {
300 return abs_ps(valueFromSinAndCosForMode<5>(svaluesse, cvaluesse, maxc));
301 }
302
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)303 template <> inline __m128 valueFromSinAndCosForMode<8>(__m128 svaluesse, __m128 cvaluesse, int maxc)
304 {
305 // 2 * First half of sin - 1
306 const auto mz = _mm_setzero_ps();
307 const auto m2 = _mm_set1_ps(2);
308 const auto m1 = _mm_set1_ps(1);
309 auto fhs = _mm_and_ps(svaluesse, _mm_cmpge_ps(svaluesse, mz));
310 return _mm_sub_ps(_mm_mul_ps(m2, fhs), m1);
311 }
312
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)313 template <> inline __m128 valueFromSinAndCosForMode<9>(__m128 svaluesse, __m128 cvaluesse, int maxc)
314 {
315 // zero out quadrant 1 and 3 which are quadrants where C and S have the same sign
316 const auto mz = _mm_setzero_ps();
317 const auto m2 = _mm_set1_ps(2);
318 const auto m1 = _mm_set1_ps(1);
319 auto css = _mm_mul_ps(svaluesse, cvaluesse);
320 return _mm_and_ps(svaluesse, _mm_cmple_ps(css, mz));
321 }
322
323 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)324 inline __m128 valueFromSinAndCosForMode<10>(__m128 svaluesse, __m128 cvaluesse, int maxc)
325 {
326 // zero out quadrant 2 and 3 which are quadrants where C and S have the different sign
327 const auto mz = _mm_setzero_ps();
328 const auto m2 = _mm_set1_ps(2);
329 const auto m1 = _mm_set1_ps(1);
330 auto css = _mm_mul_ps(svaluesse, cvaluesse);
331 return _mm_and_ps(svaluesse, _mm_cmpge_ps(css, mz));
332 }
333
334 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)335 inline __m128 valueFromSinAndCosForMode<11>(__m128 svaluesse, __m128 cvaluesse, int maxc)
336 {
337 auto q = valueFromSinAndCosForMode<3>(svaluesse, cvaluesse, maxc);
338 const auto m2 = _mm_set1_ps(2);
339 const auto m1 = _mm_set1_ps(1);
340 return _mm_sub_ps(_mm_mul_ps(m2, q), m1);
341 }
342
343 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)344 inline __m128 valueFromSinAndCosForMode<12>(__m128 svaluesse, __m128 cvaluesse, int maxc)
345 {
346 // Flip sign of sin2x in quadrant 2 or 3 (when cosine is negative)
347 const auto mz = _mm_setzero_ps();
348 const auto m2 = _mm_set1_ps(2);
349 const auto m1 = _mm_set1_ps(1);
350 const auto mm1 = _mm_set1_ps(-1);
351
352 auto s2x = _mm_mul_ps(m2, _mm_mul_ps(svaluesse, cvaluesse));
353
354 auto q23 = _mm_cmpge_ps(cvaluesse, mz);
355 auto mul = _mm_add_ps(_mm_and_ps(q23, m1), _mm_andnot_ps(q23, mm1));
356 return _mm_mul_ps(s2x, mul);
357 }
358
359 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)360 inline __m128 valueFromSinAndCosForMode<13>(__m128 svaluesse, __m128 cvaluesse, int maxc)
361 {
362 // Flip sign of sin2x in quadrant 3 (when sine is negative)
363 // and zero it in quadrant 2 and 4 (when sine and cos have different signs or s2x is negative)
364 const auto mz = _mm_setzero_ps();
365 const auto m2 = _mm_set1_ps(2);
366 const auto m1 = _mm_set1_ps(1);
367 const auto mm1 = _mm_set1_ps(-1);
368
369 auto s2x = _mm_mul_ps(m2, _mm_mul_ps(svaluesse, cvaluesse));
370
371 auto q13 = _mm_cmpge_ps(s2x, mz);
372 auto q2 = _mm_cmple_ps(svaluesse, mz);
373 auto signflip = _mm_sub_ps(m1, _mm_and_ps(q2, m2));
374
375 return _mm_and_ps(q13, _mm_mul_ps(signflip, s2x));
376 }
377
378 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)379 inline __m128 valueFromSinAndCosForMode<14>(__m128 svaluesse, __m128 cvaluesse, int maxc)
380 {
381 // abs of cos2x in the first half
382 const auto mz = _mm_setzero_ps();
383 const auto m1 = _mm_set1_ps(1);
384 const auto m2 = _mm_set1_ps(2);
385
386 auto c2x = _mm_sub_ps(m1, _mm_mul_ps(m2, _mm_mul_ps(svaluesse, svaluesse)));
387 auto q23 = _mm_cmpge_ps(svaluesse, mz);
388 return _mm_and_ps(q23, abs_ps(c2x));
389 }
390
391 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)392 inline __m128 valueFromSinAndCosForMode<15>(__m128 svaluesse, __m128 cvaluesse, int maxc)
393 {
394 // 1 - sinx in quadrant 1, -1-sinx in quadrant 4, zero otherwise
395
396 const auto mz = _mm_setzero_ps();
397 const auto m1 = _mm_set1_ps(1);
398 const auto mm1 = _mm_set1_ps(-1);
399
400 auto h1 = _mm_cmpge_ps(svaluesse, mz);
401 auto sig = _mm_add_ps(_mm_and_ps(h1, _mm_sub_ps(m1, svaluesse)),
402 _mm_andnot_ps(h1, _mm_sub_ps(mm1, svaluesse)));
403
404 auto q14 = _mm_cmpge_ps(cvaluesse, mz);
405 return _mm_and_ps(q14, sig);
406 }
407
408 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)409 inline __m128 valueFromSinAndCosForMode<16>(__m128 svaluesse, __m128 cvaluesse, int maxc)
410 {
411 // 1 - sinx in quadrant 1, cosx - 1 in quadrant 4, zero otherwise
412
413 const auto mz = _mm_setzero_ps();
414 const auto m1 = _mm_set1_ps(1);
415
416 auto h1 = _mm_cmpge_ps(svaluesse, mz);
417 auto sig = _mm_add_ps(_mm_and_ps(h1, _mm_sub_ps(m1, svaluesse)),
418 _mm_andnot_ps(h1, _mm_sub_ps(cvaluesse, m1)));
419
420 auto q14 = _mm_cmpge_ps(cvaluesse, mz);
421 return _mm_and_ps(q14, sig);
422 }
423
424 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)425 inline __m128 valueFromSinAndCosForMode<17>(__m128 svaluesse, __m128 cvaluesse, int maxc)
426 {
427 // 1-sinx in 1,2; -1-sinx in 3,4
428 const auto mz = _mm_setzero_ps();
429 const auto m1 = _mm_set1_ps(1);
430
431 auto h1 = _mm_cmpge_ps(svaluesse, mz);
432 auto sw = _mm_sub_ps(_mm_and_ps(h1, m1), _mm_andnot_ps(h1, m1)); // this is now 1 1 -1 -1
433 return _mm_sub_ps(sw, svaluesse);
434 }
435
436 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)437 inline __m128 valueFromSinAndCosForMode<18>(__m128 svaluesse, __m128 cvaluesse, int maxc)
438 {
439 // sin2x in 1, cosx in 23, -sin2x in 4
440 const auto mz = _mm_setzero_ps();
441 const auto m1 = _mm_set1_ps(1);
442 const auto m2 = _mm_set1_ps(2);
443
444 auto h1 = _mm_cmpge_ps(svaluesse, mz);
445 auto sw = _mm_sub_ps(_mm_and_ps(h1, m1), _mm_andnot_ps(h1, m1)); // this is now 1 1 -1 -1
446
447 auto s2x = _mm_mul_ps(m2, _mm_mul_ps(svaluesse, cvaluesse));
448 auto q23 = _mm_cmple_ps(cvaluesse, mz);
449
450 return _mm_add_ps(_mm_and_ps(q23, cvaluesse), _mm_andnot_ps(q23, _mm_mul_ps(sw, s2x)));
451 }
452
453 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)454 inline __m128 valueFromSinAndCosForMode<19>(__m128 svaluesse, __m128 cvaluesse, int maxc)
455 {
456 // This is basically a double frequency shape 0 in the first half only
457 const auto mz = _mm_setzero_ps();
458 const auto m1 = _mm_set1_ps(1);
459 const auto m2 = _mm_set1_ps(2);
460
461 auto s2x = _mm_mul_ps(m2, _mm_mul_ps(svaluesse, cvaluesse));
462 auto c2x = _mm_sub_ps(m1, _mm_mul_ps(m2, _mm_mul_ps(svaluesse, svaluesse)));
463 auto s4x = _mm_mul_ps(m2, _mm_mul_ps(s2x, c2x));
464
465 auto h1 = _mm_cmpge_ps(svaluesse, mz);
466 auto q23 = _mm_cmpge_ps(cvaluesse, mz);
467 auto fh = _mm_sub_ps(_mm_and_ps(q23, s2x), _mm_andnot_ps(q23, s4x));
468 auto res = _mm_add_ps(_mm_and_ps(h1, fh), _mm_andnot_ps(h1, svaluesse));
469
470 return res;
471 }
472
473 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)474 inline __m128 valueFromSinAndCosForMode<20>(__m128 svaluesse, __m128 cvaluesse, int maxc)
475 {
476 // Sine in quadrants 2 and 4; +/-1 in quadrants 1 and 3.
477 // quadrants 1 and 3 are when cos and sin have the same sign
478 const auto mz = _mm_setzero_ps();
479 const auto m1 = _mm_set1_ps(1);
480
481 auto sbc = _mm_mul_ps(svaluesse, cvaluesse);
482 auto q13 = _mm_cmpge_ps(sbc, mz);
483 auto h12 = _mm_cmpge_ps(svaluesse, mz);
484 auto mv = _mm_sub_ps(_mm_and_ps(h12, m1), _mm_andnot_ps(h12, m1));
485 return _mm_add_ps(_mm_andnot_ps(q13, mv), _mm_and_ps(q13, svaluesse));
486 }
487
488 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)489 inline __m128 valueFromSinAndCosForMode<21>(__m128 svaluesse, __m128 cvaluesse, int maxc)
490 {
491 // Sine in quadrants 2 and 4; +/-1 in quadrants 1 and 3.
492 // quadrants 1 and 3 are when cos and sin have the same sign
493 const auto mz = _mm_setzero_ps();
494 const auto m1 = _mm_set1_ps(1);
495
496 auto sbc = _mm_mul_ps(svaluesse, cvaluesse);
497 auto q13 = _mm_cmpge_ps(sbc, mz);
498 auto h12 = _mm_cmpge_ps(svaluesse, mz);
499 auto mv = _mm_sub_ps(_mm_and_ps(h12, m1), _mm_andnot_ps(h12, m1));
500 return _mm_add_ps(_mm_and_ps(q13, mv), _mm_andnot_ps(q13, svaluesse));
501 }
502
503 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)504 inline __m128 valueFromSinAndCosForMode<22>(__m128 svaluesse, __m128 cvaluesse, int maxc)
505 {
506 // first 2 quadrants are 1-sin
507 const auto mz = _mm_setzero_ps();
508 const auto m1 = _mm_set1_ps(1);
509
510 auto sp = _mm_cmpge_ps(cvaluesse, mz);
511 return _mm_and_ps(sp, svaluesse);
512 }
513
514 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)515 inline __m128 valueFromSinAndCosForMode<23>(__m128 svaluesse, __m128 cvaluesse, int maxc)
516 {
517 // first 2 quadrants are 1-sin
518 const auto mz = _mm_setzero_ps();
519 const auto m1 = _mm_set1_ps(1);
520
521 auto sp = _mm_cmple_ps(cvaluesse, mz);
522 return _mm_and_ps(sp, svaluesse);
523 }
524
525 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)526 inline __m128 valueFromSinAndCosForMode<24>(__m128 svaluesse, __m128 cvaluesse, int maxc)
527 {
528 // first 2 quadrants are 1-sin
529 const auto mz = _mm_setzero_ps();
530 const auto m1 = _mm_set1_ps(1);
531
532 auto onems = _mm_sub_ps(m1, svaluesse);
533 auto sp = _mm_cmpge_ps(svaluesse, mz);
534 return _mm_add_ps(_mm_and_ps(sp, onems), _mm_andnot_ps(sp, svaluesse));
535 }
536
537 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)538 inline __m128 valueFromSinAndCosForMode<26>(__m128 svaluesse, __m128 cvaluesse, int maxc)
539 {
540 // Zero out sine in quadrant 3 (which is cos and sin are both negative)
541 const auto mz = _mm_setzero_ps();
542 auto sl0 = _mm_cmple_ps(svaluesse, mz);
543 auto cl0 = _mm_cmple_ps(cvaluesse, mz);
544 return _mm_andnot_ps(_mm_and_ps(sl0, cl0), svaluesse);
545 }
546
547 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)548 inline __m128 valueFromSinAndCosForMode<25>(__m128 svaluesse, __m128 cvaluesse, int maxc)
549 {
550 // Sine 2x in first half
551 const auto mz = _mm_setzero_ps();
552 const auto m2 = _mm_set1_ps(2);
553
554 auto s2x = _mm_mul_ps(m2, _mm_mul_ps(svaluesse, cvaluesse));
555 auto qv = calcquadrantSSE(svaluesse, cvaluesse);
556 auto h1 = _mm_cmpge_ps(svaluesse, mz);
557 return _mm_and_ps(h1, _mm_div_ps(s2x, qv));
558 }
559
560 template <>
valueFromSinAndCosForMode(__m128 svaluesse,__m128 cvaluesse,int maxc)561 inline __m128 valueFromSinAndCosForMode<27>(__m128 svaluesse, __m128 cvaluesse, int maxc)
562 {
563 // Sine 2x in first half
564 const auto mz = _mm_setzero_ps();
565 const auto m2 = _mm_set1_ps(2);
566
567 auto s2x = _mm_mul_ps(m2, _mm_mul_ps(svaluesse, cvaluesse));
568 auto qv = calcquadrantSSE(svaluesse, cvaluesse);
569 return _mm_div_ps(s2x, qv);
570 }
571
572 // This is used by the legacy apis
singleValueFromSinAndCos(float sinx,float cosx)573 template <int mode> inline float singleValueFromSinAndCos(float sinx, float cosx)
574 {
575 auto s = _mm_set1_ps(sinx);
576 auto c = _mm_set1_ps(cosx);
577 auto r = valueFromSinAndCosForMode<mode>(s, c, 1);
578 float v;
579 _mm_store_ss(&v, r);
580 return v;
581 }
582
583 template <int mode, bool stereo, bool FM>
process_block_internal(float pitch,float drift,float fmdepth)584 void SineOscillator::process_block_internal(float pitch, float drift, float fmdepth)
585 {
586 double detune;
587 double omega[MAX_UNISON];
588
589 for (int l = 0; l < n_unison; l++)
590 {
591 detune = drift * driftLFO[l].next();
592
593 if (n_unison > 1)
594 {
595 if (oscdata->p[sine_unison_detune].absolute)
596 {
597 detune += oscdata->p[sine_unison_detune].get_extended(
598 localcopy[oscdata->p[sine_unison_detune].param_id_in_scene].f) *
599 storage->note_to_pitch_inv_ignoring_tuning(std::min(148.f, pitch)) * 16 /
600 0.9443 * (detune_bias * float(l) + detune_offset);
601 }
602 else
603 {
604 detune += oscdata->p[sine_unison_detune].get_extended(localcopy[id_detune].f) *
605 (detune_bias * float(l) + detune_offset);
606 }
607 }
608
609 omega[l] = std::min(M_PI, pitch_to_omega(pitch + detune));
610 }
611
612 float fv = 32.0 * M_PI * fmdepth * fmdepth * fmdepth;
613
614 /*
615 ** See issue 2619. At worst case we move phase by fv * 1. Since we
616 ** need phase to be in -Pi,Pi, that means if fv / Pi > 1e5 or so
617 ** we have float precision problems. So lets clamp fv.
618 */
619 fv = limit_range(fv, -1.0e6f, 1.0e6f);
620
621 FMdepth.newValue(fv);
622 FB.newValue(abs(fb_val));
623
624 float p alignas(16)[MAX_UNISON];
625 float sx alignas(16)[MAX_UNISON];
626 float cx alignas(16)[MAX_UNISON];
627 float olv alignas(16)[MAX_UNISON];
628 float orv alignas(16)[MAX_UNISON];
629
630 for (int i = 0; i < MAX_UNISON; ++i)
631 p[i] = 0.0;
632
633 auto outattensse = _mm_set1_ps(out_attenuation);
634 auto fbnegmask = _mm_cmplt_ps(_mm_set1_ps(fb_val), _mm_setzero_ps());
635 __m128 playramp[4], dramp[4];
636 if (firstblock)
637 {
638 for (int i = 0; i < 4; ++i)
639 {
640 playramp[i] = _mm_set1_ps(0.0);
641 dramp[i] = _mm_set1_ps(BLOCK_SIZE_OS_INV);
642 }
643 float tv alignas(16)[4];
644 _mm_store_ps(tv, playramp[0]);
645 tv[0] = 1.0;
646 playramp[0] = _mm_load_ps(tv);
647
648 _mm_store_ps(tv, dramp[0]);
649 tv[0] = 0.0;
650 dramp[0] = _mm_load_ps(tv);
651 }
652 else
653 {
654 for (int i = 0; i < 4; ++i)
655 {
656 playramp[i] = _mm_set1_ps(1.0);
657 dramp[i] = _mm_setzero_ps();
658 }
659 }
660 firstblock = false;
661 for (int k = 0; k < BLOCK_SIZE_OS; k++)
662 {
663 float outL = 0.f, outR = 0.f;
664
665 float fmpd = FM ? FMdepth.v * master_osc[k] : 0.f;
666 auto fmpds = _mm_set1_ps(fmpd);
667 auto fbv = _mm_set1_ps(FB.v);
668
669 for (int u = 0; u < n_unison; u += 4)
670 {
671 float fph alignas(16)[4] = {(float)phase[u], (float)phase[u + 1], (float)phase[u + 2],
672 (float)phase[u + 3]};
673 auto ph = _mm_load_ps(&fph[0]);
674 auto lv = _mm_load_ps(&lastvalue[u]);
675 auto x = _mm_add_ps(_mm_add_ps(ph, lv), fmpds);
676
677 x = Surge::DSP::clampToPiRangeSSE(x);
678
679 auto sxl = Surge::DSP::fastsinSSE(x);
680 auto cxl = Surge::DSP::fastcosSSE(x);
681
682 auto out_local = valueFromSinAndCosForMode<mode>(sxl, cxl, std::min(n_unison - u, 4));
683
684 auto pl = _mm_load_ps(&panL[u]);
685 auto pr = _mm_load_ps(&panR[u]);
686
687 auto ui = u >> 2;
688 auto ramp = playramp[ui];
689 auto olpr = _mm_mul_ps(out_local, ramp);
690 playramp[ui] = _mm_add_ps(playramp[ui], dramp[ui]);
691
692 auto l = _mm_mul_ps(_mm_mul_ps(pl, olpr), outattensse);
693 auto r = _mm_mul_ps(_mm_mul_ps(pr, olpr), outattensse);
694 _mm_store_ps(&olv[u], l);
695 _mm_store_ps(&orv[u], r);
696
697 auto lastv =
698 _mm_mul_ps(_mm_add_ps(_mm_and_ps(fbnegmask, _mm_mul_ps(out_local, out_local)),
699 _mm_andnot_ps(fbnegmask, out_local)),
700 fbv);
701 _mm_store_ps(&lastvalue[u], lastv);
702 }
703
704 for (int u = 0; u < n_unison; ++u)
705 {
706 outL += olv[u];
707 outR += orv[u];
708
709 // These are doubles and need to be so keep it unrolled
710 phase[u] += omega[u];
711 phase[u] -= (phase[u] > M_PI) * 2.0 * M_PI;
712 }
713
714 FMdepth.process();
715 FB.process();
716
717 if (stereo)
718 {
719 output[k] = outL;
720 outputR[k] = outR;
721 }
722 else
723 output[k] = (outL + outR) / 2;
724 }
725 applyFilter();
726 }
727
applyFilter()728 void SineOscillator::applyFilter()
729 {
730 if (!oscdata->p[sine_lowcut].deactivated)
731 {
732 auto par = &(oscdata->p[sine_lowcut]);
733 auto pv = limit_range(localcopy[par->param_id_in_scene].f, par->val_min.f, par->val_max.f);
734 hp.coeff_HP(hp.calc_omega(pv / 12.0) / OSC_OVERSAMPLING, 0.707);
735 }
736
737 if (!oscdata->p[sine_highcut].deactivated)
738 {
739 auto par = &(oscdata->p[sine_highcut]);
740 auto pv = limit_range(localcopy[par->param_id_in_scene].f, par->val_min.f, par->val_max.f);
741 lp.coeff_LP2B(lp.calc_omega(pv / 12.0) / OSC_OVERSAMPLING, 0.707);
742 }
743
744 for (int k = 0; k < BLOCK_SIZE_OS; k += BLOCK_SIZE)
745 {
746 if (!oscdata->p[sine_lowcut].deactivated)
747 hp.process_block(&(output[k]), &(outputR[k]));
748 if (!oscdata->p[sine_highcut].deactivated)
749 lp.process_block(&(output[k]), &(outputR[k]));
750 }
751 }
752
753 template <int mode>
process_block_legacy(float pitch,float drift,bool stereo,bool FM,float fmdepth)754 void SineOscillator::process_block_legacy(float pitch, float drift, bool stereo, bool FM,
755 float fmdepth)
756 {
757 double detune;
758 double omega[MAX_UNISON];
759
760 if (FM)
761 {
762 for (int l = 0; l < n_unison; l++)
763 {
764 detune = drift * driftLFO[l].next();
765
766 if (n_unison > 1)
767 {
768 if (oscdata->p[sine_unison_detune].absolute)
769 {
770 detune += oscdata->p[sine_unison_detune].get_extended(
771 localcopy[oscdata->p[sine_unison_detune].param_id_in_scene].f) *
772 storage->note_to_pitch_inv_ignoring_tuning(std::min(148.f, pitch)) *
773 16 / 0.9443 * (detune_bias * float(l) + detune_offset);
774 }
775 else
776 {
777 detune += oscdata->p[sine_unison_detune].get_extended(localcopy[id_detune].f) *
778 (detune_bias * float(l) + detune_offset);
779 }
780 }
781
782 omega[l] = std::min(M_PI, (double)pitch_to_omega(pitch + detune));
783 }
784
785 FMdepth.newValue(fmdepth);
786
787 for (int k = 0; k < BLOCK_SIZE_OS; k++)
788 {
789 float outL = 0.f, outR = 0.f;
790
791 for (int u = 0; u < n_unison; u++)
792 {
793 float out_local = singleValueFromSinAndCos<mode>(Surge::DSP::fastsin(phase[u]),
794 Surge::DSP::fastcos(phase[u]));
795
796 outL += (panL[u] * out_local) * out_attenuation * playingramp[u];
797 outR += (panR[u] * out_local) * out_attenuation * playingramp[u];
798
799 if (playingramp[u] < 1)
800 playingramp[u] += dplaying;
801 if (playingramp[u] > 1)
802 playingramp[u] = 1;
803
804 phase[u] += omega[u] + master_osc[k] * FMdepth.v;
805 phase[u] = Surge::DSP::clampToPiRange(phase[u]);
806 }
807
808 FMdepth.process();
809
810 if (stereo)
811 {
812 output[k] = outL;
813 outputR[k] = outR;
814 }
815 else
816 output[k] = (outL + outR) / 2;
817 }
818 }
819 else
820 {
821 for (int l = 0; l < n_unison; l++)
822 {
823 detune = drift * driftLFO[l].next();
824
825 if (n_unison > 1)
826 detune += oscdata->p[sine_unison_detune].get_extended(localcopy[id_detune].f) *
827 (detune_bias * float(l) + detune_offset);
828
829 omega[l] = std::min(M_PI, (double)pitch_to_omega(pitch + detune));
830 sine[l].set_rate(omega[l]);
831 }
832
833 for (int k = 0; k < BLOCK_SIZE_OS; k++)
834 {
835 float outL = 0.f, outR = 0.f;
836
837 for (int u = 0; u < n_unison; u++)
838 {
839 sine[u].process();
840
841 float sinx = sine[u].r;
842 float cosx = sine[u].i;
843
844 float out_local = singleValueFromSinAndCos<mode>(sinx, cosx);
845
846 outL += (panL[u] * out_local) * out_attenuation * playingramp[u];
847 outR += (panR[u] * out_local) * out_attenuation * playingramp[u];
848
849 if (playingramp[u] < 1)
850 playingramp[u] += dplaying;
851 if (playingramp[u] > 1)
852 playingramp[u] = 1;
853 }
854
855 if (stereo)
856 {
857 output[k] = outL;
858 outputR[k] = outR;
859 }
860 else
861 output[k] = (outL + outR) / 2;
862 }
863 }
864 }
865
process_block(float pitch,float drift,bool stereo,bool FM,float fmdepth)866 void SineOscillator::process_block(float pitch, float drift, bool stereo, bool FM, float fmdepth)
867 {
868 auto mode = localcopy[id_mode].i;
869
870 if (localcopy[id_fmlegacy].i == 0)
871 {
872 #define DOCASE(x) \
873 case x: \
874 process_block_legacy<x>(pitch, drift, stereo, FM, fmdepth); \
875 break;
876
877 switch (mode)
878 {
879 DOCASE(0)
880 DOCASE(1)
881 DOCASE(2)
882 DOCASE(3)
883 DOCASE(4)
884 DOCASE(5)
885 DOCASE(6)
886 DOCASE(7)
887 DOCASE(8)
888 DOCASE(9)
889 DOCASE(10)
890
891 DOCASE(11)
892 DOCASE(12)
893 DOCASE(13)
894 DOCASE(14)
895 DOCASE(15)
896 DOCASE(16)
897 DOCASE(17)
898 DOCASE(18)
899 DOCASE(19)
900 DOCASE(20)
901 DOCASE(21)
902 DOCASE(22)
903 DOCASE(23)
904 DOCASE(24)
905 DOCASE(25)
906 DOCASE(26)
907 DOCASE(27)
908 }
909 #undef DOCASE
910 applyFilter();
911
912 if (charFilt.doFilter)
913 {
914 if (stereo)
915 {
916 charFilt.process_block_stereo(output, outputR, BLOCK_SIZE_OS);
917 }
918 else
919 {
920 charFilt.process_block(output, BLOCK_SIZE_OS);
921 }
922 }
923
924 return;
925 }
926
927 fb_val = oscdata->p[sine_feedback].get_extended(localcopy[id_fb].f);
928
929 #define DOCASE(x) \
930 case x: \
931 if (stereo) \
932 if (FM) \
933 process_block_internal<x, true, true>(pitch, drift, fmdepth); \
934 else \
935 process_block_internal<x, true, false>(pitch, drift, fmdepth); \
936 else if (FM) \
937 process_block_internal<x, false, true>(pitch, drift, fmdepth); \
938 else \
939 process_block_internal<x, false, false>(pitch, drift, fmdepth); \
940 break;
941
942 switch (mode)
943 {
944 DOCASE(0)
945 DOCASE(1)
946 DOCASE(2)
947 DOCASE(3)
948 DOCASE(4)
949 DOCASE(5)
950 DOCASE(6)
951 DOCASE(7)
952 DOCASE(8)
953 DOCASE(9)
954 DOCASE(10)
955
956 DOCASE(11)
957 DOCASE(12)
958 DOCASE(13)
959 DOCASE(14)
960 DOCASE(15)
961 DOCASE(16)
962 DOCASE(17)
963 DOCASE(18)
964 DOCASE(19)
965 DOCASE(20)
966 DOCASE(21)
967 DOCASE(22)
968 DOCASE(23)
969 DOCASE(24)
970 DOCASE(25)
971 DOCASE(26)
972 DOCASE(27)
973 }
974 #undef DOCASE
975
976 if (charFilt.doFilter)
977 {
978 if (stereo)
979 {
980 charFilt.process_block_stereo(output, outputR, BLOCK_SIZE_OS);
981 }
982 else
983 {
984 charFilt.process_block(output, BLOCK_SIZE_OS);
985 }
986 }
987 }
988
init_ctrltypes()989 void SineOscillator::init_ctrltypes()
990 {
991 oscdata->p[sine_shape].set_name("Shape");
992 oscdata->p[sine_shape].set_type(ct_sineoscmode);
993
994 oscdata->p[sine_feedback].set_name("Feedback");
995 oscdata->p[sine_feedback].set_type(ct_osc_feedback_negative);
996
997 oscdata->p[sine_FMmode].set_name("Behavior");
998 oscdata->p[sine_FMmode].set_type(ct_sinefmlegacy);
999
1000 oscdata->p[sine_lowcut].set_name("Low Cut");
1001 oscdata->p[sine_lowcut].set_type(ct_freq_audible_deactivatable);
1002
1003 oscdata->p[sine_highcut].set_name("High Cut");
1004 oscdata->p[sine_highcut].set_type(ct_freq_audible_deactivatable);
1005
1006 oscdata->p[sine_unison_detune].set_name("Unison Detune");
1007 oscdata->p[sine_unison_detune].set_type(ct_oscspread);
1008
1009 oscdata->p[sine_unison_voices].set_name("Unison Voices");
1010 oscdata->p[sine_unison_voices].set_type(ct_osccount);
1011 }
1012
init_default_values()1013 void SineOscillator::init_default_values()
1014 {
1015 oscdata->p[sine_shape].val.i = 0;
1016 oscdata->p[sine_feedback].val.f = 0;
1017 oscdata->p[sine_FMmode].val.i = 1;
1018
1019 oscdata->p[sine_lowcut].val.f = oscdata->p[sine_lowcut].val_min.f; // high cut at the bottom
1020 oscdata->p[sine_lowcut].deactivated = true;
1021 oscdata->p[sine_highcut].val.f = oscdata->p[sine_highcut].val_max.f; // low cut at the top
1022 oscdata->p[sine_highcut].deactivated = true;
1023
1024 oscdata->p[sine_unison_detune].val.f = 0.1;
1025 oscdata->p[sine_unison_voices].val.i = 1;
1026 }
1027
handleStreamingMismatches(int streamingRevision,int currentSynthStreamingRevision)1028 void SineOscillator::handleStreamingMismatches(int streamingRevision,
1029 int currentSynthStreamingRevision)
1030 {
1031 if (streamingRevision <= 9)
1032 {
1033 oscdata->p[sine_shape].val.i = oscdata->p[sine_shape].val_min.i;
1034 }
1035
1036 if (streamingRevision <= 10)
1037 {
1038 oscdata->p[sine_feedback].val.f = 0;
1039 oscdata->p[sine_FMmode].val.i = 0;
1040 }
1041
1042 if (streamingRevision <= 12)
1043 {
1044 oscdata->p[sine_lowcut].val.f = oscdata->p[sine_lowcut].val_min.f; // high cut at the bottom
1045 oscdata->p[sine_lowcut].deactivated = true;
1046 oscdata->p[sine_highcut].val.f = oscdata->p[sine_highcut].val_max.f; // low cut at the top
1047 oscdata->p[sine_highcut].deactivated = true;
1048 oscdata->p[sine_feedback].set_type(ct_osc_feedback);
1049
1050 int wave_remap[] = {0, 8, 9, 10, 1, 11, 4, 12, 13, 2, 3, 5, 6, 7, 14, 15, 16, 17, 18, 19};
1051
1052 // range checking for garbage data
1053 if (oscdata->p[sine_shape].val.i < 0 ||
1054 (oscdata->p[sine_shape].val.i >= (sizeof wave_remap) / sizeof *wave_remap))
1055 oscdata->p[sine_shape].val.i = oscdata->p[sine_shape].val_min.i;
1056 else
1057 {
1058 // make sure old patches still point to the correct waveforms
1059 oscdata->p[sine_shape].val.i = wave_remap[oscdata->p[sine_shape].val.i];
1060 }
1061 }
1062
1063 if (streamingRevision <= 15)
1064 {
1065 oscdata->retrigger.val.b = true;
1066 }
1067 }
1068
valueFromSinAndCos(float sinx,float cosx,int wfMode)1069 float SineOscillator::valueFromSinAndCos(float sinx, float cosx, int wfMode)
1070 {
1071 // As you port to SSE, put them here
1072 switch (wfMode)
1073 {
1074 case 0:
1075 return sinx;
1076 case 1:
1077 return singleValueFromSinAndCos<1>(sinx, cosx);
1078 case 2:
1079 return singleValueFromSinAndCos<2>(sinx, cosx);
1080 case 3:
1081 return singleValueFromSinAndCos<3>(sinx, cosx);
1082 case 4:
1083 return singleValueFromSinAndCos<4>(sinx, cosx);
1084 case 5:
1085 return singleValueFromSinAndCos<5>(sinx, cosx);
1086 case 6:
1087 return singleValueFromSinAndCos<6>(sinx, cosx);
1088 case 7:
1089 return singleValueFromSinAndCos<7>(sinx, cosx);
1090 case 8:
1091 return singleValueFromSinAndCos<8>(sinx, cosx);
1092 case 9:
1093 return singleValueFromSinAndCos<9>(sinx, cosx);
1094 case 10:
1095 return singleValueFromSinAndCos<10>(sinx, cosx);
1096 case 11:
1097 return singleValueFromSinAndCos<11>(sinx, cosx);
1098 case 12:
1099 return singleValueFromSinAndCos<12>(sinx, cosx);
1100 case 13:
1101 return singleValueFromSinAndCos<13>(sinx, cosx);
1102 case 14:
1103 return singleValueFromSinAndCos<14>(sinx, cosx);
1104 case 15:
1105 return singleValueFromSinAndCos<15>(sinx, cosx);
1106 case 16:
1107 return singleValueFromSinAndCos<16>(sinx, cosx);
1108 case 17:
1109 return singleValueFromSinAndCos<17>(sinx, cosx);
1110 case 18:
1111 return singleValueFromSinAndCos<18>(sinx, cosx);
1112 case 19:
1113 return singleValueFromSinAndCos<19>(sinx, cosx);
1114 case 20:
1115 return singleValueFromSinAndCos<20>(sinx, cosx);
1116 case 21:
1117 return singleValueFromSinAndCos<21>(sinx, cosx);
1118 case 22:
1119 return singleValueFromSinAndCos<22>(sinx, cosx);
1120 case 23:
1121 return singleValueFromSinAndCos<23>(sinx, cosx);
1122 case 24:
1123 return singleValueFromSinAndCos<24>(sinx, cosx);
1124 case 25:
1125 return singleValueFromSinAndCos<25>(sinx, cosx);
1126 case 26:
1127 return singleValueFromSinAndCos<26>(sinx, cosx);
1128 case 27:
1129 return singleValueFromSinAndCos<27>(sinx, cosx);
1130 }
1131
1132 return 0;
1133 }
1134