diff options
| -rw-r--r-- | src/unheard/cycles.clj | 26 | ||||
| -rw-r--r-- | test/unheard/cycles_test.clj | 28 |
2 files changed, 51 insertions, 3 deletions
diff --git a/src/unheard/cycles.clj b/src/unheard/cycles.clj index 513b090..5511d64 100644 --- a/src/unheard/cycles.clj +++ b/src/unheard/cycles.clj @@ -29,6 +29,20 @@ [& 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)))) @@ -49,6 +63,10 @@ (= :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] @@ -72,7 +90,13 @@ (let [children (:v node) n (count children) child-idx (mod iteration n)] - (unfold-node (nth children child-idx) start end (quot 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. diff --git a/test/unheard/cycles_test.clj b/test/unheard/cycles_test.clj index b912895..ef9aca7 100644 --- a/test/unheard/cycles_test.clj +++ b/test/unheard/cycles_test.clj @@ -1,6 +1,6 @@ (ns unheard.cycles-test (:require [clojure.test :refer [deftest is testing]] - [unheard.cycles :refer [l f unfold]])) + [unheard.cycles :refer [l f p unfold]])) (deftest unfold-tests (testing "single scalar" @@ -62,4 +62,28 @@ (testing "fork with nested list subdivides correctly" (is (= [[0 1/3 :a] [1/3 2/3 :b] [2/3 1 :c] [1 2 :x]] - (unfold 1 (f (l :a :b :c) :x)))))) + (unfold 1 (f (l :a :b :c) :x))))) + + (testing "simple parallel - all children at same time" + (is (= [[0 1 :a] [0 1 :b] [0 1 :c]] + (unfold 1 (p :a :b :c))))) + + (testing "parallel with list - children subdivide in parallel" + (is (= [[0 1/2 :a] [1/2 1 :b] + [0 1/2 :c] [1/2 1 :d]] + (unfold 1 (p (l :a :b) (l :c :d)))))) + + (testing "parallel with fork - forks extend together" + (is (= [[0 1 :a] [0 1 :c] + [1 2 :b] [1 2 :d]] + (unfold 1 (p (f :a :b) (f :c :d)))))) + + (testing "list containing parallel" + (is (= [[0 1/2 :x] [0 1/2 :y] + [1/2 1 :z]] + (unfold 1 (l (p :x :y) :z))))) + + (testing "parallel with different cycle lengths" + (is (= [[0 1 :a] [0 1 :b] + [1 2 :a] [1 2 :c]] + (unfold 1 (p :a (f :b :c))))))) |
