22 Nov 2012 crhodes   » (Master)

A long, long time ago (but in this galaxy), I used lilypond. Back in the days when I did relatively frequent consort singing of secular music from the Renaissance, a number of factors (the relative scarcity and cost of professional editions; the ambition to perform somewhat obscure works; and the need to make typesetting music indistinguishable from five-dimensional General Relativity, at least to a casual eye) meant that lilypond was a natural fit. It was far from perfect, and its difficult workflow informed some later work, but I used it and it was good.

Time passes... and now I have a four-year-old daughter who is interested in making music, or at least in banging notes on the piano. There are probably many kinds of teaching materials available; the one that we picked up (Dogs and Birds, somewhat based on Kodály's teaching methods), has pictures of animals inside the noteheads, to help build associations between the notation and the keys. And it was good.

But, of course, the child is not satisfied with the somewhat artificial ‘melodies’ in the teaching book; she wants as well to play tunes that she knows, for example ones that they are learning at school for the upcoming extravaganza that is the Christmas Performance. Fair enough; but she is still very attached to the animal noteheads, so what to do?

Some time later, after a bit of work with image manipulation tools (GIMP and inkscape, I thank you), I have 14 Encapsulated Postscript files, one white and one black for each notename, which are tolerably close to the ones she knows. After that, it is a simple matter to convince lilypond to use those noteheads. “How simple?” I hear you cry... approximately this simple:


#(set-global-staff-size 36)

#(define black-mapping
   (list
    (cons (ly:make-pitch 0 0 NATURAL) "c-black.eps")
    (cons (ly:make-pitch 0 1 NATURAL) "d-black.eps")
    (cons (ly:make-pitch 0 2 NATURAL) "e-black.eps")
    (cons (ly:make-pitch 0 3 NATURAL) "f-black.eps")
    (cons (ly:make-pitch 0 4 NATURAL) "g-black.eps")
    (cons (ly:make-pitch 0 5 NATURAL) "a-black.eps")
    (cons (ly:make-pitch 0 6 NATURAL) "b-black.eps")))

#(define white-mapping
   (list
    (cons (ly:make-pitch 0 0 NATURAL) "c-white.eps")
    (cons (ly:make-pitch 0 1 NATURAL) "d-white.eps")
    (cons (ly:make-pitch 0 2 NATURAL) "e-white.eps")
    (cons (ly:make-pitch 0 3 NATURAL) "f-white.eps")
    (cons (ly:make-pitch 0 4 NATURAL) "g-white.eps")
    (cons (ly:make-pitch 0 5 NATURAL) "a-white.eps")
    (cons (ly:make-pitch 0 6 NATURAL) "b-white.eps")))

#(define (notename-equals? p1 p2)
   (= (ly:pitch-notename p1) (ly:pitch-notename p2)))

#(define (notehead-text grob)
   (let* ((pitch (ly:event-property (event-cause grob) 'pitch))
          (duration (ly:event-property (event-cause grob) 'duration))
          (mapping (if (ly:duration duration (ly:make-duration 1 0))
                       black-mapping white-mapping))
          (epsname (cdr (assoc pitch mapping notename-equals?))))
     (markup #:general-align Y CENTER (#:epsfile X 2 epsname))))

notepics = {
  \override NoteHead #'stencil = #ly:text-interface::print
  \override NoteHead #'text = #notehead-text
}

{ \notepics e'4 e' e'2 | e'4 e' e'2 | e'4 g' c' d' | e'1 | 
  f'4 f' f' f' | f' e' e' e' | e' d' d' e' | d'2 g' |
  e'4 e' e'2 | e'4 e' e'2 | e'4 g' c' d' | e'1 |
  f'4 f' f' f' | f' e' e' e' | g' g' f' d' | c'1 }

{ \notepics \relative c' { 
  g'4 g g d | e e d2 | b'4 b a a | g2. d4 | 
  g g g d | e e d2 | b'4 b a a | g2. d4 | 
  g g g d | g g g2 | g4 g g g | g g g g |
  g g g d | e e d2 | b'4 b a a | g1 | } }

Latest blog entries     Older blog entries

New Advogato Features

New HTML Parser: The long-awaited libxml2 based HTML parser code is live. It needs further work but already handles most markup better than the original parser.

Keep up with the latest Advogato features by reading the Advogato status blog.

If you're a C programmer with some spare time, take a look at the mod_virgule project page and help us with one of the tasks on the ToDo list!