Thank you Carl for that needle in the haystack! I have been trying the stencil function out and with a define-markup-command I can now calculate the vowel aligned X for a word!
The only challenge is how to pass the alignment value on to the define-music-function that runs through the music elements?
Is it even possible to call a define-markup-command from a define-music-function?
Or can I make a function which has access to both the music, layout and props objects at the same time?
Below is the code so far.
\version "2.12.1"
#(define vowel-set (list->char-set (string->list "AEIOUYÅÄÖaeiouyåäö")))
#(define secondary-vowel-set (list->char-set (string->list "")))
#(define-markup-command (centertext layout props text) (markup?)
(let* ((text-stencil (interpret-markup layout props text))
(text-width (interval-length (ly:stencil-extent text-stencil X)))
(vowel-count (string-count text vowel-set))
(vowel-position (string-index text vowel-set))
(prev-text (substring text 0 vowel-position))
(vowel (string-ref text vowel-position))
(postv-text (substring text (+ vowel-position 1) (string-length text)))
(prev-text-stencil (interpret-markup layout props prev-text))
(vowel-stencil (interpret-markup layout props vowel))
(postv-text-stencil (interpret-markup layout props postv-text))
(prev-text-width (interval-length (ly:stencil-extent prev-text-stencil X)))
(vowel-width (interval-length (ly:stencil-extent vowel-stencil X)))
(postv-text-width (interval-length (ly:stencil-extent postv-text-stencil X)))
(vowel-width-plus-space (+ vowel-width (- text-width (+ prev-text-width vowel-width postv-text-width))))
(vowel-centered-x (- (* 0.5 text-width) (+ postv-text-width (* 0.5 vowel-width-plus-space))))
(stack-stencil-line 0
(list text-stencil text-stencil text-stencil)) ;INSTEAD I WANT TO RETURN vowel-centered-x
)))
#(define (vowel-center music)
"For every EventChord in elements of @var{music}, if elements in
EventChord has a LyricEvent, add an item just before the EventChord
that will provide the appropriate self-alignment-X to allow vowel-centered
lyrics."
(define (insert-X-align element-list)
(if (null? element-list)
'()
(let* ((element (car element-list))
(element-name (ly:music-property element 'name))
(syllable (if (eq? element-name 'EventChord)
(let ((sub-element
(car
(ly:music-property element 'elements))))
(if (eq? (ly:music-property
sub-element
'name
'LyricEvent))
(ly:music-property sub-element 'text)
#f))
#f)))
(if syllable
(cons
(make-music
'ContextSpeccedMusic
'context-type 'Bottom
'element
(make-music
'OverrideProperty
'pop-first #t
'grob-property-path '(self-alignment-X)
'grob-value 0 ;HERE I WOULD INSTEAD LIKE TO CALL THE MARKUP FUNCTION centertext WITH syllable
'once #t
'symbol 'LyricText))
(cons
element
(insert-X-align (cdr element-list))))
(cons element (insert-X-align (cdr element-list)))))))
(set! (ly:music-property music 'elements)
(insert-X-align (ly:music-property music 'elements)))
music)
vowelAlignedLyrics =
#(define-music-function (parser location music) (ly:music?)
"Set x alignment so each syllable of @var{music} will be
aligned with the first vowel centered on the note."
(vowel-center music))
chant = \relative c'' { c4 d4 c4 c4 c4 }
words = \lyricmode { aligned words dont come easy }
\score {
<<
\new Voice = "melody" \chant
\new Lyrics \lyricsto "melody" \vowelAlignedLyrics \words
>>
}