I like how the function unfold allows for rather concisely expressed transformations. I also like that with this function, methods can easily by commented out or added without changing the nesting structure of the overall Lisp program. However, what I do not like is that the unfold methods don't support any further arguments, and as a result the required preliminary work with def-unfold-set is rather cumbersome.
So, I rolled by own version of unfold that addresses this shortcoming. This new function works as follows: you can use the names of arbitrary Opusmodus functions, as long as they expect an OMN sequence as first argument. The following example applies first the function gen-retrograde and then quantum to the material mat.
(setf mat '((q c4 d4 e4) (h f4 q b3)))
(fn-unfold '((gen-retrograde) (quantum :fraction -0.2)) mat)
=> ((7W B3 7H._QT F4 -E..) (E E4 D4 S. C4 -ET))
If you would like to have functions with more concise names (as unfold does), just define functions with shorter names. Here are some examples.
(defun tr (sequence transpose &key (section NIL) (exclude NIL) (ambitus 'piano) (omn NIL))
"Like pitch-transpose, but sequence as first param."
(pitch-transpose transpose sequence :section section :exclude exclude :ambitus ambitus :omn omn))
(defun ld (sequence values &key set ignore seed (section NIL) (exclude NIL) (omn NIL))
"Like length-divide, but sequence as first param."
(length-divide values sequence :set set :ignore ignore :section section :exclude exclude :omn omn :seed seed))
With the definitions above, we can now use fn-unfold as follows for computing an octave transposition of mat and then applying length-divide to the second bar with the given settings.
(fn-unfold '((tr 12) (ld (2 3) :section 1)) mat)
=> ((Q C5 D5 E5) (3H E5 FS5 G5 3Q A4 BB4 CS5))
BTW: The definition of fn-unfold is pretty short. By far the longest part of this definition is the doc string 🙂
(defun fn-unfold (fns sequence) "Much like the buildin Opusmodus `unfold`, but instead works with functions and additional arguments can be given to the functions. Apply to `sequence` all fns in order. * Arguments: - fns (list of lists): Each sublist has the form (<omn-fn> &rest <args>), where <omn-fn> is a function expecting an OMN sequence as first argument and arbitrary further argments, and <args> are the further arguments beyond the OMN sequence given to the function. - sequence: OMN sequence * Examples: Some material to use ;;; (setf mat '((q c4 d4 e4) (h f4 q b3))) Remember: all functions used must expect a OMN sequence as *first* argument. ;;; (fn-unfold '((gen-retrograde) (quantum :fraction -0.2)) mat) Some short-hand versions of common functions are defined for conciseness. ;;; (fn-unfold '((tr 12) (ld (2 3) :section 1)) mat) " (reduce (lambda (seq fn) (apply (if (functionp (first fn)) (first fn) (fdefinition (first fn))) seq (rest fn))) fns :initial-value sequence))
here is a sketch for an alternative "binary-(or element-)layer-FUNCTION
(defun element-layer (lists &key (rnd nil)) (let ((lists (if (null rnd) lists (rnd-order lists :list t)))) (car (last (loop for x in (rest lists) with list = (car lists) collect (setf list (loop for i in list with cnt = 0 when (equal i 0) collect (nth cnt x) and do (incf cnt) else collect i))))))) (element-layer (list '(1 0 0 1 1 0 0 1 0 0 0 0) '(0 2 3 0 4 5 0 6 0 7 8 0) '(11 12 13 14 15 16 17)) :rnd nil) => (1 11 2 1 1 3 12 1 4 5 13 6) ;;; hierarchic: every 0's will be replaced by the values from the next/sub-list...