diff options
Diffstat (limited to 'src/unheard')
| -rw-r--r-- | src/unheard/cycles.clj | 38 |
1 files changed, 38 insertions, 0 deletions
diff --git a/src/unheard/cycles.clj b/src/unheard/cycles.clj index 29cad91..3cbfb97 100644 --- a/src/unheard/cycles.clj +++ b/src/unheard/cycles.clj @@ -92,6 +92,44 @@ (defn scalar? [x] (not (and (map? x) (:type x)))) +(defn paste + "Paste operator: replaces scalar values in a template pattern with provided values. + + Takes a template pattern and a sequence of replacement values. Each scalar + in the template (in depth-first order) is replaced by the corresponding value + from the replacement sequence. If a replacement value is nil, the original + scalar is preserved. + + This allows separating rhythmic structure from content. Any values can be used + as placeholders in the template (commonly keywords like :_ or numbers). + + Examples: + (paste (f :_ (f :_ :_) :_ :_) :c :e :g :b :d) + => (f :c (f :e :g) :b :d) + + (paste (f :_ (f :_ :_) :_ :_) :c :e nil :b :d) + => (f :c (f :e :_) :b :d) + + (paste (l 1 2 1) :a :b :c) + => (l :a :b :c)" + [template & values] + (let [values-seq (atom (seq values))] + (letfn [(replace-scalars [node] + (if (scalar? node) + (let [replacement (first @values-seq)] + (swap! values-seq rest) + (if (nil? replacement) + node + replacement)) + ;; Non-scalar: check if :v is a vector (l, f, p) or single value (rate, elongate, rep) + (let [v (:v node)] + (if (vector? v) + ;; Combinators with multiple children + (assoc node :v (mapv replace-scalars v)) + ;; Modifiers with single child + (assoc node :v (replace-scalars v))))))] + (replace-scalars template)))) + (defn get-weight "Returns the weight of a node. Elongated nodes have their specified weight, all other nodes have a default weight of 1." |
