contents
top
The elementary pattern
Accessing pitches from a sequence
Generating a music expression
An elementary music function
The prelude music function
Splitting a music sequence
Generating the three voices
The completed example

home
contact

Using Scheme in LilyPond: Prelude example

We show in this detailed example how to define a music function in Scheme that can generate the music of J.S. Bach's first prelude from Das Wohltemperierte Clavier.

The elementary pattern

In a first step, we define a function that reads five notes, eg:
{ c' e'  g' c'' e'' }

[image of music]

and generate three simultaneous music sequences:
<< { r8     g'16[ c''] e''[ g' c'' e''] }
   { r16 e'8. ~ e'4 }
   { c'2 } >>

[image of music]

Accessing pitches from a sequence

First, we have to extract what is important in the input sequence: the five pitches. In order to find out where the pitches are placed, we display the music expression:
guile> (display-scheme-music #{ \notemode { c' e'  g' c'' e'' } #})
(make-music 'SequentialMusic
  'elements (list 
             (make-music 'SequentialMusic
               'elements (list 
                          (make-music 'EventChord
                            'elements (list 
                                       (make-music 'NoteEvent
                                         'duration (ly:make-duration 2 0 1 1)
                                         'pitch (ly:make-pitch 0 0 0))))
                          (make-music 'EventChord
                            'elements (list 
                                       (make-music 'NoteEvent
                                         'duration (ly:make-duration 2 0 1 1)
                                         'pitch (ly:make-pitch 0 2 0))))
                          (make-music 'EventChord
                            'elements (list 
                                       (make-music 'NoteEvent
                                         'duration (ly:make-duration 2 0 1 1)
                                         'pitch (ly:make-pitch 0 4 0))))
                          (make-music 'EventChord
                            'elements (list 
                                       (make-music 'NoteEvent
                                         'duration (ly:make-duration 2 0 1 1)
                                         'pitch (ly:make-pitch 1 0 0))))
                          (make-music 'EventChord
                            'elements (list 
                                       (make-music 'NoteEvent
                                         'duration (ly:make-duration 2 0 1 1)
                                         'pitch (ly:make-pitch 1 2 0))))))))
So, the path to follow, starting from the { c' e' g' c'' e'' } sequence, is: the sequence -> "elements" property -> item #i (an EventChord) -> "elements" property -> item #0 (a NoteEvent) -> "pitch" property. Using regular calls to (ly:music-property ...), this is a very long expression, that's why we introduce a property accessor for music expressions:
#(define-macro (-> . expr)
   (define (gen-music-prop-expr music prop . rest)
     (let ((result-expr (if (number? prop)
                            `(list-ref ,music ,prop)
                            `(ly:music-property ,music ',prop))))
       (if (null? rest)
           result-expr
           (apply gen-music-prop-expr result-expr rest))))
   (apply gen-music-prop-expr expr))
Now, we can try it:
guile> (-> #{ \notemode { c' e'  g' c'' e'' } #} elements 0 elements 0 elements 0 pitch)
#<Pitch c' >
guile> (macroexpand-1 '(-> #{ \notemode { c' e'  g' c'' e'' } #} elements 0 elements 0 elements 0 pitch))
(ly:music-property
 (list-ref
  (ly:music-property
   (list-ref
    (ly:music-property
     (list-ref
      (ly:music-property
       #{ \notemode { c' e'  g' c'' e'' } #} 
       'elements) 
      0)
     'elements)
    0)
   'elements)
  0)
 'pitch)

Generating a music expression

Now that we know how to access a property located inside a music expression, we want to generate a more complex pattern from the five note sequence. We use display-scheme-music in order to see how such an expression can be written in raw scheme:
guile> (display-scheme-music #{ \notemode << { r8     g'16[ c''] e''[ g' c'' e''] }
                      { r16 e'8. ~ e'4 }
                      { c'2 } >> #})
(make-music 'SequentialMusic
  'elements (list 
             (make-music 'SimultaneousMusic
               'elements (list 
                          (make-music 'SequentialMusic
                            'elements (list 
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'RestEvent
                                                      'duration (ly:make-duration 3 0 1 1))))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch (ly:make-pitch 0 4 0))
                                                    (make-music 'BeamEvent
                                                      'span-direction -1)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch (ly:make-pitch 1 0 0))
                                                    (make-music 'BeamEvent
                                                      'span-direction 1)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch (ly:make-pitch 1 2 0))
                                                    (make-music 'BeamEvent
                                                      'span-direction -1)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch (ly:make-pitch 0 4 0))))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch (ly:make-pitch 1 0 0))))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch (ly:make-pitch 1 2 0))
                                                    (make-music 'BeamEvent
                                                      'span-direction 1)))))
                          (make-music 'SequentialMusic
                            'elements (list 
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'RestEvent
                                                      'duration (ly:make-duration 4 0 1 1))))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 3 1 1 1)
                                                      'pitch (ly:make-pitch 0 2 0))
                                                    (make-music 'TieEvent)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 2 0 1 1)
                                                      'pitch (ly:make-pitch 0 2 0))))))
                          (make-music 'SequentialMusic
                            'elements (list 
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 1 0 1 1)
                                                      'pitch (ly:make-pitch 0 0 0))))))))))
We now have enough material to write a function that takes a five note sequence as an argument, and return three simultaneous sequences, containing the notes of the three voices.
#(define (prelude-pattern five-note-sequence)
  "Build half a prelude measure, from `five-note-sequence', which
should be a five note SequentialMusic object.
The result is a SimultaneousMusic, containing three SequencialMusic 
objects."
  (let ((pitch1 (-> five-note-sequence elements 0 elements 0 pitch))
        (pitch2 (-> five-note-sequence elements 1 elements 0 pitch))
        (pitch3 (-> five-note-sequence elements 2 elements 0 pitch))
        (pitch4 (-> five-note-sequence elements 3 elements 0 pitch))
        (pitch5 (-> five-note-sequence elements 4 elements 0 pitch)))
    (make-music 'SimultaneousMusic
               'elements (list 
                          (make-music 'SequentialMusic
                            'elements (list 
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'RestEvent
                                                      'duration (ly:make-duration 3 0 1 1))))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch3) ;;; <-- pitch3
                                                    (make-music 'BeamEvent
                                                      'span-direction -1)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch4) ;;; <-- pitch4
                                                    (make-music 'BeamEvent
                                                      'span-direction 1)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch5) ;;; <-- pitch5
                                                    (make-music 'BeamEvent
                                                      'span-direction -1)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch3))) ;;; <-- pitch3
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch4))) ;;; <-- pitch4
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch5) ;;; <-- pitch5
                                                    (make-music 'BeamEvent
                                                      'span-direction 1)))))
                          (make-music 'SequentialMusic
                            'elements (list 
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'RestEvent
                                                      'duration (ly:make-duration 4 0 1 1))))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 3 1 1 1)
                                                      'pitch pitch2) ;;; <-- pitch2
                                                    (make-music 'TieEvent)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 2 0 1 1)
                                                      'pitch pitch2))))) ;;; <-- pitch2
                          (make-music 'SequentialMusic
                            'elements (list 
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 1 0 1 1)
                                                      'pitch pitch1))))))))) %;;; <-- pitch1

An elementary music function

We define a preludeHalfMeasure music function in order to test this elementary pattern generator:
preludeHalfMeasure = #(def-music-function (parser location music) (ly:music?)
                        (prelude-pattern music))

{ \preludeHalfMeasure { c' e'  g' c'' e'' } }

[image of music]

The prelude music function

Calling preludeHalfMeasure for every group of five notes would be tedious, so we will now write a music function that will take a larger sequence of notes, split it, call prelude-pattern on individual groups and finally build the three voices.

Splitting a music sequence

First, we define a function that takes a sequences of notes, and split it in several sequences containing five notes.
#(define (split-music-sequence music-seq n)
  "Return a list of music sequences, which elements are taken from
`music-seq', grouped by n elements."
  (define (split seq acc)
    (let ((rest (list-tail seq n)))
      (if (pair? rest)
          (split rest (cons (list-head seq n) acc))
          (reverse! (cons seq acc)))))
  (let ((elements (-> music-seq elements)))
    (if (null? elements)
        (list)
        (map-in-order (lambda (seq) (make-music 'SequentialMusic 'elements seq))
                      (split elements (list))))))
A simple test:
splitMusic = #(def-music-function (paper location n music) (number? ly:music?)
               (make-music 'SimultaneousMusic 
                           'elements (split-music-sequence music n)))

\splitMusic #5 { 
  c'   e'  g' c'' e''
  c'   d'  a' d'' f''
  b    d'  g' d'' f''
  c'   e'  g' c'' e''
}

[image of music]

Generating the three voices

Finally, we can define the prelude music function.
prelude = #(def-music-function (parser location music) (ly:music?)
            (let ((notes-up (list))
                  (notes-middle (list))
                  (notes-down (list)))
             (for-each (lambda (five-note-seq)
                        (let ((half-measure (prelude-pattern five-note-seq)))
                         (set! notes-up (cons (-> half-measure elements 0)
                                         (cons (-> half-measure elements 0) notes-up)))
                         (set! notes-middle (cons (-> half-measure elements 1)
                                             (cons (-> half-measure elements 1) notes-middle)))
                         (set! notes-down (cons (-> half-measure elements 2)
                                           (cons (-> half-measure elements 2) notes-down)))))
                       (split-music-sequence music 5))
             #{
               \context PianoStaff <<
                 \context Staff = "up" <<
                   \context Voice = "high" {
                     \clef treble
                     $(make-music 'SequentialMusic 'elements (reverse notes-up))
                   }
                   \context Voice = "middle" {
                     \clef treble
                     $(make-music 'SequentialMusic 'elements (reverse notes-middle))
                   }
                 >>
                 \context Staff = "down" <<
                   \context Voice = "low" {
                     \clef bass
                     $(make-music 'SequentialMusic 'elements (reverse notes-down))
                   }
                 >>
               >> #})) 
First, the measures of the three voices are stacked into three variables, notes-up, notes-middle and notes-down.Then, they are placed in a PianoStaff template.

The completed example

The complete score can now be written:
\version "2.5.18"

\header{
    title = "Preludium"
    opus = "BWV 846"
    composer = "Johann Sebastian Bach (1685-1750)"
}
\paper {
  indent = 5\mm
  betweensystemspace = 12\mm
}
#(set-global-staff-size 13)

#(define-macro (-> . expr)
   (define (gen-music-prop-expr music prop . rest)
     (let ((result-expr (if (number? prop)
                            `(list-ref ,music ,prop)
                            `(ly:music-property ,music ',prop))))
       (if (null? rest)
           result-expr
           (apply gen-music-prop-expr result-expr rest))))
   (apply gen-music-prop-expr expr))

#(define (prelude-pattern five-note-sequence)
  "Build half a prelude measure, from `five-note-sequence', which
should ne a five note SequentialMusic object.
The result is a SimultaneousMusic, containing three SequencialMusic 
objects."
  (let ((pitch1 (-> five-note-sequence elements 0 elements 0 pitch))
        (pitch2 (-> five-note-sequence elements 1 elements 0 pitch))
        (pitch3 (-> five-note-sequence elements 2 elements 0 pitch))
        (pitch4 (-> five-note-sequence elements 3 elements 0 pitch))
        (pitch5 (-> five-note-sequence elements 4 elements 0 pitch)))
    (make-music 'SimultaneousMusic
               'elements (list 
                          (make-music 'SequentialMusic
                            'elements (list 
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'RestEvent
                                                      'duration (ly:make-duration 3 0 1 1))))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch3) ;;; <-- pitch3
                                                    (make-music 'BeamEvent
                                                      'span-direction -1)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch4) ;;; <-- pitch4
                                                    (make-music 'BeamEvent
                                                      'span-direction 1)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch5) ;;; <-- pitch5
                                                    (make-music 'BeamEvent
                                                      'span-direction -1)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch3))) ;;; <-- pitch3
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch4))) ;;; <-- pitch4
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 4 0 1 1)
                                                      'pitch pitch5) ;;; <-- pitch5
                                                    (make-music 'BeamEvent
                                                      'span-direction 1)))))
                          (make-music 'SequentialMusic
                            'elements (list 
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'RestEvent
                                                      'duration (ly:make-duration 4 0 1 1))))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 3 1 1 1)
                                                      'pitch pitch2) ;;; <-- pitch2
                                                    (make-music 'TieEvent)))
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 2 0 1 1)
                                                      'pitch pitch2))))) ;;; <-- pitch2
                          (make-music 'SequentialMusic
                            'elements (list 
                                       (make-music 'EventChord
                                         'elements (list 
                                                    (make-music 'NoteEvent
                                                      'duration (ly:make-duration 1 0 1 1)
                                                      'pitch pitch1))))))))) %;;; <-- pitch1

#(define (split-music-sequence music-seq n)
  "Return a list of music sequences, which elements are taken from
 `music-seq', grouped by n elements."
  (define (split seq acc)
    (let ((rest (list-tail seq n)))
      (if (pair? rest)
          (split rest (cons (list-head seq n) acc))
          (reverse! (cons seq acc)))))
  (let ((elements (-> music-seq elements)))
    (if (null? elements)
        (list)
        (map-in-order (lambda (seq) (make-music 'SequentialMusic 'elements seq))
                      (split elements (list))))))

prelude = #(def-music-function (parser location music) (ly:music?)
             (let ((notes-up (list))
                   (notes-middle (list))
                   (notes-down (list)))
               (for-each (lambda (five-note-seq)
                           (let ((half-measure (prelude-pattern five-note-seq)))
                             (set! notes-up (cons (-> half-measure elements 0)
                                                  (cons (-> half-measure elements 0) notes-up)))
                             (set! notes-middle (cons (-> half-measure elements 1)
                                                      (cons (-> half-measure elements 1) notes-middle)))
                             (set! notes-down (cons (-> half-measure elements 2)
                                                    (cons (-> half-measure elements 2) notes-down)))))
                         (split-music-sequence music 5))
             #{
               \context PianoStaff <<
                 \context Staff = "up" <<
                   \context Voice = "high" {
                     \clef treble
                     $(make-music 'SequentialMusic 'elements (reverse notes-up))
                   }
                   \context Voice = "middle" {
                     \clef treble
                     $(make-music 'SequentialMusic 'elements (reverse notes-middle))
                   }
                 >>
                 \context Staff = "down" <<
                   \context Voice = "low" {
                     \clef bass
                     $(make-music 'SequentialMusic 'elements (reverse notes-down))
                   }
                 >>
               >> #}))

\score { 
  << 
    {
      %% The notes : regular mottos + ending
      \prelude {
        c'   e'  g' c'' e''
        c'   d'  a' d'' f''
        b    d'  g' d'' f''
        c'   e'  g' c'' e''
        c'   e'  a' e'' a''
        c'   d'  fis' a' d''
        b    d'  g' d'' g''
        b    c'  e' g' c''
        a    c'  e' g' c''
        d    a   d' fis' c''
        g    b   d' g' b'   
        g    bes e' g' cis''
        f    a   d' a' d''  
        f    aes d' f' b'   
        e    g   c' g' c''  
        e    f   a c' f'  
        d    f   a c' f'  
        g,   d   g b f' 
        c    e   g c' e'  
        c    g   bes c' e'
        f,   f   a c' e'  
        fis, c   a c' ees'
        aes, f   b c' d'  
        g,   f   g b d' 
        g,   e   g c' e'  
        g,   d   g c' f'  
        g,   d   g b f' 
        g,   ees a c' fis'
        g,   e!  g c' g'  
        g,   d   g c' f'  
        g,   d   g b f' 
        c,   c   g bes e'
      }
      <<
        \context Staff = "up" {
          r8 f16 a c' f' c' a c' a f a f d f d
          r8 g'16 b' d'' f'' d'' b' d'' b' g' b' d' f' e' d'
          <e' g' c''>1 \bar "|."
        }
        \context Staff = "down" {
          << { r16 c8. ~ c4 ~ c2 r16 b,8. ~ b,4 ~ b,2 c1 }
             \\ { c,2 c, c, c, c,1 } >>
        }
      >>
    }
    %% manual staff switches
    \context Voice = "high" {
      \voiceOne s1*9 \oneVoice
    }
    \context Voice = "middle" {
      \voiceTwo s1*9 \change Staff = "down" \voiceOne
    }
    \context Voice = "low" {
      \oneVoice s1*9 \voiceTwo
    }
  >>
  \midi { \tempo 4 = 80 }
  \layout { }
}

[image of music]


Last modified: Sat Apr 2 23:11:03 CEST 2005