Skip to content

Instantly share code, notes, and snippets.

@catfact
Last active January 21, 2020 04:48
Show Gist options
  • Select an option

  • Save catfact/2e47684e028b5b1b8a50e75dd6253089 to your computer and use it in GitHub Desktop.

Select an option

Save catfact/2e47684e028b5b1b8a50e75dd6253089 to your computer and use it in GitHub Desktop.
solinish
// 8-voice paraphonic synth,
// _very loosely_ inspired by ARP Solina architecture
Routine {
// first element is an oscillator.
// classically this is a sawtooth, with some nonlinear lowpass filtering
// we'll expand on it with:
// - triangle->saw width control
// - sine and pulse options
// - random width and hz modulation
SynthDef.new(\slsh_osc, {
arg out=0, amp=0.25,
hz=220, cutoff=10000, w=0, z=0,
hz_drift_amt = 0,
hz_drift_rate = 1.0,
width_drift_amt = 0,
width_drift_rate = 1.0;
var f, w0, w1, waves, snd;
f = hz * (1 + LFNoise2.ar(hz_drift_rate, mul:hz_drift_amt));
f = Lag.ar(f, 0.005);
w0 = w*pi*0.5 * ( 1 + LFNoise2.ar(width_drift_rate, mul:width_drift_amt));
w1 = ((w*0.49) + 0.5) * (1 + LFNoise2.ar(width_drift_rate, mul:width_drift_amt));
waves =[
SinOscFB.ar(f, w0),
VarSaw.ar(f, 0, w1),
Pulse.ar(f, w1)
];
snd = Select.ar(z, waves);
Out.ar(out, snd * amp);
}).send(s);
// filter stage
// classically, a smidge of exponential lag.
// we'll expand by adding resonant highpass+lowpass
SynthDef.new(\slsh_filter, {
arg in=0, out=0,
lag_time=0.0001,
hpf_hz=20, hpf_rq = 1,
lpf_hz=10000, lpf_rq = 1;
var snd;
snd = In.ar(in);
snd = Lag.ar(snd, lag_time);
snd = RLPF.ar(snd, lpf_hz, lpf_rq);
snd = RHPF.ar(snd, hpf_hz, hpf_rq);
ReplaceOut.ar(out, snd);
}).send(s);
// the "ensemble" effect is basically just a few choruses in parallel
// each chorus is an interpolated delay line with feedback and time modulation.
// we'll expand on it by adding:
// - random modulation,
// - a resonant filter in the feedback path (TODO)
// - panning per chorus
// other TODOs:
// - some kinda saturation to emulate BBD
// - LFO waveshape options
SynthDef.new(\slsh_chorus, {
arg in=0, out=0, dry=0.5, wet=0.5, pan=0,
time=0.027, mod_rate=0.2, mod_depth=0.001, fb=0.1,
maxdelaytime=1.0;
var snd, t, del;
snd = In.ar(in);
t = time * (1 + SinOsc.ar(mod_rate, mul:mod_depth));
del = DelayC.ar(snd + LocalIn.ar, maxdelaytime, t);
LocalOut.ar(del * fb);
Out.ar(out, Pan2.ar(Mix.new([dry*snd, wet*del]), pan));
}).send(s);
s.sync;
// all voices are summed to a mono bus,
// then 3 choruses are applied in stereo
~voice_mix_bus = Bus.audio(s, 1);
~chorus_group = Group.new(s, \addToTail);
~chorus= [
// chorus 1
Synth.new(\slsh_chorus, [
\in, ~voice_mix_bus.index, \out, 0, \dry, 0.5, \wet, 0.5,
\pan, -0.5, \time, 1/6, \mod_rate, 1/5, \mod_depth, 1/8, \fb, -24.dbamp
], target:~chorus_group),
// chorus 2
Synth.new(\slsh_chorus, [
\in, ~voice_mix_bus.index, \out, 0, \dry, 0.5, \wet, 0.5,
\pan, 0, \time, 1/7, \mod_rate, 1/6, \mod_depth, 1/8, \fb, -24.dbamp
], target:~chorus_group),
// chorus 3
Synth.new(\slsh_chorus, [
\in, ~voice_mix_bus.index, \out, 0, \dry, 0.5, \wet, 0.5,
\pan, 0.5, \time, 1/8, \mod_rate, 1/7, \mod_depth, 1/8, \fb, -24.dbamp
], target:~chorus_group),
];
/// each slsh voice will have:
// - a group
// - a mono output bus
// - two oscillator synths
// - one multi-filter synth
// - amplitude envelope synth
// voices are created statically!
//
SynthDef.new(\slsh_amp_env, {
arg in=0, out=0,
gate=0, amp=0.1,
atk=0.1, dec=0.1, sus=1.0, rel=0.1;
var aenv;
aenv = EnvGen.ar(Env.adsr(atk, dec, sus, rel), gate);
Out.ar(out, In.ar(in) * aenv * amp);
}).send(s);
s.sync;
n = 8;
// NB: busses are not stored in the voice object,
// just in case we want to switch to dynamic synth allocation
// (busses would remain static in that case i think)
~voice_bus = Array.fill(n, {Bus.audio(s, 1)});
~voices = Array.newClear(n);
~voice_group = Group.new(s, \addToHead);
~make_voice = { arg idx;
var bidx = ~voice_bus[idx].index;
var v;
v = ();
v.gr = Group.new(~voice_group);
// TODO: use control busses for synth params, &c
v.osc1 = Synth.new(\slsh_osc, [\out, bidx, \hz, 100], v.gr, \addToTail);
v.osc2 = Synth.new(\slsh_osc, [\out, bidx, \hz, 150], v.gr, \addToTail);
v.filter = Synth.new(\slsh_filter, [\in, bidx, \out, bidx], v.gr, \addToTail);
v.aenv = Synth.new(\slsh_amp_env, [\in, bidx, \out, ~voice_mix_bus.index], v.gr, \addToTail);
~voices[idx] = v;
};
~free_voice = { arg idx; ~voices[idx].group.free; };
// create the voices
n.do({
arg i;
i.postln;
~make_voice.value(i);
});
}.play;
//------------------
//-- execute this chunk...
// set hz array
(
~v_hz = { arg idx, hz=[110, 165];
~voices[idx].osc1.set(\hz, hz[0]);
~voices[idx].osc2.set(\hz, hz[1]);
};
~voices.do({ arg x, i; [i, x].postln; });
~voice_bus.do({ arg x, i; [i, x].postln; });
~v_on = { arg idx; ~voices[idx].aenv.set(\gate, 1); };
~v_off = { arg idx; ~voices[idx].aenv.set(\gate, 0); };
)
//-----------------------------------
//------------------------
//-- ...then these lines
~v_on.value(0); // turn on
~v_hz.value(0, [110, 220]); // 2 oscs in octaves
~voices[0].osc1.set(\z, 1); // low osc to saw
~voices[0].osc2.set(\z, 2); // high osc to pulse
~voices[0].filter.set(\lpf_hz, 880 * 2); // close the LPF a bit..
~voices[0].filter.set(\lpf_rq, 0.5); // ..and give it a little resonance
// voice 2
~v_on.value(1);
~v_hz.value(1, [220 * 2, 330 * 4]); // 2 oscs in 5th+1oct
~voices[1].osc1.set(\w, 0.5); // ... add some feedback
~voices[1].filter.set(\hpf_hz, 570); // close the HPF a bit...
~voices[1].filter.set(\hpf_rq, 0.25); // ..and give it a little resonance
~v_off.value(0);
~v_off.value(1);
/// make the choruses fast and shallow, instead of fat and slow
/// also hardpan, bigger base delay, no feedback
~chorus[0].set(\mod_depth, 1/256);
~chorus[0].set(\mod_rate, 4);
~chorus[0].set(\time, 1/6);
~chorus[0].set(\fb, 0);
~chorus[0].set(\pan, -1);
~chorus[1].set(\mod_depth, 1/256);
~chorus[1].set(\mod_rate, 5);
~chorus[1].set(\time, 1/7);
~chorus[1].set(\fb, 0);
~chorus[1].set(\pan, 0);
~chorus[2].set(\mod_depth, 1/256);
~chorus[2].set(\mod_rate, 6);
~chorus[2].set(\time, 1/5);
~chorus[2].set(\fb, 0);
~chorus[2].set(\pan, 1);
/*
~voice_bus[1].scope;
s.scope;
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment