«beat grid» by vividsnow

on 15 Jul'14 19:57 in rhythmicbeatprototypemetregrid

prototype of simple metre beat grid running on server w/ synced TempoBusClock

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
( // beat grid object prototype
~g = {
var l = 6,  // base pulse: 2**l, so max tempo (on 48khz) is around 48000/64/(2**6) = 11.7
state_bus = Bus.control(numChannels:2), // base beat and beat counter
rate_bus = { var b = Bus.control; b.set(0.5.rrand(2).debug('beat rate')); b }.(), // set beat rate
beat_runner = { var  // base beat runner 
	base = Impulse.kr(In.kr(rate_bus)*(2**l)), // base pulse
	c = PulseCount.kr(base, PulseDivider.kr(base,2**(l*4))); // l*4 - max multi beat partition
	Out.kr(state_bus, [base,c]) }.play,
q = { |n=0,s=0,p=0,q| var i = In.kr(state_bus,2), c = Latch.kr(i[1],1);  // server grid quantizer
	n = n + l; q = if(q.isNil, {n}, {q+l}); // if q is defined than start on specified by q grid place
	(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
// proto object
grid = (l:l,q:q,state_bus:state_bus,rate_bus:rate_bus,beat_runner:beat_runner);

rate_bus.get({ |rate| var // sclang-side synced clock
	status = [nil],
	rater = { Out.kr(rate_bus, \tempo.kr(rate)) }.play,
	clock = TempoBusClock(rater, rate),
	notifier = { var bc = In.kr(state_bus,2); SendReply.kr(bc[0], '/pulse', [bc[1],In.kr(rate_bus)]) }.play,
	osc = OSCFunc({ |msg| status[0] = msg[3]; clock.beats = msg[3]/(2**l) + (Server.default.latency / clock.beatDur) }, '/pulse');
	grid.putPairs([status: status, notifier: notifier, rater: rater, osc: osc, clock: clock, on:[0],
		nextTimeOnGrid: { |self, clock| var n = l + self.on[0], // client grid quantizer
			beats = (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);
			self.on.debug('on');
			clock.beats + beats }])});
	grid
}.()
)

( // survive cmdperiod
CmdPeriod.add(~g.beat_runner.addUniqueMethod(\cmdPeriod, _.debug('still running')));
CmdPeriod.add(~g.rater.addUniqueMethod(\cmdPeriod, _.debug('still running')));
CmdPeriod.add(~g.notifier.addUniqueMethod(\cmdPeriod, _.debug('still running')));
~g.clock.permanent = true;
~g.osc.permanent = true;
ServerTree.add(~g, Server.default);
~g[\doOnServerTree] = { 
    Synth(~g.beat_runner.defName); Synth(~g.rater.defName); Synth(~g.notifier.defName);
    ~g.clock.tempo = ~g.clock.tempo }
)

// examples

~g.clock.beats; // elapsed beats
~g.status; // elapsed base pulses
~g.clock.tempo; // current tempo
~g.clock.tempo = 1; // change tempo (both sclang+scsynth)

( // shortcuts
~q = ~g[\q]; // quantizer: ~q.(n,m,k) means impulse each 2**n beat with m*(2**(n-1-k)) beat shift
~d = {|l=0| 2**l/In.kr(~g.rate_bus) }; // beat duration: ~d.(l) means duration of beatdur**l
~r = {|l=0| In.kr(~g.rate_bus)/(2**l) }; // beat rate: ~d.(l) means rate of beatrate**l
~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 
~sn = {|...a|SinOsc.ar(*a)};
~w = {|p,mn,mx|Demand.kr(p,0,Diwhite(mn,mx)).max(mn)};
~l = {|d=12,s=1,e=0|Line.ar(s,e,d,doneAction:2)};
)

/* grid quantizer explained:
~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
default values: n=0,m=0,k=0,q=null
*/

// simple examples
{~sn.([60,61]) * ~e.(~q.(),~d.())/4 * ~l.()}.play // every beat
{~sn.(160) * ~e.(~q.(0,0.5),0.2)/4!2 * ~l.()}.play // every beat + 1/4 beat shift
{~sn.(320) * ~e.(~q.(0,1,1),0.2)/4!2 * ~l.()}.play // same since: ~q.(0,0.5) equals ~q.(0,1,1)
{~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
{~sn.(360) * ~e.(~q.(0,3,1),0.2)/4!2 * ~l.()}.play // every beat + 3/4 beat shift
{~sn.(120) * ~e.(~q.(0,1),0.05)/4!2 * ~l.()}.play // every beat + 1/2 beat shift
{~sn.(240) * ~e.(~q.(1,1),0.05)/4!2 * ~l.()}.play // every 2 beat + 1 beat shift
{~sn.(200.exprand(1000)) * ~e.(~q.(1,1,0),0.05)/4!2 * ~l.()}.play // same on other freq
{~sn.(200.exprand(1000)) * ~e.(~q.(1,3,1),0.05)/4!2 * ~l.()}.play // every 2 beat + 3/2 beat shift
{~sn.(200.exprand(1000)) * ~e.(~q.(1,3,2),0.05)/4!2 * ~l.()}.play // every 2 beat + 3/4 beat shift
{~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
{~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 

// run pattern in sync with server tempo grid
(Pdef(\t, Pbind(*[
degree:Pseries(), delta: Pseq(1!10), dur:1
])).play(~g.clock, quant:[1,0.5]))  // default quant: i.e. on first 1.5 beat
Pdef(\t).stop;

(Pdef(\tt, Pbind(*[ // grid
degree:Pseries(), delta: Pseq(2!10,inf), dur:0.2, 
])).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)
Pdef(\tt).stop;

// live coding example
~g.clock.tempo = 1;
Ndef(\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;
Ndef(\k, {~sn.(60) * ~e.(~q.(), ~d.(-2)) ! 2 / 8 }).play;
Ndef(\k1, {~sn.(490.rrand(800)) * ~e.(~q.(1,1.5), ~d.(-2)) ! 2 / 6 }).play;
Ndef(\k2, { FreeVerb.ar(RLPF.ar(Saw.ar([90,140]).mean,370) * ~e.(~q.(-1) bitXor: ~q.(1), 0.15) ! 2) / 2 }).play;

Ndef(\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;

Ndef(\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;

( Ndef(\k1, {
~sn.(~w.(~q.(3),60,80).midicps*~sn.(~w.(~q.(),5,18)).range(0.05,1.03))
.madd(0.95,0.05*~sn.(~w.(~q.(4),200,800))).tanh ! 2 * ~e.(~q.(3,3,1), ~d.(1)) / 8 }).play )

Ndef(\k, {~sn.(~w.(~q.(3),60,80)) * ~e.(~q.(), ~d.() * ~w.(~q.(2),4,8)/4) ! 2 / 12 }).play;

( 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],
T2A.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 )

(Ndef(\trt, { var q = ~q.(2,{~w.(~q.(3),0,15)}!6,3).scramble.clump(2).sum;
~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)

~g.clock.tempo = 2; // make it faster
ancestors
full graph
raw 6382 chars (focus & ctrl+a+c to copy)
reception
comments