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))
One interesting thing that could be implemented as a function could be a form of generating Negative Harmony.
In the video below, there are some explanation of what it is and the origin in the Levy book.
It was a trendy topic due to the Jacob Collier interview. And there are a lot of fun videos making versions of pop tunes using negative harmony.
The way I understand it, it is simply a kind of mapping notes in relation to an axis, like in the figure below.
So we need a function that could map a note in any register to another note in the closest register to the first on.
So, any C note will be mapped to G, all Db to F#, all D to F, all, Eb to E, all B to Ab, all Bb to A.
It´s also possible to generate other mappings as well.
I think that replace map or substitute map can do the job, but I´m not sure (I will try), but I find interesting to post it here to explore the idea.
All the best,
It´s kind of funny to sse in this por versions how every is upside down and how you can generate an entirely new song from exactly the same material.
POP TUNES with negative harmony:
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...