Time mark engraver: Difference between revisions
Masterpaster (talk | contribs) m Undo revision 5979 by Masterpaster (talk) Tag: Undo |
mNo edit summary |
||
| (6 intermediate revisions by 2 users not shown) | |||
| Line 1: | Line 1: | ||
When working with LilyPond’s MIDI output | 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. | ||
This snippets introduces a Scheme engraver that allows you to insert time marks directly into the music. At any point in the score you can add <code>\timeMark</code>, and LilyPond computes the elapsed time up to that position and displays it (for example, ‘2m15s’). | |||
This is useful when you need to | |||
This is useful when you need to | |||
* align score events with audio | * align score events with audio or video cues, | ||
* locate timestamps for MIDI-driven playback or mockups, | * locate timestamps for MIDI-driven playback or mockups, | ||
* find “time since start” at a few key | * find “time since start” at a few musical key positions without counting or math, or | ||
* work reliably across complex tempo maps (rit., accel., metric modulations, etc.). | * work reliably across complex tempo maps (rit., accel., metric modulations, etc.). | ||
''Upgrade notice'': This snippet must be updated with the {{Convert-ly-devel}} script if used with a LilyPond version newer than 2.24. | |||
<lilypond version="2.24"> | |||
% LSR source: https://lists.gnu.org/archive/html/lilypond-user/2024-03/msg00127.html | |||
% LSR credits: 2024 - Jean Abou Samra | |||
#(define (Elapsed_time_engraver context) | |||
(define (format-time seconds) ; example: 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 #Elapsed_time_engraver | |||
} | |||
} | |||
% The argument of the `\textEndMark` here is just a placeholder | |||
% that the engraver replaces with a time stamp (because the | |||
% `time-mark` subproperty is set). | |||
timeMark = \tweak details.time-mark ##t | |||
\tweak color "red" | |||
\textEndMark "" | |||
{ | |||
c'1 | \timeMark | |||
\tempo 4 = 120 c'4 8. 16 2 | \timeMark | |||
\tempo 4 = 180 \repeat unfold 4 c'2 | \timeMark | |||
\repeat unfold 180 c'4 | \timeMark | |||
} | |||
</lilypond> | |||
[[Category:Automatic notation]] | |||
[[Category:Contexts and engravers]] | |||
[[Category:Editorial annotations]] | |||
[[Category:Midi]] | |||
[[Category:Scheme]] | |||
[[Category:Snippet]] | |||