Stephane Boussuge wrote on 23 October
> your edit-omn function is definitively super-mega useful !!
I updated my function edit-omn. Remember that this function is for turning relatively simple functions processing some OMN parameter sequence (e.g., pitch or length lists) quasi automatically into much more expressive functions processing arbitrary OMN expressions, including nested expressions, and automatically supporting typical Opusmodus arguments like section and flat.
I now added support for creating functions with 'dynamic' arguments, i.e. arguments where different values are used for processing different sublists of OMN expressions. For example, again consider a generalisation of the builtin Opusmodus function gen-rotate,which represents a very useful compositional concept, but its current implementation is rather restricted. I already showed how the new and generalised function rotate-omn expands the existing gen-rotate by introducing arguments like flat and section as shown below.
(setf melody '((-h e c4 e4) (q. f4 e g4 q a4) (q g4 f4 e e4 d4)))
;; Rotate default parameter pitch of 2nd bar (section 1) to the right.
;; The link above shows how to alternatively rotate length values etc.
(rotate-omn 1 melody :section '(1) :flat nil)
; => ((-h e c4 e4) (q. a4 e f4 q g4) (q g4 f4 e e4 d4))
Using a new version of rotate-omn defined with the revised edit-omn we can now also specify different rotation amounts for different bars. The first argument of the function is given a list of rotation amounts (0 1 2), so the pitches of the first bar are not rotated at all, the pitches of the first bar are rotated 1 step to the right, and the pitches of the second bar 2 steps.
(rotate-omn '(0 1 2) melody :flat nil)
=> ((-h e c4 e4) (q. a4 e f4 q g4) (q e4 d4 e g4 f4))
Such 'dynamic' function arguments can be combined with the section argument to limit the processing of the input music to only certain bars. The following call rotates the bars 2 and 3 only (section is (1 2)), the first bar of this selection by 2 steps to the right, and the next by 1 step.
(rotate-omn '(2 1) melody :section '(1 2) :flat nil)
; => ((-h e c4 e4) (q. g4 e a4 q f4) (q d4 g4 e f4 e4))
Now, remember that this post is actually about simplifying the definition of functions like rotate-omn. The definition of this function is pretty short, as the actual rotation is already implemented by gen-rotate, and all support of arguments like section, flat and now also 'dynamic' arguments is automatically implemented by edit-omn. For getting dynamic arguments, all we need to do is the following. First, we test whether the argument we want to turn into a 'dynamic' argument is a list. If not, we want to use it as a static value. The result of that test (using listp) is bound to the local variable n-list-arg?, because we need to use this value at two places. The argument additional-args on the last line gets our argument n if it is a list.
We tell edit-omn how to actually transform the input music (e.g., sublists) by handing it a function -- that is another program that does the actual transformation. In the case below this function is an anonymous function (it has no name). Such functions are defined with lambda, which is pretty much like defun but without expecting a function name. Our anonymous function basically just calls gen-rotate, but if n is a list, then we first need to extract the OMN data to rotate and the amount by which to rotate it from the single argument xs of our anonymous function.
(defun rotate-omn (n sequence &key (parameter :pitch) (flat T) (section nil))
(let ((n-list-arg? (listp n)))
(edit-omn parameter sequence
(gen-rotate (second xs) (first xs))
(gen-rotate n xs)))
:additional-args (when n-list-arg? n))))
With this approach you can write relatively simply functions for processing individual parameters, and then turn that relatively simple function into a much more fancy function processing arbitrary OMN expressions, including nested expressions, and automatically supporting typical Opusmodus arguments like section and flat, and now also with dynamic arguments.
As mentioned in other posts, the function edit-omn can be found as part of my tot library at GitHub (and yes, that library has a buch of dependencies, and is therefore a bit tricky to install the first time; updates are a bit more easy). I also cleaned up the definition of edit-omn and fixed some bug that way.