Hi all, I’ve finally got a little breathing room to update and clean up some of my Lilypond code, and implement some things I’ve always wanted to (but haven’t had time).
One of those things is “alignTo”, which claims to allow you to avoid calculating skips, etc., and simply place things at the correct musical moment; see snippet at bottom of email (credit not given here, but would be in the final patch!). If this really does work, I think it should be rolled into the main Lilypond codebase, and I’m hoping to do just that. But I have some questions, and I’d love some input/answers: 1. Is this the best way to implement such a mechanism? In particular, is overloading \tag the best option? If so, can/should we define a different name (e.g., \anchor ?) so that it has some code differentiation from other uses of \tag? 2. The interface seems just a *little* convoluted to me. I think I’d prefer not putting the function in the context line (\new Staff \alignTo \global \harmony), but rather invoke it in the note definition area, e.g. harmony = { %% intro %% verse %% bridge \alignTo #'bridge \repeat unfold 4 a'4 R1 %% coda } and then \new Staff << \global \harmony >> Is that possible? 3. Currently, the mechanism [cleverly!] extends the previous event to “fill the time/gap”. But that means you need to have “spurious” events, e.g. harmony = { %% intro %% verse %% bridge \tag #'bridge \repeat unfold 4 a'4 R1 %% coda } doesn’t work, you have to write harmony = { %% intro R1 %% verse %% bridge \tag #'bridge \repeat unfold 4 a'4 R1 %% coda } Is there a way around that? I’m sure I’ll have more questions, but that’s a good place to start. Any help/answers/suggestions/comments appreciated! Best, Kieren. _______________________________________ %%% SNIPPET BEGINS \version "2.25.24" %%% FUNCTIONS alignTo = #(define-music-function (conductor music) (ly:music? ly:music?) "Lengthen any note or rest in the sequence MUSIC just before each tag that matches a tag in the sequence CONDUCTOR, so that the tags occur at the same times from to the beginnings of MUSIC and CONDUCTOR." (define (find-sequence music) (or (fold-some-music (lambda (x) (music-is-of-type? x 'sequential-music)) (lambda (x e1) (or e1 ; use the first nonempty sequence (let ((es (ly:music-property x 'elements))) (if (null? es) #f es)))) #f music) (begin (ly:input-warning (ly:music-property music 'origin) "alignTo: cannot find sequential music") '()))) (let loop ((aligner (find-sequence conductor)) (alignee (find-sequence music)) (time-left (ly:music-length conductor))) (case (length alignee) ((0) music) ((1) (ly:music-set-property! (car alignee) 'duration (make-duration-of-length time-left)) music) (else (let* ((a (first alignee)) ; music that might be lengthened, (b (second alignee)) ; if this music is tagged (tag (ly:music-property b 'tags)) (tail (and (not (null? tag)) (find-tail (lambda (x) (not (null? (lset-intersection eq? tag (ly:music-property x 'tags))))) aligner)))) (if tail (let ((tail-length (ly:music-length (make-sequential-music tail)))) (if (ly:moment<? time-left tail-length) (ly:input-warning (ly:music-property b 'origin) "alignTo: already beyond conductor's tag ~a" (lset-intersection eq? tag (ly:music-property (car tail) 'tags))) (ly:music-set-property! a 'duration (make-duration-of-length (ly:moment-sub time-left tail-length)))))) (loop (or tail aligner) (cdr alignee) (ly:moment-sub time-left (ly:music-length a)))))))) %%% EXAMPLE global = { %% intro \tag #'intro \mark "Introduction" \time 4/4 \repeat unfold 5 s1 \bar "||" \time 3/4 s4*3*5 %% verse \tag #'verse \mark "Verse" \bar "||" \time 2/4 s4*2*5 %% bridge \tag #'bridge \mark "Bridge" \bar "||" \time 2/2 s1*5 %% coda \tag #'coda \mark "Coda" \bar "||" \time 9/8 s8*9*5 \bar "|." } melody = { %% intro \repeat unfold 20 c''4 \repeat unfold 15 c''4 %% verse \repeat unfold 10 c''4 %% bridge \repeat unfold 20 c''4 %% coda \repeat unfold 15 c''4. } harmony = { %% intro R1 %% verse %% bridge \tag #'bridge \repeat unfold 4 a'4 R1 %% coda } accompaniment = { %% intro \repeat unfold 3 b1 R1 %% verse \tag #'verse b2 b2 R2. %% bridge \tag #'bridge c'1 R2 %% coda \tag #'coda d'2. d'4. R8*9 } \score { << \new Staff << \global \melody >> \new Staff \alignTo \global \harmony \new Staff \alignTo \global \accompaniment >> \layout { indent = 0 system-count = 4 \context { \Score \omit BarNumber \override RehearsalMark.self-alignment-X = #LEFT } } } %%% SNIPPET ENDS