blob: 5511d64a91203dc917ba02adfe8388ca97e81de2 (
plain)
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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
|
(ns unheard.cycles)
(defn l
"List combinator: subdivides time evenly among children, advancing in lockstep.
Each child receives an equal portion of the parent's time duration.
All children advance synchronously through their patterns.
Equivalent to TidalCycles/Strudel 'fastcat' operator.
Example:
(l :a :b :c) with cycle-length 1
=> [[0 1/3 :a] [1/3 2/3 :b] [2/3 1 :c]]"
[& args]
{:v (vec args) :type :l})
(defn f
"Fork combinator: cycles through children sequentially across iterations.
Each iteration selects one child in round-robin fashion.
The selected child gets the full time duration for that iteration.
Forks extend the total pattern duration by (num-children × child-cycles).
Equivalent to TidalCycles/Strudel 'slowcat' operator.
Example:
(f :a :b :c) with cycle-length 1
=> [[0 1 :a] [1 2 :b] [2 3 :c]]"
[& args]
{:v (vec args) :type :f})
(defn p
"Parallel combinator: all children occur simultaneously.
Each child gets the full time duration of the parent.
All children are active at the same time, producing overlapping intervals.
Equivalent to TidalCycles/Strudel 'stack' operator.
Example:
(p :a :b :c) with cycle-length 1
=> [[0 1 :a] [0 1 :b] [0 1 :c]]"
[& args]
{:v (vec args) :type :p})
(defn scalar? [x]
(not (and (map? x) (:type x))))
(defn gcd [a b]
(if (zero? b) a (recur b (mod a b))))
(defn lcm [a b]
(/ (* a b) (gcd a b)))
(defn compute-cycle [node]
(cond
(scalar? node) 1
(= :f (:type node))
(let [children (:v node)
n (count children)]
(* n (reduce lcm 1 (map compute-cycle children))))
(= :l (:type node))
(let [children (:v node)]
(reduce lcm 1 (map compute-cycle children)))
(= :p (:type node))
(let [children (:v node)]
(reduce lcm 1 (map compute-cycle children)))))
(defn unfold-node [node start end iteration]
(let [duration (- end start)]
(cond
(scalar? node)
[[start end node]]
(= :l (:type node))
(let [children (:v node)
n (count children)
slice-size (/ duration n)]
(mapcat (fn [i child]
(let [child-start (+ start (* i slice-size))
child-end (+ start (* (inc i) slice-size))]
(unfold-node child child-start child-end iteration)))
(range n)
children))
(= :f (:type node))
(let [children (:v node)
n (count children)
child-idx (mod iteration n)]
(unfold-node (nth children child-idx) start end (quot iteration n)))
(= :p (:type node))
(let [children (:v node)]
(mapcat (fn [child]
(unfold-node child start end iteration))
children)))))
(defn unfold
"Unfolds a pattern tree into concrete time intervals.
Takes a cycle-length and a pattern node (scalar, list, or fork),
and returns a vector of [start end value] intervals representing
when each scalar value is active.
Args:
cycle-length - Duration of each iteration (can be any number)
node - Pattern tree built from scalars, (l ...), and (f ...)
Returns:
Vector of [start end value] tuples, where start and end are rational
numbers representing time positions.
The total duration of the result is (* cycle-length (compute-cycle node)).
Examples:
(unfold 1 :a)
=> [[0 1 :a]]
(unfold 1 (l :a :b))
=> [[0 1/2 :a] [1/2 1 :b]]
(unfold 1 (f :a :b))
=> [[0 1 :a] [1 2 :b]]"
[cycle-length node]
(let [cycle-count (compute-cycle node)]
(vec (mapcat (fn [i]
(unfold-node node
(* i cycle-length)
(* (inc i) cycle-length)
i))
(range cycle-count)))))
|