// SuperCollider audio for Dale Parsons' chess music program // H. James Harkins /* for convenience when running the python components adjust the paths for your environment, then copy and paste into terminal not needed for the supercollider side PYTHONPATH=~/Downloads/chess export PYTHONPATH cd Downloads/chess/chess python */ // resources are encapsulated in an environment, shouldn't interfere with other stuff // use server window volume control if it gets too loud // to start: select all between these parens and run ( ~chessAudio = Environment.make({ // change to false if you want to leave scsynth running after cleanup ~quitServerOnCleanup = true; ~run = { ~init.(); ~makeGui.(); // later }; ~numBanks = 2; ~numOscil = 32; ~synthdefs = [ SynthDef(\sqr1, { |bus, freq, left, right, gate = 1| var sig = Pulse.ar([freq, freq * 1.002], 0.5); Out.ar(bus, RLPF.ar(sig, ExpRand(500, 8000), ExpRand(0.05, 0.4)) * [left, right] * EnvGen.kr(Env.adsr(0.01, 0.2, 0.7, 0.1), gate, doneAction: 2)) }), SynthDef(\saw1, { |bus, freq, left, right, gate = 1| var sig = Saw.ar([freq, freq * 1.002]); Out.ar(bus, RLPF.ar(sig, ExpRand(500, 8000), ExpRand(0.05, 0.4)) * [left, right] * EnvGen.kr(Env.adsr(0.01, 0.2, 0.7, 0.1), gate, doneAction: 2)) }) ]; ~init = { // ~msgLog = List.new; ~responder = OSCresponderNode(nil, 'list', { |time, resp, msg| // ~msgLog.add([time, msg]); ~hitOscillator.(*msg); }).add; ~oscils = { Array.newClear(~numOscil) } ! ~numBanks; ~s = Server.default; ~s.waitForBoot(inEnvir { ~grp = Group(~s); ~bus = Bus.audio(~s, 2); // all synths play here ~limiter = { var sig = In.ar(~bus, 2); Limiter.ar(sig, 0.95) }.play(~grp); ~synthdefs.do { |def| def.memStore }; }); }; ~hitOscillator = { |cmd, bank, osc, freq, phase, left, right| if(freq <= 0 or: { left == 0 and: { right == 0 } }) { // stop oscil if(~oscils[bank][osc].notNil) { ~s.sendBundle(0.2, ~oscils[bank][osc].setMsg(\gate, 0)); ~oscils[bank][osc] = nil; ~updateGui.(bank, osc, freq); } } { // start oscil if(~oscils[bank][osc].notNil) { ~s.sendBundle(0.2, [\error, -1], ~oscils[bank][osc].setMsg(\gate, 0)); }; ~oscils[bank][osc] = ~playSynth.(bank, osc, freq, phase, left, right); ~updateGui.(bank, osc, freq); }; }; ~playSynth = { |bank, osc, freq, phase, left, right| var return; ~s.makeBundle(0.2, { return = Synth(~synthdefs[bank].name, [bus: ~bus, freq: freq, left: left, right: right], target: ~grp, addAction: \addToHead); }); return }; ~cleanup = { ~responder.remove; if(~quitServerOnCleanup) { // server quit obviously kills synthesis objects ~s.quit; } { // remove synthesis objects without stopping the synth ~oscils.do { |bank| bank.do { |osc| osc.release; } }; ~grp.free; ~bus.free; // ~limiter is freed by clearing the group }; ~closeGui.(); }; }); ~chessAudio.push; ~run.(); ) // to finish: ( ~cleanup.(); Environment.pop; ~chessAudio = nil; ) // test code m = #[ [\list, 0, 0, 247.500000, 0.000000, 0.375000, 0.125000], [\list, 0, 0, -1.000000, -1.000000, 0.000000, 0.000000], [\list, 1, 1, 247.500000, 0.000000, 0.125000, 0.375000], [\list, 1, 1, -1.000000, -1.000000, 0.000000, 0.000000] ]; n = NetAddr("127.0.0.1", NetAddr.langPort); r = Routine { m.do { |msg| n.sendMsg(*msg); 1.yield; } }; // each time you run this line, one of the test messages will be processed r.next;