Skip to content

Instantly share code, notes, and snippets.

@albertoventurini
Last active March 30, 2018 20:08
Show Gist options
  • Save albertoventurini/43f86d156deb3ba64ef9734c792d3f9c to your computer and use it in GitHub Desktop.
Save albertoventurini/43f86d156deb3ba64ef9734c792d3f9c to your computer and use it in GitHub Desktop.
Multi oscillator synth written in Supercollider
(
s.meter;
s.freqscope;
s.plotTree;
)
(
var g0 = Group.new(s); // g0 = main synth group
var g1 = Group.after(g0); // g1 = effect group
var b0 = Bus.audio(s);
var b1 = Bus.audio(s);
var makeSlider, makeChoice;
var getBus;
var busses = Dictionary.new(0);
var choices = Dictionary.new(0);
var notes = Array.newClear(128);
Window.closeAll;
w = Window("gui", Rect(750, 50, 1200, 800)).front;
w.view.decorator_(FlowLayout(w.bounds, 15@15, 7@7));
makeChoice = {
arg name, items, initValue = 0;
var view, menu, bus;
bus = Bus.control(s);
busses.put(name, bus);
view = CompositeView(w, 200@100);
menu = PopUpMenu(view, 200@50)
.items_(items)
.action_({ |obj|
obj.value.postln;
bus.set(obj.value);
//choices.add(name -> obj.item);
});
StaticText(view, Rect(0, 60, 80, 20)).string_(name);
menu.valueAction = initValue;
};
makeChoice.value("osc1_type", items: [\sin, \tri, \saw, \pulse]);
makeChoice.value("osc1_octave", items: [-2, -1, 0, 1, 2], initValue: 2);
makeChoice.value("osc2_type", items: [\sin, \tri, \saw, \pulse]);
makeChoice.value("osc2_octave", items: [-2, -1, 0, 1, 2], initValue: 2);
makeChoice.value("osc3_type", items: [\sin, \tri, \saw, \pulse]);
makeChoice.value("osc3_octave", items: [-2, -1, 0, 1, 2], initValue: 2);
makeSlider = {
arg name, min = 0, max = 1, initValue = 0, exponential = false;
var view, slider, numberBox, value, onUpdate, valToSlider, sliderToVal, bus;
bus = Bus.control(s);
busses.put(name, bus);
bus.set(initValue);
onUpdate = { |val|
val.postln;
bus.set(val);
};
// Transform a number from the range (min, max) to the range (0, 1)
valToSlider = { |val|
if(exponential, val.explin(min, max, 0, 1), val.linlin(min, max, 0, 1));
};
// Transform a number in the range (0, 1) to the range (min, max)
sliderToVal = { |val|
if(exponential, val.linexp(0, 1, min, max), val.linlin(0, 1, min, max));
};
view = CompositeView(w, 120@200);
slider = Slider(view, Rect(0, 0, 30, 120));
numberBox = NumberBox(view, Rect(0, 125, 80, 25)).clipLo_(min).clipHi_(max).decimals_(4);
slider.action = { |obj|
var val = sliderToVal.value(obj.value);
numberBox.value_(val);
onUpdate.value(val);
};
numberBox.action = { |obj|
var sliderVal = valToSlider.value(obj.value);
slider.value_(sliderVal);
onUpdate.value(obj.value);
};
StaticText(view, Rect(0, 150, 80, 20)).string_(name);
numberBox.valueAction_(initValue);
};
makeSlider.value("lfo1_freq", 0, 10, 1);
makeSlider.value("osc1_amp", 0, 1, 0.1);
makeSlider.value("osc2_amp", 0, 1, 0.1);
makeSlider.value("osc3_amp", 0, 1, 0.1);
makeSlider.value("osc1_detune", 0.9, 1.1, 1);
makeSlider.value("osc2_detune", 0.9, 1.1, 1);
makeSlider.value("osc3_detune", 0.9, 1.1, 1);
makeSlider.value("osc1_pw", 0, 1, 0.7);
makeSlider.value("osc2_pw", 0, 1, 0.34);
makeSlider.value("osc3_pw", 0, 1, 0.49);
makeSlider.value("attack", 0, 5, 0.1);
makeSlider.value("decay", 0, 1, 0);
makeSlider.value("sustain", 0, 1, 1);
makeSlider.value("release", 0, 5, 1);
makeSlider.value("filter_cutoff", 220, 15000, 220, exponential: true);
makeSlider.value("filter_res", 0.05, 0.95, 0.5);
makeSlider.value("filter_attack", 0, 5, 0);
makeSlider.value("filter_decay", 0, 1, 0);
makeSlider.value("filter_sustain", 0, 1, 1);
makeSlider.value("filter_release", 0, 5, 5);
SynthDef.new(\synth, {
arg freq = 440, outbus = 0, gate = 1, amp = 0.15, ffreqBus = 0, rqBus = 0;
var osc1, osc2, osc3, lfo1, sig, env, ffreq, rq, pw1, pw2, pw3, fenv, getOsc;
lfo1 = SinOsc.kr(In.kr(busses["lfo1_freq"])).range(0.9, 1.1);
getOsc = { |name|
var detune = In.kr(busses[name ++ "_detune"]);
var amp = In.kr(busses[name ++ "_amp"]);
var pulseWidth = In.kr(busses[name ++ "_pw"]);
var octave = pow(2, In.kr(busses[name ++ "_octave"]) - 2);
Select.ar(which: In.kr(busses[name ++ "_type"]), array: [
SinOsc.ar(freq * detune * octave, mul: amp)!2,
LFTri.ar(freq * detune * octave, mul: amp)!2,
VarSaw.ar(freq * detune * octave, width: pulseWidth * lfo1, mul: amp)!2,
Pulse.ar(freq * detune * octave, width: pulseWidth * lfo1, mul: amp)!2
]);
};
osc1 = getOsc.value("osc1");
osc2 = getOsc.value("osc2");
osc3 = getOsc.value("osc3");
env = EnvGen.kr(Env.adsr(In.kr(busses["attack"]), In.kr(busses["decay"]), In.kr(busses["sustain"]), In.kr(busses["release"])), gate, doneAction: 2);
sig = Mix.ar([osc1, osc2, osc3]);
sig = sig * env * amp;
ffreq = In.kr(busses["filter_cutoff"]);
fenv = EnvGen.kr(Env.adsr(In.kr(busses["filter_attack"]), In.kr(busses["filter_decay"]), In.kr(busses["filter_sustain"]), In.kr(busses["filter_release"])), gate, doneAction: 2);
ffreq = ffreq * fenv;
rq = In.kr(busses["filter_res"]);
//rq = 0.5;
sig = RLPF.ar(sig, ffreq, rq);
Out.ar(outbus, sig);
}).add;
MIDIdef.noteOn(\noteOn, {
arg vel, nn, chan, src;
[vel, nn, chan, src].postln;
notes[nn] = Synth.new(\synth, [
\freq, nn.midicps,
\gate, 1,
\outbus, 0,
\osc1_type, 123
], g0);
});
MIDIdef.noteOff(\noteOff, {
arg vel, nn;
[vel, nn].postln;
notes[nn].set(\gate, 0);
notes[nn] = nil;
});
)
(
MIDIClient.init;
MIDIIn.connectAll;
)
(
~notes = [30, 34, 37, 42, 46, 49];
p = Pdef(\pattern,
Pbind(
\instrument, \synth,
\midinote, Prand(~notes.collect({ |n| n + 12 }), inf),
\dur, 0.25
)
).play;
)
p.stop;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment