Scheme music helper functions

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")))))