When writing music functions in Lilypond, we often wish to apply
complicated functions to music expressions. We want to process all
notes, or just the rests, or every 5th breathing mark. Lilypond makes
this easy for us by providing the (partially documented) music-map
function. This function works just like pure Scheme equivalent and
applies proc to mus (music-map proc mus)
. There is even a
Lilypond wrapper to this Scheme function in \musicMap
. This is a very
useful function for writing iterative music functions.
There are some functions that are missing though: there is no
music-cons
, music-car
or music-cdr
, the barebones of working on
Lilypond music structures as lists like music-map
does. These
functions have probably not been written yet because the meaning of
cons
, car
and cdr
are a little fuzzy for music. We can think of
it well with sequential music, but how do we cons
onto simultaneous
music? I offer one (non tail–recursive) solution below.
music-car
finds the first music event that is not sequential or
relative octave music. On encountering simultaneous music, it returns
the first event in each simultaneous context.
music-car = #(define-music-function (mus) (ly:music?)
(let
((elem (ly:music-property mus 'element '()))
(elems (ly:music-property mus 'elements '()))
(sim (music-is-of-type? mus 'simultaneous-music))
(rel (music-is-of-type? mus 'relative-octave-music))
(seq (music-is-of-type? mus 'sequential-music)))
(cond
(rel (make-music 'RelativeOctaveMusic 'element (music-car elem)))
(sim (make-music 'SimultaneousMusic 'elements (map music-car elems)))
(seq (make-music 'SequentialMusic 'elements (list (music-car elems))))
(else mus))))
music-cdr
performs the opposite function, returning the rest of the
music after a music-car
.
music-cdr = #(define-music-function (mus) (ly:music?)
(let
((elem (ly:music-property mus 'element '()))
(elems (ly:music-property mus 'elements '()))
(sim (music-is-of-type? mus 'simultaneous-music))
(rel (music-is-of-type? mus 'relative-octave-music))
(seq (music-is-of-type? mus 'sequential-music)))
(cond
(rel (make-music 'RelativeOctaveMusic 'element (music-cdr elem)))
(sim (make-music 'SimultaneousMusic 'elements (map music-cdr elems)))
(seq (make-music 'SequentialMusic 'elements (cons (music-cdr (car elems)) (cdr elems))))
(else mus))))
music-cons
adds the music event given to the beginning of the music
and for simultaneous music adds the event only to the first voice.
music-cons = #(define-music-function (el mus) (ly:music? ly:music?)
(let*
((elem (ly:music-property mus 'element '()))
(elems (ly:music-property mus 'elements '()))
(sim (music-is-of-type? mus 'simultaneous-music))
(rel (music-is-of-type? mus 'relative-octave-music))
(seq (music-is-of-type? mus 'sequential-music))
(elseq (music-is-of-type? el 'sequential-music))
(elelems (ly:music-property el 'elements '()))
(rem (and seq (not (null? elems)) (or (music-is-of-type? (car elems) 'simultaneous-music)
(music-is-of-type? (car elems) 'relative-octave-music)
(music-is-of-type? (car elems) 'sequential-music)))))
(cond
(rem (make-music 'SequentialMusic 'elements (cons (music-cons el (car elems)) (cdr elems))))
(rel (make-music 'RelativeOctaveMusic 'element (music-cons el elem)))
(sim (make-music 'SimultaneousMusic 'elements (cons (music-cons el (car elems)) (cdr elems))))
(seq (make-music 'SequentialMusic 'elements (if elseq (append elelems elems) (cons el elems))))
(else (ly:error "Not a type of music to cons")))))