Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

LilyPond: Extracting pitch names from music

I use LilyPond to create practice scores and etudes. I've figured out how to allow note entry in Moveable Do solfege notation and have a template (see below) that supports displaying the solfege symbols as lyrics beneath the notes. At present, I have to manually extract the lyrics from the notation plus markup that generates the music. I've been able to partially automate this with some python and vim code (not shown here), but it is still somewhat unsatisfactory.

It seems to me that the best solution would be to use LilyPond's built-in Scheme interpreter to extract the pitch names while the file is being processed. I've made some attempts to use map with ly:note-pitchname, but so far no success. Probably because I know squat about Scheme, especially as used in LilyPond scripts.

% Moveable Do as lyrics example

% define some solfege pitchnames 
% (in practice, the full set goes into "english.ly")
pitchnames = #`(
    (do . ,(ly:make-pitch -1 0 NATURAL))
    (re . ,(ly:make-pitch -1 1 NATURAL))
    (mi . ,(ly:make-pitch -1 2 NATURAL))
    )

#(ly:parser-set-note-names parser pitchnames)

% compose as though in C major
mynotes =  \relative do' {\key do \major do2 re4( mi4) }

% transpose to desired key
melody = \transpose do mi  { \mynotes }

% I WANT TO AUTOMATICALLY CREATE THE
% THE PITCHNAMES IN THIS BLOCK
% FROM THE CONTENTS OF \mynotes
solfa = \lyricmode { 
    \set ignoreMelismata = ##t  % one syllable per note
    do re mi 
    \unset ignoreMelismata  % allow normal placement of other lyrics
    }

% Produce score with solfege names as lyrics
\score {
    <<
    \new Voice = "myVoice" { 
        \melody 
    }
    \new Lyrics \lyricsto "myVoice" \solfa
    >>
    }
\version "2.12.3"                 
like image 426
Mike Ellis Avatar asked Dec 07 '10 15:12

Mike Ellis


1 Answers

I received useful info from Valentin Villenave on the LilyPond Users Forum that led to the following workable solution:

LilyPond provides a NoteNames engraver that will automatically print pitch names, eg. "c d e" as lyrics beneath the notes, but there is a longstanding bug that causes NoteNames to revert to the Dutch pitch names. Valentin's workaround is to create an associative array and use it as a lookup in a lambda function that gets called as each pitch is about to be printed. By substituting entries from the array, the desired pitch names are printed.

To make the solution completely workable, I also had to add a second score block to separate the midi output generation from the score printing. This is needed to prevent the NoteNames engraver from producing midi output.

I've tested this solution with a much larger file using the complete set of chromatic solfege names. It works very well. The only remaining issue is that it would be nice to be able to adjust font properties on the NoteNames output to make the solfege distinct from normal lyrics. So far I haven't been able to make that happen.

% Moveable Do as lyrics example

% define solfege pitchnames
pitchnames = #`(
    (do . ,(ly:make-pitch -1 0 NATURAL))
    (re . ,(ly:make-pitch -1 1 NATURAL))
    (mi . ,(ly:make-pitch -1 2 NATURAL))
    )

#(ly:parser-set-note-names parser pitchnames)

% Apparently, LilyPond reverts to dutch names when
% using the NoteNames context. The following 
% workaround was posted by V. Villenave at
% http://lists.gnu.org/archive/html/lilypond-user/2010-10/msg00687.html

newnames =
#`(("c" . "do")
   ("d" . "re")
   ("e" . "mi"))

myNoteNames =
#(lambda (grob)
   (let* (
          ;; bindings
          (default-name (ly:grob-property grob 'text))
          (new-name (assoc-get default-name newnames))
         )  
          ;; body
         (ly:grob-set-property! grob 'text new-name)
         (ly:text-interface::print grob)
         )
   )

% compose as though in C major
mynotes =  \relative do' {\key do \major do2 re4( mi4) }

% transpose to desired key
melody = \transpose do mi  { \mynotes }

% Produce score with solfege names as lyrics
\score {
    <<
    \new Voice = "myVoice" { 
        \melody 
    }
    \context NoteNames \with {
       \override NoteName #'stencil = #myNoteNames
    } { \mynotes }

    >>
}
% Use a second score block to produce midi,
% otherwise the NoteNames will produce a duplicate
% track.
\score {
    \new Voice = "myVoice" { 
        \melody 
    } 
      %% This generates the midi file
    \midi {

    }        
}    
\version "2.12.3"  

UPDATE: It turns out that font properties can be controlled with the markup function, eg, by changing

(ly:grob-set-property! grob 'text new-name)

to

(ly:grob-set-property! grob 'text (markup #:italic #:smaller new-name))

There are, probably, other ways to accomplish the same thing but this is simple and does what I need. At this point, I consider this question answered. Note that a future version of LilyPond may fix the NoteNames bug and eliminate the need to use Scheme for this purpose.

like image 65
Mike Ellis Avatar answered Sep 28 '22 06:09

Mike Ellis