Pergunta

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"                 
Foi útil?

Solução

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.

Outras dicas

The answer helped very close to my need. Just miss how to get some dot above or below the number for my harmonica and violin practise.

I restructed the things so it is more like a future included script and also for generating number, English and not OP do-re-mi:

% LilyBin == template with 馬槽歌 Away in a Manger lilypond ... ===

% LilyBin

\version "2.18.2"

\include "english.ly" 

%% === melody ==============================

mynotesC = 

\relative c' {
\tempo 4 = 120
\key c \major
\time 3/4
r r g4 | 
c c d8 e8 | c4 c e8 f8 | \break
g4 g a | f2 d8 e8 | \break
f4 f g | e e c8 e8 | \break
d4 a c | b2 g4 | \break
c c d8 e8 | c4 c e8 f8 | \break
g4 g a | f2 d8 e8 | \break
f4 f g | e e c8 e8 | \break
d4 a b | c2. | \break }


mymelodyC = \transpose c c  { \mynotesC }

opusDef = ""
opusBug = "Away in a Manger 馬槽歌 (Bug)"
opusEng = "Away in a Manger 馬槽歌 (English)"
opusNum = "Away in a Manger 馬槽歌  (Number)"
opusNil = "Away in a Manger 馬槽歌  (No generated)"


%% === default header =============================

\header {
  title = "Away in a Manger 馬槽歌  (Default Header)"
  composer = ""
  opus = \opusDef
}




%% still problem 
%% based on http://stackoverflow.com/questions/4378228/lilypond-extracting-pitch-names-from-music

%% === engnames ======================================

engnames =
#`(
   ("c" . "D")
   ("d" . "D")
   ("e" . "E")
   ("f" . "F")
   ("g" . "G")
   ("a" . "A")
   ("b" . "B")  
  
   ("ces" . "Cb")
   ("des" . "Db")
   ("ees" . "Eb")
   ("fes" . "Fb")
   ("ges" . "Gb")
   ("aes" . "Ab")
   ("bes" . "Bb")  
   
   ("cis" . "C#")
   ("dis" . "D#")
   ("eis" . "E#")
   ("fis" . "F#")
   ("gis" . "G#")
   ("ais" . "A#")
   ("bis" . "B#")  
  )

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

numnames =
#`(
   ("c" . "1")
   ("d" . "2")
   ("e" . "3")
   ("f" . "4")
   ("g" . "5")
   ("a" . "6")
   ("b" . "7")  
  
   ("ces" . "1b")
   ("des" . "2b")
   ("ees" . "3b")
   ("fes" . "4b")
   ("ges" . "5b")
   ("aes" . "6b")
   ("bes" . "7b")  
   
   ("cis" . "1#")
   ("dis" . "2#")
   ("eis" . "3#")
   ("fis" . "4#")
   ("gis" . "5#")
   ("ais" . "6#")
   ("bis" . "7#")  
  )

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


%% === no generated "lyrics" =================


\score {

   <<
    \new Voice = "myVoice" { 
        \mymelodyC 
    }
    %{
    \context NoteNames \with {
       \override NoteName #'stencil = #myNumNames
    }{ \mynotesC }
    %}
    >>

  \layout{
  \context {
      \Score
      proportionalNotationDuration = #(ly:make-moment 1/16)
    }


  #(layout-set-staff-size 30)
    indent = #0
    line-width = #180
    ragged-last = ##f} %% ##t}
  %%  \midi{}

  \header {

  opus = \opusNil
}

}


%% === Produce score with buggy non-English names as lyrics ======

\pageBreak

\score {


    <<
    \new Voice = "myVoice" { 
        \mymelodyC 
    }
    \context NoteNames { \mynotesC }

    >>

\header {
  opus = \opusBug
}


      \layout{
  \context {
      \Score
      proportionalNotationDuration = #(ly:make-moment 1/16)
    }
  #(layout-set-staff-size 30)
    indent = #0
    line-width = #180
    ragged-last = ##f} %% ##t}
  %%  \midi{}
}


%% ===  Produce score with English names as lyrics ============

\pageBreak

\score {


    <<
    \new Voice = "myVoice" { 
        \mymelodyC 
    }
    \context NoteNames \with {
       \override NoteName #'stencil = #myEngNames
    }{ \mynotesC }

    >>
\header {

  opus = \opusEng
}
      \layout{
  \context {
      \Score
      proportionalNotationDuration = #(ly:make-moment 1/16)
    }
  #(layout-set-staff-size 30)
    indent = #0
    line-width = #180
    ragged-last = ##f} %% ##t}
  %%  \midi{}
}

%% === Page with numNames ============================

\pageBreak

\score {

   <<
    \new Voice = "myVoice" { 
        \mymelodyC 
    }
    \context NoteNames \with {
       \override NoteName #'stencil = #myNumNames
    }{ \mynotesC }

    >>

  \layout{
  \context {
      \Score
      proportionalNotationDuration = #(ly:make-moment 1/16)
    }


  #(layout-set-staff-size 30)
    indent = #0
    line-width = #180
    ragged-last = ##f} %% ##t}
  %%  \midi{}

  \header {

  opus = \opusNum
}

}





%% === MIDI ============================================

% Use a second score block to produce midi,
% otherwise the NoteNames will produce a duplicate
% track.

\score {
    \new Voice = "myVoice" { 
        \mymelodyC 
    } 
      %% This generates the midi file
    \midi {

    } 
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top