It is known (and documented) that bar checks need to be used with care
within Lyrics contexts. I was thinking about this problem and wondered
if a custom engraver would be able to assist. (The actual bar checks
are implemented in an iterator, presumably because it results in faster
processing when skipping typesetting.) At this time, I am not sure if
custom iterators can be created within Scheme, so consider this just a
proof-of-concept using a Scheme engraver.
It would seem that for the case of lyrics, the behavior of a so-called
"lyric bar check" would be less precise than that of the normal bar
check. Instead of requiring the bar check to occur only at the first
moment of a measure, the logic instead simply checks that no lyrics have
been created from the beginning of the measure to the time of the lyric
bar check. That is to say, the lyric bar check should be the first
"thing" that occurs in a measure when it appears.
Attached is the proof-of-concept code.
NOTE: The final lyric bar check intentionally is an example of a failing
case. Here is the expected compiled output:
====
lyric-bar-check.ly:64:54: warning: lyric bar check failed
\addlyrics { | lo -- rem | ips -- um | do -- lor sit
| a -- met }
====
Also NOTE: The code includes a toplevel-music-function that converts
normal bar checks that occur within Lyrics contexts into lyric bar
checks that the custom engraver handles. This was done so that the user
could input lyric bar checks using the same symbol as for normal music
but get the unique behavior. Not sure if this transparent approach is
best, as it could be seen as surprising to some users.
-- Aaron Hill
\version "2.25.13"
#(define-event-class 'lyric-bar-check 'music-event)
#(define custom-music-descriptions
`((LyricBarCheck
. ((description . "Check whether lyrics correlate with
the start of the measure.")
(types . (lyric-bar-check event))))))
#(set!
custom-music-descriptions
(map (lambda (x)
(set-object-property! (car x)
'music-description
(cdr (assq 'description (cdr x))))
(let ((lst (cdr x)))
(set! lst (assoc-set! lst 'name (car x)))
(set! lst (assq-remove! lst 'description))
(hashq-set! music-name-to-property-table (car x) lst)
(cons (car x) lst)))
custom-music-descriptions))
#(set! music-descriptions
(sort
(append music-descriptions custom-music-descriptions)
alist<?))
Lyric_bar_check_engraver =
#(lambda (context)
(let ((found-lyrics? #f))
(make-engraver
((start-translation-timestep engraver)
(if (ly:context-property context 'measureStartNow #f)
(set! found-lyrics? #f)))
(acknowledgers
((lyric-syllable-interface engraver grob source-engraver)
(set! found-lyrics? #t)))
(listeners
((lyric-bar-check engraver event)
(if found-lyrics?
(ly:event-warning event "lyric bar check failed")))))))
\layout { \context { \Lyrics \consists \Lyric_bar_check_engraver } }
#(define (lyrics-convert-bar-checks music)
(define (convert-bar-check mus)
(if (music-is-of-type? mus 'bar-check-event)
(make-music 'LyricBarCheck
'origin (ly:music-property mus 'origin))
mus))
(define (process-lyrics mus)
(if (and (music-is-of-type? mus 'context-specification)
(eq? 'Lyrics (ly:music-property mus 'context-type)))
(music-map convert-bar-check mus)
mus))
(music-map process-lyrics music))
#(set! toplevel-music-functions
(append (list lyrics-convert-bar-checks)
toplevel-music-functions))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
{ r4 b'4 2~ | 4 4 2 | 8 8 4. 8 4 \fine }
\addlyrics { | lo -- rem | ips -- um | do -- lor sit | a -- met }