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 }

Reply via email to