Aligning lyrics based on vowels
This snippet shows how, in music with lyrics, you can align lyrics based on where vowels fall within LyricText items.
In each measure of this snippet, the first note demonstrates the use of these functions, and the second note shows LilyPond’s default alignment.
Source: Solution by Kieren MacMillan posted to the mailing list in this thread. The code for this solution was inspired by Center lyric syllables (ignoring punctuation).
\version "2.26"
% View this snippet online:
% https://wiki.lilypond.community/wiki/Aligning_lyrics_based_on_vowels
% Define the set of characters that will be
% classified as vowels for these commands:
#(define vowel-set
(list->char-set
(string->list "AÁÀÃÂÆǼEÉÈÊIÍÌÎOÓÒÕÔŒU
ÚÙÛYaáàãâæǽeéèêiíìîoóòõôœuúùûy")))
% Tweak this set based on your needs.
% For example, if your lyrics are in a language
% in which ⟨y⟩ is *always* pronounced as a vowel,
% you will want to make sure that Y and y are in your list.
% This function returns the width of the
% grob (stencil) for a given piece of text.
#(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))))
% This callback sets the alignment point of a LyricText grob
% to the centre of the first vowel that it contains.
#(define (center-on-first-vowel grob)
(let* ((text (ly:grob-property-data grob 'text))
(syllable (markup->string text))
(vowel-position
(if (string-index syllable vowel-set)
(string-index syllable vowel-set)
0))
(vowel-end
(if (string-index syllable vowel-set)
(+ (string-index syllable vowel-set) 1)
(string-length syllable)))
(preword (substring syllable 0 vowel-position))
(word (substring syllable vowel-position vowel-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)))
% This callback sets the alignment point of a LyricText grob
% to the point midway between its first and last vowels.
#(define (center-between-vowels grob)
(let* ((text (ly:grob-property-data grob 'text))
(syllable (markup->string text))
(word-position
(if (string-index syllable vowel-set)
(string-index syllable vowel-set)
0))
(word-end
(if (string-index-right syllable vowel-set)
(+ (string-index-right syllable vowel-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)))
% Helper functions that use the above code:
cvs = \once \override LyricText.X-offset = #center-between-vowels
cfv = \once \override LyricText.X-offset = #center-on-first-vowel
\fixed c' {
\*8 { f2 f }
% The command \* requires
% version 2.25 or later.
% For version 2.24, use
% \repeat unfold 8
}
\addlyrics {
\cfv piste piste
\cfv “O—!!!” “O—!!!”
\cvs “O—!!!” “O—!!!”
\cfv blessed blessed
\cfv heaven heaven
\cvs heav’n heav’n
\cvs youths youths
\cvs each each
}
If you want to apply one of these functions globally, you can place either of the following in your \layout block:
\override Score.LyricText.X-offset = #center-between-vowels
\override Score.LyricText.X-offset = #center-on-first-vowel
If you then need to switch off the function locally, use \once \revert LyricText.X-offset.