from klotho import Tree, RhythmTree as RT, plot, play
from fractions import Fraction
import numpy as npRecall the five basic rhythmic types: Pulse, Periodic, Irregular, Accelerating, and Decelerating. Now that we understand how Rhythm Trees use nesting and hierarchy, we can combine these basic types at different levels of the tree:
Mixed Types¶
Irregular-Pulse¶
Unequal top-level groups, each subdivided into a steady pulse.
for i in range(4):
S = [1, 2, 3, 5, 7, 11, 13]
np.random.shuffle(S)
S = tuple((s, (1,)*np.random.randint(3, 8 if s > 2 else 4)) for s in S)
rt = RT(meas=2, subdivisions=S)
plot(rt, layout='containers').play(bpm=62, beat='1/4')
print()
print("...and so on...")
...and so on...
Pulse-Accel/Decel¶
Equal top-level groups, each containing an accelerating or decelerating subdivision.
S = tuple((1, tuple(range(1, 11)[::-1])) for _ in range(3))
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=92, beat='1/4')S = tuple((1, tuple(range(1, 11))) for _ in range(3))
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=92, beat='1/4')Irregular-Accel/Decel¶
Unequal top-level groups, each with an accelerating or decelerating interior.
S = tuple((np.random.randint(1, 5), tuple(range(1, np.random.randint(5, 12))[::-1])) for _ in range(4))
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=84, beat='1/4')S = tuple((np.random.randint(1, 5), tuple(range(1, np.random.randint(5, 12)))) for _ in range(4))
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=84, beat='1/4')Accel/Decel-Pulse¶
Top-level groups that accelerate or decelerate, each filled with an even pulse.
S = tuple((n, (1,)*5) for n in range(1, 8)[::-1])
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=84, beat='1/4')S = tuple((n, (1,)*5) for n in range(1, 8))
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=84, beat='1/4')Accel-Decel / Decel-Accel¶
Acceleration at one level, deceleration at the other — the two directional types working against each other.
S = tuple((n, tuple(range(1, 8))) for n in range(1, 8)[::-1])
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=72, beat='1/4')S = tuple((n, tuple(range(1, 8)[::-1])) for n in range(1, 8))
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=64, beat='1/4')Irregular-Cycling¶
Unequal top-level groups, each subdivided by a repeating rhythmic motif.
S = tuple((np.random.randint(1, 6), (1, 3) * 3) for _ in range(6))
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=54, beat='1/4')Accel/Decel-Cycling¶
Accelerating or decelerating top-level groups, each containing a cycling subdivision.
S = tuple((n, (1, 2, 3) * 2) for n in range(1, 7)[::-1])
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=54, beat='1/4')S = tuple((n, (1, 2, 3) * 2) for n in range(1, 9))
rt = RT(subdivisions=S)
plot(rt, layout='containers').play(bpm=36, beat='1/4')These combinations can nest further. There’s nothing stopping us from combining three (or more) types at successive levels of the tree:
Irregular-Accel-Pulse¶
Three levels deep: irregular grouping at the top, acceleration in the middle, pulse at the leaves.
S = tuple(
(np.random.randint(1, 5),
tuple((n, (1,)*4) for n in range(1, np.random.randint(4, 8))[::-1]))
for _ in range(3)
)
rt = RT(span=2, subdivisions=S)
plot(rt, layout='containers').play(bpm=72, beat='1/4')...and so on...
The point is that these basic rhythmic types — Pulse, Cycling, Irregular, and Accelerating/Decelerating — can be freely composed at any level of the tree. Each level of nesting adds a new dimension of rhythmic structure, and the combinatorial possibilities grow rapidly. We can, of course, also inject rests into any of these.
This is another benefit of working with rhythm in tree form: we can apply targeted design decisions at specific temporal strata. The top level might be irregular while the subdivisions within each group follow a steady pulse, or the macro pacing might accelerate while the inner detail cycles through a repeated motif. Each level of the tree is an independent design choice.
If we think about rhythm in terms of these simple basic types rather than getting lost in the specific numbers, we have a vocabulary for creating rhythms — a way to describe what we want before worrying about the exact values.