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