no quarks, just a couple of custom classes to help drive the core of it:
Nearest_Even {
*ar {
arg val;
var val_floor, val_ceil, res, distance;
val_floor = val.floor;
val_ceil = val.ceil;
res = Select.ar (val % 2,
[ val_floor, val_ceil ],
);
distance = (val - res).abs;
^ [ res, distance ];
}
}
Nearest_Odd {
*ar {
arg val;
var val_floor, val_ceil, res, distance;
val_floor = val.floor;
val_ceil = val.ceil;
res = Select.ar (val + 1 % 2,
[ val_floor, val_ceil ],
);
distance = (val - res).abs;
^ [ res, distance ];
}
}
Crossfade_Formant {
*ar {
arg phasor = 0, phase_mod = 0, harm = 1, pm_index = 1, amp = 1; // lag = 0;
var harm_even, harm_odd, sig_even, sig_odd, sig, phase;
phase = phase_mod * pm_index;
harm_even = Nearest_Even.ar (harm);
sig_even = cos (phasor * 2pi * harm_even[0] + phase);
harm_odd = Nearest_Odd.ar (harm);
sig_odd = cos (phasor * 2pi * harm_odd[0] + phase);
sig = XFade2.ar (sig_even, sig_odd, harm_even[1] * 2 - 1) * amp;
^ sig;
}
}
and then the bulk of the synthesis work is done in the SynthDef:
detune = LFNoise2.ar (7) / 60;
vibrato = SinOsc.ar (2 ** (vibrato_freq_env - 6)) * vibrato_width_env / 12;
fund_phasor = LFSaw.ar (freq_0_env * (2 ** (detune + vibrato))).range (0, 1);
modulator = sin (fund_phasor * 2pi);
car_0 = amp_env ** 0.5 * Crossfade_Formant.ar (fund_phasor, modulator, K2A.ar (1), ind_0_env, amp_0_env);
car_1 = amp_env ** 1.5 * Crossfade_Formant.ar (fund_phasor, modulator, harm_1_env, ind_1_env, amp_1_env);
car_2 = amp_env ** 2 * Crossfade_Formant.ar (fund_phasor, modulator, harm_2_env, ind_2_env, amp_2_env);
sig = car_0 + car_1 + car_2;
sig = sig * drive;
sig = sig.tanh;