Some loose remarks on this:
.) miSCellaneous_lib contains PSPdiv that can be used to produce complicated polyrhythms. The Sieve and Psieve classes can be used for rhythms as a special application. See Ex.5, audio examples, in the tutorial “Sieves and Psieve patterns”.
.) In general, I find it interesting to design rhythms besides integer relations (which can be regarded as a bias from vocal resp. instrumental music). With computers we are free to generate any rhythm, not being forced to think about playability. Recently I had a discussion with a student, inspired by Bjorklund I wrote a little function to generate rhythmic cells with beats of two arbitrarily related lengths, see below.
(
SynthDef("snare", { arg out = 0, amp = 0.1, sinfreq = 180, att = 0.01, rel = 0.2, ffreq = 2000, pan = 0;
var snd1 = WhiteNoise.ar(amp);
var snd2 = SinOsc.ar(sinfreq, 0, amp);
var env = EnvGen.kr(Env.perc(att, rel), doneAction: 2);
var sum = HPF.ar(snd1, ffreq) + snd2 * env;
OffsetOut.ar(out, Pan2.ar(sum, pan));
}).add;
SynthDef("kick", { arg out = 0, amp = 0.3, sinfreq = 60, glissf = 0.9, att = 0.01, rel = 0.45, pan = 0;
var gliss = XLine.kr(sinfreq, sinfreq * glissf, rel);
var snd = SinOsc.ar(gliss);
var env = EnvGen.kr(Env.perc(att, rel), doneAction: 2);
snd = snd * env * amp;
OffsetOut.ar(out, Pan2.ar(snd, pan));
}).add;
)
// This Function divides a sum into two sections of beats of equal lengths
// beat numbers and ratio can be given and are understood depending on ratioType
(
~div_1 = { |sum = 1, beatNum_1 = 1, beatNum_2 = 1, ratio = 1, ratioType = \sum|
var sum_1, sum_2, beat_1, beat_2;
// we want:
// sum_1 = beatNum_1 * beat_1
// sum_2 = beatNum_2 * beat_2
// sum_1 + sum_2 = sum
(ratioType == \sum).if {
// convention #1: ratio = sum_1 / sum_2 -->
// sum_2 = sum_1 / ratio
// sum_1 + sum_2 = sum -->
// sum_1 * (1 / ratio + 1) = sum -->
sum_1 = sum / (1 / ratio + 1);
sum_2 = sum_1 / ratio;
beat_1 = sum_1 / beatNum_1;
beat_2 = sum_2 / beatNum_2;
}{
(ratioType != \beat).if { "assume ratioType \beat".warn };
// convention #2: ratio = beat_1 / beat_2 -->
// beat_2 = beat_1 / ratio
// (beat_1 * beatNum_1) + (beat_2 * beatNum_2) = sum -->
// (beat_1 * beatNum_1) + (beat_1 / ratio * beatNum_2) = sum -->
beat_1 = sum / (beatNum_1 + (beatNum_2 / ratio));
beat_2 = beat_1 / ratio;
};
(beat_1 ! beatNum_1 ++ (beat_2 ! beatNum_2)).flat
};
)
~div_1.(12, 4, 2, 1/2, \sum)
-> [ 1, 1, 1, 1, 4, 4 ]
~div_1.(12, 4, 2, 1/2, \beat)
-> [ 1.5, 1.5, 1.5, 1.5, 3, 3 ]
~div_1.(12, 4, 2, 2.sqrt, \beat)
-> -> [ 2.2163883751088, 2.2163883751088, 2.2163883751088, 2.2163883751088, 1.5672232497824, 1.5672232497824 ]
~div_1.(12, 4, 0, inf)
-> [ 3, 3, 3, 3 ]
(
// change of single parameters
~sum = 1;
~beatNum_1 = 3;
~beatNum_2 = 2;
~ratio = 1;
~ratioType = \sum;
~rhy = Pn(Plazy { Pseq(~div_1.(~sum, ~beatNum_1, ~beatNum_2, ~ratio, ~ratioType)) });
~kick = Pbind(
\instrument, \kick,
\sinfreq, [100, 50, 101],
\dur, 1/2,
\pan, [-0.5, 0, 0.5],
\amp, 0.1
);
~sn = Pbind(
\instrument, \snare,
\dur, ~rhy / 2,
\pan, Pwhite(-0.3, 0.3),
\amp, 0.1
);
x = Ppar([~kick, ~sn]).trace.play;
)
s.scope
// change on the fly (snare)
~ratio = 0.7;
~beatNum_1 = 10;
~sum = 3;
~ratioType = \beat;
~ratio = 0.2;
x.stop
(
// dynamic change of ratios with Pseg (different periods in kick and snare)
// only taken over for the next array with Plazy
~sum = 1;
~beatNum_1 = 2;
~beatNum_2 = 4;
~ratio_1 = 1;
~ratio_2 = 1;
~ratioType = \beat;
~rhy_kick = Pn(Plazy { Pseq(~div_1.(~sum, ~beatNum_1, ~beatNum_2, ~ratio_1, ~ratioType)) });
~rhy_snare = Pn(Plazy { Pseq(~div_1.(~sum, ~beatNum_2, ~beatNum_1, ~ratio_2, ~ratioType)) });
// variable sum
// ~rhy_kick = Pn(Plazy { Pseq(~div_1.(rrand(1, 1.5), ~beatNum_1, ~beatNum_2, ~ratio_1, ~ratioType)) });
// ~rhy_snare = Pn(Plazy { Pseq(~div_1.(rrand(1, 1.5), ~beatNum_2, ~beatNum_1, ~ratio_2, ~ratioType)) });
~kick = Pbind(
\instrument, \kick,
\do, Pseg(Pseq([1, 10], inf), Pseq([5, 5], inf), Pseq([3, -3], inf)).collect(~ratio_2 = _),
\sinfreq, [200, 50, 101],
\dur, ~rhy_kick,
\att, 0.01,
\rel, 0.1,
\pan, [-0.5, 0, 0.5],
\amp, 0.2
);
~sn = Pbind(
\instrument, \snare,
\do, Pseg(Pseq([10, 1], inf), Pseq([3, 3], inf), Pseq([3, -3], inf)).collect(~ratio_1 = _),
\dur, ~rhy_snare,
\pan, Pwhite(-0.3, 0.3),
\amp, 0.1
);
x = Ppar([~kick, ~sn]).trace.play;
)
~ratio_1 = 0.6;
~ratio_2 = 0.4;
~ratioType = \sum;
x.stop