{
   "ancestor_list" : [
      "1-4Wh"
   ],
   "description" : "prototype of simple metre beat grid running on server w/ synced TempoBusClock",
   "name" : "beat grid",
   "author" : "vividsnow",
   "id" : "1-4Wm",
   "is_private" : null,
   "code" : "( // beat grid object prototype\r\n~g = {\r\nvar l = 6,  // base pulse: 2**l, so max tempo (on 48khz) is around 48000/64/(2**6) = 11.7\r\nstate_bus = Bus.control(numChannels:2), // base beat and beat counter\r\nrate_bus = { var b = Bus.control; b.set(0.5.rrand(2).debug('beat rate')); b }.(), // set beat rate\r\nbeat_runner = { var  // base beat runner \r\n\tbase = Impulse.kr(In.kr(rate_bus)*(2**l)), // base pulse\r\n\tc = PulseCount.kr(base, PulseDivider.kr(base,2**(l*4))); // l*4 - max multi beat partition\r\n\tOut.kr(state_bus, [base,c]) }.play,\r\nq = { |n=0,s=0,p=0,q| var i = In.kr(state_bus,2), c = Latch.kr(i[1],1);  // server grid quantizer\r\n\tn = n + l; q = if(q.isNil, {n}, {q+l}); // if q is defined than start on specified by q grid place\r\n\t(PulseCount.kr(i[0]) + neg(2**q-(c%(2**q))) + (s*2.pow(n-(p?0)-1)) % (2**n))/*.abs*/.sign bitXor: 1 bitAnd: i[0] }, // pulse divider extension\r\n// proto object\r\ngrid = (l:l,q:q,state_bus:state_bus,rate_bus:rate_bus,beat_runner:beat_runner);\r\n\r\nrate_bus.get({ |rate| var // sclang-side synced clock\r\n\tstatus = [nil],\r\n\trater = { Out.kr(rate_bus, \\tempo.kr(rate)) }.play,\r\n\tclock = TempoBusClock(rater, rate),\r\n\tnotifier = { var bc = In.kr(state_bus,2); SendReply.kr(bc[0], '/pulse', [bc[1],In.kr(rate_bus)]) }.play,\r\n\tosc = OSCFunc({ |msg| status[0] = msg[3]; clock.beats = msg[3]/(2**l) + (Server.default.latency / clock.beatDur) }, '/pulse');\r\n\tgrid.putPairs([status: status, notifier: notifier, rater: rater, osc: osc, clock: clock, on:[0],\r\n\t\tnextTimeOnGrid: { |self, clock| var n = l + self.on[0], // client grid quantizer\r\n\t\t\tbeats = (2**n) - (self.status[0] % (2**n)) + (if(self.on.size > 1, {2**(n-1-(self.on[2]?0))*self.on[1]}, {0})) / (2**l);\r\n\t\t\tself.on.debug('on');\r\n\t\t\tclock.beats + beats }])});\r\n\tgrid\r\n}.()\r\n)\r\n\r\n( // survive cmdperiod\r\nCmdPeriod.add(~g.beat_runner.addUniqueMethod(\\cmdPeriod, _.debug('still running')));\r\nCmdPeriod.add(~g.rater.addUniqueMethod(\\cmdPeriod, _.debug('still running')));\r\nCmdPeriod.add(~g.notifier.addUniqueMethod(\\cmdPeriod, _.debug('still running')));\r\n~g.clock.permanent = true;\r\n~g.osc.permanent = true;\r\nServerTree.add(~g, Server.default);\r\n~g[\\doOnServerTree] = { \r\n    Synth(~g.beat_runner.defName); Synth(~g.rater.defName); Synth(~g.notifier.defName);\r\n    ~g.clock.tempo = ~g.clock.tempo }\r\n)\r\n\r\n// examples\r\n\r\n~g.clock.beats; // elapsed beats\r\n~g.status; // elapsed base pulses\r\n~g.clock.tempo; // current tempo\r\n~g.clock.tempo = 1; // change tempo (both sclang+scsynth)\r\n\r\n( // shortcuts\r\n~q = ~g[\\q]; // quantizer: ~q.(n,m,k) means impulse each 2**n beat with m*(2**(n-1-k)) beat shift\r\n~d = {|l=0| 2**l/In.kr(~g.rate_bus) }; // beat duration: ~d.(l) means duration of beatdur**l\r\n~r = {|l=0| In.kr(~g.rate_bus)/(2**l) }; // beat rate: ~d.(l) means rate of beatrate**l\r\n~e = {|p,d,a,c|EnvGen.ar(Env.perc(a?0.05,d?0.5,1,c?(-4)),p).lag(1e-3)}; // trigable perc envelope: triger, duration, attack, curve \r\n~sn = {|...a|SinOsc.ar(*a)};\r\n~w = {|p,mn,mx|Demand.kr(p,0,Diwhite(mn,mx)).max(mn)};\r\n~l = {|d=12,s=1,e=0|Line.ar(s,e,d,doneAction:2)};\r\n)\r\n\r\n/* grid quantizer explained:\r\n~q.(n,m,k,q) means impulse each 2**n beat (starting from 2**(q?n) beat) with m*(2**(n-1-k)) beat shift\r\ndefault values: n=0,m=0,k=0,q=null\r\n*/\r\n\r\n// simple examples\r\n{~sn.([60,61]) * ~e.(~q.(),~d.())/4 * ~l.()}.play // every beat\r\n{~sn.(160) * ~e.(~q.(0,0.5),0.2)/4!2 * ~l.()}.play // every beat + 1/4 beat shift\r\n{~sn.(320) * ~e.(~q.(0,1,1),0.2)/4!2 * ~l.()}.play // same since: ~q.(0,0.5) equals ~q.(0,1,1)\r\n{~sn.(Stepper.kr(~q.(),0,60,90,2).midicps) * ~e.(~q.(0,0.5),0.2)/4!2 * ~l.()}.play // every beat + 1/4 beat shift\r\n{~sn.(360) * ~e.(~q.(0,3,1),0.2)/4!2 * ~l.()}.play // every beat + 3/4 beat shift\r\n{~sn.(120) * ~e.(~q.(0,1),0.05)/4!2 * ~l.()}.play // every beat + 1/2 beat shift\r\n{~sn.(240) * ~e.(~q.(1,1),0.05)/4!2 * ~l.()}.play // every 2 beat + 1 beat shift\r\n{~sn.(200.exprand(1000)) * ~e.(~q.(1,1,0),0.05)/4!2 * ~l.()}.play // same on other freq\r\n{~sn.(200.exprand(1000)) * ~e.(~q.(1,3,1),0.05)/4!2 * ~l.()}.play // every 2 beat + 3/2 beat shift\r\n{~sn.(200.exprand(1000)) * ~e.(~q.(1,3,2),0.05)/4!2 * ~l.()}.play // every 2 beat + 3/4 beat shift\r\n{~sn.(200.exprand(1000)) * ~e.(~q.(1,[2,3],2),0.05)/4 * ~l.()}.play // every 2 beat + 2/4 beat shift on left and 3/4 beat shift on right\r\n{~sn.(Stepper.kr(~q.(2),0,1)*60) * ~e.(~q.(2,[0,1,2],2).sum,0.1)/4!2 * ~l.(30)}.play // every 4 beat make series of 3 pulses separated by 1/2 beat \r\n\r\n// run pattern in sync with server tempo grid\r\n(Pdef(\\t, Pbind(*[\r\ndegree:Pseries(), delta: Pseq(1!10), dur:1\r\n])).play(~g.clock, quant:[1,0.5]))  // default quant: i.e. on first 1.5 beat\r\nPdef(\\t).stop;\r\n\r\n(Pdef(\\tt, Pbind(*[ // grid\r\ndegree:Pseries(), delta: Pseq(2!10,inf), dur:0.2, \r\n])).play(~g.clock, quant:(parent:~g, on:[2,1.5])))  // position like ~q.(...), but args in \"on\" key (here: on nearest 4 beat line + 3 beat shift)\r\nPdef(\\tt).stop;\r\n\r\n// live coding example\r\n~g.clock.tempo = 1;\r\nNdef(\\bk, {Formlet.ar(LPF.ar(T2A.ar(~q.(-2) bitXor: ~q.()), ~w.(~q.(3),250,500)), Demand.kr(~q.(2), 0, Dbrown(120,420,20)).max(120), 0.01, ~d.(-1)) ! 2 / 4 }).play;\r\nNdef(\\k, {~sn.(60) * ~e.(~q.(), ~d.(-2)) ! 2 / 8 }).play;\r\nNdef(\\k1, {~sn.(490.rrand(800)) * ~e.(~q.(1,1.5), ~d.(-2)) ! 2 / 6 }).play;\r\nNdef(\\k2, { FreeVerb.ar(RLPF.ar(Saw.ar([90,140]).mean,370) * ~e.(~q.(-1) bitXor: ~q.(1), 0.15) ! 2) / 2 }).play;\r\n\r\nNdef(\\k3, {RLPF.ar(Pulse.ar(~w.(~q.(2),60,110)),~w.(~q.(4),180,300)) * ~e.(~q.(0,0.5), 0.1) ! 2 / 3 }).play;\r\n\r\nNdef(\\k2, {BPF.ar(Saw.ar(~w.(~q.(2),60,110)),~w.(~q.(3),180,300)) * ~e.(~q.(-1) bitXor: ~q.(1), 0.15) ! 2 / 2 }).play;\r\n\r\n( Ndef(\\k1, {\r\n~sn.(~w.(~q.(3),60,80).midicps*~sn.(~w.(~q.(),5,18)).range(0.05,1.03))\r\n.madd(0.95,0.05*~sn.(~w.(~q.(4),200,800))).tanh ! 2 * ~e.(~q.(3,3,1), ~d.(1)) / 8 }).play )\r\n\r\nNdef(\\k, {~sn.(~w.(~q.(3),60,80)) * ~e.(~q.(), ~d.() * ~w.(~q.(2),4,8)/4) ! 2 / 12 }).play;\r\n\r\n( Ndef(\\p, { var a = Array; DynKlank.ar(`[a.exprand(8,2e2,2e4).sort, a.exprand(8,0.1,1).sort.reverse, a.exprand(8,0.25,1).sort.reverse],\r\nT2A.ar(~q.(-1,1) bitXor: ~q.(1,1,1)) / 30 * LFNoise0.ar(1/4).madd(0.7,0.3), ~w.(~q.(3),4,8)/4, 0, ~w.(~q.(3),1,4)/4) ! 2 }).play )\r\n\r\n(Ndef(\\trt, { var q = ~q.(2,{~w.(~q.(3),0,15)}!6,3).scramble.clump(2).sum;\r\n~sn.(Stepper.kr(q,0,1,20).linexp(1,20,60+[0,2],~w.(~q.(3,[0,1]),0,300,800))).madd(4).tanh.madd(2).pow(3).tanh.madd(0.1) * ~e.(q, ~d.(-2)) }).play)\r\n\r\n~g.clock.tempo = 2; // make it faster",
   "labels" : [
      "rhythmic",
      "beat",
      "prototype",
      "metre",
      "grid"
   ]
}
