Last active
August 14, 2025 14:20
-
-
Save catfact/538d0c27d3e53c513aa833026f9bb75b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // polyform lite - nb editon v.0.1 @sonoCircuit | |
| // supercollider class based on the skilled labour study written by ezra buchla & dani derks for monome.org | |
| NB_PolyForm { | |
| // classvar <voiceKeys, | |
| classvar <synthParams, <synthGroup, <synthVoices; | |
| // do these all need to be classvars? | |
| // [eb] it strikes me as a little weird to make everything classvars, which are global | |
| // (so these Groups are always present in the background, forever) | |
| // but i guess in the context it's fine | |
| classvar numVoices = 6; | |
| *initClass { | |
| // six voices | |
| // voiceKeys = [ \1, \2, \3, \4, \5, \6]; | |
| // [eb] not needed, i think using a dictionary is weirding things up | |
| // global synth params | |
| synthParams = Dictionary.newFrom([ | |
| \amp, 0.8, | |
| \spread, 0, | |
| \mix, -0.6, | |
| \saw_tune, 0, | |
| \pulse_tune, 0, | |
| \saw_shape, 1, | |
| \pulse_width, 0.5, | |
| \cutoff_lpf, 1200, | |
| \res_lpf, 0.2, | |
| \env_lpf_depth, 0.24, | |
| \attack, 0, | |
| \decay, 2.2, | |
| \sendA, 0, | |
| \sendB, 0 | |
| ]); | |
| // evaluate after startup | |
| StartUp.add { | |
| var s = Server.default; | |
| // make sure that the server has actually finished booting | |
| s.waitForBoot { | |
| // create a group | |
| synthGroup = Group.new(s); | |
| // // create a dictionary to hold the synth voices | |
| // synthVoices = Dictionary.new; | |
| // // iterate over voiceKeys and assign each synthvoice to a new group within synthGroup | |
| // voiceKeys.do({ arg i; | |
| // synthVoices[i] = Group.new(synthGroup); // <<< do these need to be initialized some other way? | |
| // }); | |
| // [eb] don't need a dictionary here | |
| synthVoices = Array.fill(numVoices, { Group.new(synthGroup) }); | |
| // define synthDef | |
| SynthDef(\nb_polyForm,{ | |
| arg note = 60, amp = 1.0, spread = 0, mix = 0, | |
| saw_tune = 0, pulse_tune = 0, saw_shape = 1, pulse_width = 0.5, | |
| cutoff_lpf = 1200, res_lpf = 0.1, env_lpf_depth = 0.2, | |
| gate = 1, attack = 0.001, decay = 10, | |
| sendA = 0, sendB = 0, | |
| out = 0, sendABus = 0, sendBBus = 0; | |
| var env = EnvGen.kr(Env.perc(attack, decay), doneAction: 2); | |
| var env_gate = EnvGate(gate:gate, doneAction:2); | |
| var freq = note.midicps.clip(8, 12544); | |
| var freq_saw = Lag.kr(freq * 2.pow(saw_tune / 12)); | |
| var freq_pulse = Lag.kr(freq * 2.pow(pulse_tune / 12)); | |
| var osc_saw = VarSaw.ar(freq_saw, 0.248, saw_shape); | |
| var osc_pulse = Pulse.ar(freq_pulse, pulse_width); | |
| var osc_mix = XFade2.ar(osc_saw, osc_pulse, mix, amp) * -6.dbamp; | |
| var cut_lin_lpf = cutoff_lpf.explin(20, 18000, 0, 1); | |
| var cut_lpf_mod = cut_lin_lpf + (env * env_lpf_depth); | |
| var cutoff = cut_lpf_mod.linexp(0, 1, 20, 18000); | |
| var rq_lpf = res_lpf.linlin(0, 1, 0, 4); | |
| var osc_lpf = MoogFF.ar(osc_mix, cutoff, rq_lpf) * env * env_gate; | |
| var snd = Pan2.ar(osc_lpf, spread * Rand(-0.7, 0.7)); | |
| Out.ar(out, snd); | |
| Out.ar(sendABus, sendA * snd); | |
| Out.ar(sendBBus, sendB * snd); | |
| }).add; | |
| // add OSC functions | |
| OSCFunc.new({ |msg, time, addr, recvPort| | |
| var i = msg[1].asInteger; | |
| var note = msg[2].asInteger; | |
| var vel = msg[3].asFloat; | |
| synthVoices[i].set(\gate, -1.05); | |
| // [eb] ^ wasn't working with Env.perc / dictionary | |
| Synth.new(\nb_polyForm, | |
| [ | |
| \note, note, | |
| \vel, vel, | |
| \sendABus, ~sendA ? Server.default.outputBus, | |
| \sendBBus, ~sendB ? Server.default.outputBus, | |
| ] ++ synthParams.getPairs, target: synthVoices[i] | |
| ); | |
| }, "/nb_polyform/note_on"); | |
| OSCFunc.new({ |msg, time, addr, recvPort| | |
| var i = msg[1].asInteger; | |
| synthVoices[i].set(\gate, 0); | |
| }, "/nb_polyform/note_off"); | |
| OSCFunc.new({ |msg, time, addr, recvPort| | |
| var key = msg[1].asSymbol; | |
| var val = msg[2].asFloat; | |
| //voiceKeys.do({ arg i; | |
| numVoices.do({ arg i; | |
| synthVoices[i].set(key, val); | |
| // [eb] wasn't working with dictionary, not sure why | |
| }); | |
| synthParams[key] = val; | |
| }, "/nb_polyform/set_param"); | |
| OSCFunc.new({ |msg, time, addr, recvPort| | |
| synthGroup.set(\gate, -1.05); | |
| }, "/nb_polyform/panic"); | |
| OSCFunc.new({ |msg, time, addr, recvPort| | |
| synthGroup.free; | |
| }, "/nb_polyform/free"); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
just looked over your comments and changes and everything make sense to me. thank you, very much appreciated!