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

Reply via email to