Jump to content

Time mark engraver

From LilyPond wiki
Revision as of 11:14, 27 December 2025 by Lemzwerg (talk | contribs) (Lemzwerg moved page Time Mark Engraver to Time mark engraver without leaving a redirect)

When working with LilyPond’s MIDI output, it can be tedious to find the exact elapsed time from the start of the score to an arbitrary point—especially when the music contains multiple tempo changes. Counting measures and doing tempo math is error-prone; inspecting the MIDI file manually is possible but time-consuming.

To solve this, Jean wrote a small custom engraver that allows you to insert time marks directly into the music. At any point in the score you can add \timeMark, and LilyPond will compute the elapsed time up to that position and display it (2m15s e.g.).

Use case

This is useful when you need to:

  • align score events with audio/video cues,
  • locate timestamps for MIDI-driven playback or mockups,
  • find “time since start” at a few key musical points without counting or math,
  • work reliably across complex tempo maps (rit., accel., metric modulations, etc.).
\version "2.24.4"
% source: https://lists.gnu.org/archive/html/lilypond-user/2024-03/msg00127.html
% credits: 2024 - Jean Abou Samra

#(define (Custom_engraver!! context)
   (define (format-time seconds)  ; ex.: 2m15s
     (let ((minutes (euclidean-quotient seconds 60))
           (rest (euclidean-remainder seconds 60)))
       (string-append (if (zero? minutes) "" (format #f "~am" minutes))
                      (format #f "~as" (round rest)))))
   (let ((wholes-per-minute 15)
         (last-time ZERO-MOMENT)
         (total-time 0)
         (marks '()))
     (make-engraver
      ((process-music engraver)
       (let* ((new-time (ly:context-current-moment context))
              (time-delta (ly:moment-main (ly:moment-sub new-time last-time)))
              (new-wholes-per-minute
                (and=> (ly:context-property context 'tempoWholesPerMinute #f)
                       ly:moment-main)))
         (set! total-time
               (+ total-time (* 60 (/ time-delta wholes-per-minute))))
         (set! last-time new-time)
         (when new-wholes-per-minute
           (set! wholes-per-minute new-wholes-per-minute))))
      (acknowledgers
       ((text-mark-interface engraver grob source-engraver)
        (set! marks (cons grob marks))))
      ((process-acknowledged engraver)
       (for-each (lambda (grob)
                   (when (assq-ref (ly:grob-property grob 'details) 'time-mark)
                     (ly:grob-set-property! grob 'text (format-time 
total-time))))
                 marks)
       (set! marks '())))))

\layout {
  \context {
    \Score
    \consists #Custom_engraver!!
  }
}

timeMark = \tweak details.time-mark ##t \tweak color "red" \textEndMark 
"Abracadabra"

{
  c'1
  \timeMark
  \tempo 4 = 120
  c'4 8. 16 2
  \timeMark
  \tempo 4 = 180
  c'2 2
  \timeMark
  \repeat unfold 180 c'4
  \timeMark
}