On 20/05/2025 14:07, Christophe Thiebaud wrote:

I'm a long-time LilyPond user and (formerly) a professional software developer — now retired, with more time for musical experiments and hobbies. I recently completed a small side project: animating a score in sync with its audio, using the SVG output from LilyPond.

https://bwv1006.cthiebaud.com/

The idea is simple: highlight each note in the SVG score as it is played in real time. The implementation, however, turned out to be... less simple.


      Attempt 1: Modify LilyPond

I initially tried to implement this /inside/ LilyPond, attempting to inject custom attributes into the SVG output to aid synchronization. But I quickly realized that diving into LilyPond’s internals (especially via Guile) would be a steep climb. I gave up that route, for now.

I would say your first instinct was right, although there is a lot to learn and understand when extending LilyPond. The attached example adds an id attribute to note heads, where the id value corresponds to the midi time. More information could be added to the id, such as the staff or voice where the note appears. Maybe this is enough for you to correlate the SVG and MIDI files.

--
Timothy Lanfear, Bristol, UK.

Attachment: svgid.midi
Description: MIDI audio

\version "2.24.0"

#(define divisions 384)
#(define quarter (ly:make-moment 1/4))

LabelNotes =
#(lambda (context)
  (make-engraver
    (acknowledgers
      ((note-head-interface engraver grob source-engraver)
        (let* ((moment (ly:context-current-moment context))
               (midiclocks (* divisions (ly:moment-main (ly:moment-div moment quarter)))))
          (ly:grob-set-property! grob 'output-attributes `((id . ,midiclocks))))))))

\layout {
  \context { \Voice \consists \LabelNotes }
}

voiceFive = #(context-spec-music (make-voice-props-set 4) 'Voice)

\score {
  \new Staff {
    \key d\minor
    \time 3/4
    <<
      \new Voice {
        \voiceOne
        \partial 2 a'4. a'8 |
        e''4 e''4. e''8 |
        f''4 d''4. c''8 |
        bes'4 a' g'16[( f' e' f')] |
        g'16[( e')  f'( d')]
      }
      \new Voice {
        \voiceThree
        \partial 2 f'2 |
        bes'4 a'2 |
        a'4 s2 |
        g'4 f'4 s4 |
        s4
      }
      \new Voice {
        \voiceFive
        \partial 2 s2 |
        g'4 g'2 |
        f'4 f'2 |
        s2. |
        s2
      }
      \new Voice {
        \voiceTwo
        \partial 2 d'2 |
        d'4 cis'2 |
        d'4 bes2 |
        g'4 a cis' |
        d'8 s8
      }
    >>
  }
  \midi { }
  \layout {}
}

Reply via email to