Jump to content

Center Lyric Syllables (ignoring punctuation)

From LilyPond wiki

When a lot of verses have the same text, but with difference punctuation, e.g. because some verses are in quotation marks, it looks awkward because the text doesn't line up vertically. Even when there is only a single verse, it can look awkward to have the punctuation be included in the width of the syllable when centering it beneath the note. Settings for 'self-alignment-X and lyricMelismaAlignment are respected.

It looks much nicer to center the text of the syllable and then put the punctuation around it.

\version "2.24.0"

%% http://lsr.di.unimi.it/LSR/Item?id=888

%LSR by Wolf Alight
%=>http://lists.gnu.org/archive/html/lilypond-user/2010-02/msg00444.html
%=>http://lists.gnu.org/archive/html/lilypond-user/2013-05/msg00800.html

%LSR modified by Alexander Kobel
%=>http://permalink.gmane.org/gmane.comp.gnu.lilypond.general/89675

%LSR modified by Thomas Morley
%=>http://lilypond.1069038.n5.nabble.com/LyricText-center-on-word-breaks-lyricMelismaAlignment-tt183456.html

%As of 2.25.10 (ly:grob-property-data lyric-text-grob 'self-alignment-X) may return a procedure.
% -Harm

%% Note: Only characters of the string used to define space-set
%% are recognized by 'center-on-word'
#(define space-set
  (list->char-set 
    (string->list "—.?-;,:“”‘’–— */()[]{}|<>!`~&…")))

#(define (width grob text)
  (let* ((X-extent 
           (ly:stencil-extent (grob-interpret-markup grob text) X)))
   (if (interval-empty? X-extent)
       0 
       (cdr X-extent))))

#(define (center-on-word grob)
  (let* ((text (ly:grob-property-data grob 'text))
         (syllable (markup->string text))
         (word-position
           (if (string-skip syllable space-set)
               (string-skip syllable space-set)
               0))
         (word-end
           (if (string-skip-right syllable space-set)
               (+ (string-skip-right syllable space-set) 1)
               (string-length syllable)))
         (preword (substring syllable 0 word-position))
         (word (substring syllable word-position word-end))
         (preword-width (width grob preword))
         (word-width (width grob (if (string-null? syllable) text word)))
         (note-column (ly:grob-parent grob X))
         (note-column-extent (ly:grob-extent note-column note-column X))
         (note-column-width (interval-length note-column-extent))
         (self-X (ly:grob-property-data grob 'self-alignment-X)))

  (-
    (*
      (/ (- note-column-width word-width) 2)
      (1+ (if (procedure? self-X) (self-X grob) self-X)))
    preword-width)))

%% For general use this take this layout-setting
%% In the example below the override is applied to selected Lyrics only
%%
%\layout {
%  \context {
%    \Lyrics
%    \override LyricText.X-offset = #center-on-word
%  }
%}

melody = \relative c' { 
  c4 
  d e f | 
  c d 
  e( f) 
  g1 \bar"|." 
}

lyr = \lyricmode {
  \set stanza = "1. "
  Do, ““Re, Mi, Fa, Do, Re,
  %\once \set Score.lyricMelismaAlignment = #4.5
  "...mimifa,"
  \markup \bold Sol—
}

lyrA = \lyricmode {
  \set stanza = "2. "
  “Do Re, Mi, Fa, Do, Re, 
  mimifa... 
  Sol!”
}

lyrControl = \lyricmode{ 
  \set stanza = "Control: "
  \revert LyricText.X-offset
  Do Re Mi Fa Do Re mimifa Sol
}

\score {
  \new Staff <<
    \new Voice = "voice" \melody
    \new Lyrics = "testI" 
      \with { \override LyricText.X-offset = #center-on-word }
      \lyricsto "voice" \lyr
    \new Lyrics = "testII" 
      \with { \override LyricText.X-offset = #center-on-word }
      \lyricsto "voice" \lyrA
    %% The control-voice omits the override
    \new Lyrics = "control" 
      \lyricsto "voice" \lyrControl
  >>
  \layout {
    \context {
      \Score
      lyricMelismaAlignment = #-0.6
    }
  }
}