5

I'm transcribing a piece with extremely frequent time changes. Some parts have a lot of full-measure rests. I'm trying to define a function that produces a full-measure rest based on the current time signature, but as a relative Lilypond Scheme novice I can't figure out how to get the current time signature. I think I could get it from the timing context, but I can't figure out how to get that context from inside a music function.

Here's what I have:

autoRest = (define-music-function
  (parser location) ()
  (make-music
    'MultiMeasureRestEvent
    'duration
    (ly:make-duration 0 0 11 8) ; An 11/8 bar
  )
)

I'm sure I'm missing something basic here. How do I get the time signature?

3 Answers 3

5

Sadly, that's not really possible in LilyPond's current design. After the file has been parsed and music objects have been preprocessed by music functions, music enters "iteration", where the timing gets established based on the durations of the individual elements. It is at this time that you can reliably retrieve the measure length with (ly:context-property context 'measureLength) in an engraver (see https://extending-lilypond.readthedocs.io/en/latest/translation.html about those). However, at that point the music objects are already being processed; they have been created long before.

If it were easy, I think MM rests lasting one measure automatically would have been implemented long ago…

4

It would be possible to wrap each part in music functions that keep track of the time signatures (if we only have one part we would not in fact need this and would be able to do this on toplevel). There are some big caveats with this: This will fail if one autoRest is executed before a time command that takes effect before the rest. This means that you need to specify time for each Element of Simultaneous Music, else you will not get the right duration.

See this code for details how this could be done:

partA =
#(define-music-function () ()
   (let* ((measure-length 1)
          (old-time time)
          (time (define-music-function (d n) (number? number?)
                  (set! measure-length (/ d n))
                  (old-time (cons d n))))
          (aR (lambda* (#:optional (times 1))
                (make-music
                  'MultiMeasureRestMusic
                  'duration
                  (ly:make-duration 0 0 (* measure-length times))))))
     #{
       $(time 3 4)
       c'2. |
       $(aR) |
       $(time 5 4)
       $(aR 3) |
       d'4 e' c' f' g'
     #}))

partB =
#(define-music-function () ()
   (let* ((measure-length 1)
          (old-time time)
          (time (define-music-function (d n) (number? number?)
                  (set! measure-length (/ d n))
                  (old-time (cons d n))))
          (aR (lambda* (#:optional (times 1))
                (make-music
                  'MultiMeasureRestMusic
                  'duration
                  (ly:make-duration 0 0 (* measure-length times))))))
     #{
       $(time 3 4)
       e'4 f' d' |
       e' f' d' |
       $(time 5 4)
       c'2. d'2 |
       c'2. d'2 |
       c'2. b2 |
       $(aR)
     #}))

<<
  \new Staff \partA
  \new Staff \partB
>>
0
3

You can replace \time by a custom function which does the same and stores the current time signature in a global variable. This is very much contrary to the spirit of functional programming but should work as long as your music is evaluated in the same order it will be processed (which, I suspect, covers most practical cases).

Here is some relevant code:

#(begin
(define *current-time* (cons 4 4))
; Usage: `\Time a b` (**not** a/b, sadly)
; Sets the current time signature and stores it in a global variable.
(define Time (define-music-function (a b) (integer? integer?)
  (set! *current-time* (cons a b))
  #{ \time $(cons a b) #} ))
(define (skip-n-bars ev) (define-music-function (n) (integer?)
  (make-music ev 'duration
    (ly:make-duration 0 0 (* n (car *current-time*)) (cdr *current-time*)))))
; sb = skip bars; rb = rest bars
; Usage: `\sb n` will skip `n` bars with current time signature
(define sb (skip-n-bars 'SkipEvent))
(define rb (skip-n-bars 'MultiMeasureRestMusic))
)
\score { \relative c' {
  \Time 6 8 c4. d \rb 3 e f
} }

As you can see, there are two other differences with \time:

  • I was unable to make \Time 6/8 work, so I had to settle for a space instead (6/8 is lexed as a pair but not accepted by the pair? predicate) — I did not try too hard either, if anyone has a solution for this, feel free to comment;
  • \rb and \sb invocations do not set the last duration in the same way R or s would do, so the note after the \rb call takes its duration from the note before it (here 4.). (I would call this a feature, since it is quite rare to want to reuse a multi-bar rest duration for a note!).
2
  • (By the way, I have somewhere in my macros file a very similar trick with \clef, which allows e.g. setting a temporary bass clef for low notes and then return to the previous clef, whichever one it was. The use cases are very few however (I play the trombone and go from either C clef down to bass on occasion)). Aug 20, 2022 at 20:41
  • No need to do begin. Just use multiple statements, each started by a #. About the 6/8 thing: Simply do Time= #(define-music-function (frac) (pair?) (set! *current-time* frac) #{ \time #frac #} ). 6/8 is interpreted as scheme pair by the parser. Also keep in mind that this function will update the value by parsing time, not by score time, which means you might run into issues when using simultaneous music, or even using multiple variables not necessary defined in score order.
    – Lazy
    Aug 20, 2022 at 21:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.