Big time signatures
In some modern conducting scores it is common to not print individual time signatures for each staff, but to print large time signatures either over the whole system or over the instrument groups. This code implements such behaviour by
- creating a new
BigTimeSignaturespanner grob that spans multiple time signatures and prints an enlarged signature over their extent, and - creating an engraver that collects all time signatures, replacing them with a
BigTimeSignaturespanner on them.
It is possible to limit height of the BigTimeSignature by overriding BigTimeSignature.height-limit. Similarly use BigTimeSignature.details.min-height to limit how small this can get. Since this implementation reuses the individual time signature stencils it is possible to modify the width thus. Scaling the width of the spanner will not affect spacing — but the calculate the actual scaling factor requires knowing the system-spacing. Thus this include a method which estimates the scaling factor for setting the width of the time signatures to a suitable value.
Scaling time signatures isotropic will lead to very strong time signatures. To get a narrower look this proposes the following mechanic: BigTimeSignature.details.width-adjustment may be set to a factor. If the height is increased by some percentage width will be increased by only this part of that percentage. E.g. if that value is set to 0.5 then if height is increased by 100% width will be increased by only 50%.
\version "2.24"
%%%%%%%%%%%%%%%% Big Time Signatures %%%%%%%%%%%%%%%%
% Version: 3 %
% Author: Tina Petzel (lilypond@petzel.at) %
% Description: Implements large time signatures as %
% occasionally used in modern conducting scores. %
% This is done by implementing a spanner around %
% the individual time signatures to calculate %
% the full extent. The big time signature %
% stencil is then obtained by scaling one %
% original stencil to the requested extent. %
% %
% Setting the `height-limit` property of the %
% `BigTimeSignature` grob will set a limit to %
% the size of the time signatures (else they %
% will span the whole group). Since this reuses %
% the original stencil changes to this will be %
% reflected. This way e.g. the width can be %
% adjusted. Similarly the minimum height of the %
% big time signature can be set in %
% `details.min-height`.
% %
% This file also defines a %
% `big-time-signature::estimate-factors`- %
% function which estimates the scaling factors %
% before spacing logic is applied. The X-factor %
% is hereby determined by the property %
% details.width-adjustment. Reasonably this %
% should be between 0 and 1. It specifies how %
% much of an increase in height will be applied %
% to width as well. E.g. when this is 0.5 and %
% height is increased by 100% the width will be %
% increased by 50%. Set this to 0 to leave width %
% untouched, set to 1 to scale width similar to %
% height. The actual scaling is left to the %
% original time signature, as this way the extra %
% width can be considered during spacing. Note %
% that this means that width needs to be %
% determined before spacing is layed out, so %
% this needs to work with estimates. %
% %
% This file also contains a %
% `time-signature::scale-to-big-width-estimate` %
% grob transformer which scales the X-extent of %
% a time signature by the X-factor estimate of %
% the big time signature (if it has one). Note %
% that this will cause UB and race conditions if %
% one time signature is part of multiple big %
% time signatures. %
% %
% Functions: %
% * (big-time-signature::y-extent-from-elements %
% grob): Calculate Y-extent of big time signature %
% * (big-time-signature::print grob): Create scaled %
% stencil from extent. %
% * (big-time-signature::estimate-factors grob): %
% Estimate scaling factors of big time %
% signature. currently this assumes that each %
% time signature corresponds to one staff which %
% will cause 5 staff spaces per time signature %
% plus 4 staff spaces of staff-staff-spacing. %
% * (time-signature::scale-to-big-width-estimate %
% grob): Scale stencil of time signature by the %
% estimated width factor for the big time sig. %
% %
% Engravers: %
% * big-time-signature-engraver: Acknowledges time %
% signature grobs and creates BigTimeSignature %
% spanners. %
% * Acknowledges: time-signature-interface %
% * Creates: BigTimeSignature %
% * Sets: %
% * @BigTimeSignature 'elements object %
% * @time-signature-interface %
% :big-time-signature object %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Define a new `BigTimeSignature` spanner
% Determine full extent of individual time signatures
#(define (big-time-signature::y-extent-from-elements grob)
(let*
((height-limit (ly:grob-property grob 'height-limit +inf.0))
(minimum-height
(assoc-get 'min-height (ly:grob-property grob 'details) 0))
(elts (ly:grob-object grob 'elements)) ; time-sig grobs
(elts-list (ly:grob-array->list elts))
; common refpoint
(ref (ly:grob-common-refpoint-of-array grob elts Y))
(exts ; extents of all ts-grobs
(map
(lambda (x) (ly:grob-extent x ref Y))
elts-list))
(full-ext ; full extent of ts column
(cons
(apply min (map car exts))
(apply max (map cdr exts))))
(full-ext-height (interval-length full-ext))
(full-ext-center (interval-center full-ext))
(limited-height
(max minimum-height (min full-ext-height height-limit)))
(limited-ext
(cons (- full-ext-center (/ limited-height 2))
(+ full-ext-center (/ limited-height 2)))))
; Y-parent should be System = ref already,
; but just in case make sure
(ly:grob-set-parent! grob Y ref)
limited-ext))
% Print big time sig. Scaling of width is done by scaling original stencil
#(define (big-time-signature::print grob)
(let*
((elts (ly:grob-object grob 'elements)) ; time-sig grobs
(limited-ext (ly:grob-property grob 'Y-extent))
(limited-height (interval-length limited-ext))
; the stencil of one time signature
(stc (ly:grob-property (ly:grob-array-ref elts 0) 'stencil))
; the Y-extent of the time sig stencil
(stc-ext-y (ly:stencil-extent stc Y))
; scaling factor for the stencil
(f (/ limited-height (interval-length stc-ext-y))))
; shift resulting stencil to begin of time sig column
(ly:stencil-translate-axis
; scale stencil and make sure it is not shifted
(ly:stencil-aligned-to (ly:stencil-scale stc 1 f) Y DOWN)
(car limited-ext) Y)))
% Get an estimate for the scaling factors just from count
#(define (big-time-signature::estimate-factors grob)
(let*
((height-limit (ly:grob-property grob 'height-limit +inf.0))
(minimum-height
(assoc-get 'min-height (ly:grob-property grob 'details) 0))
(width-adjustment
(assoc-get 'width-adjustment (ly:grob-property grob 'details) 0))
(elts (ly:grob-object grob 'elements)) ; time-sig grobs
(elts-list (ly:grob-array->list elts))
(full-ext-height (+ (* 5 (length elts-list)) (* 4 (1- (length elts-list)))))
(limited-height
(max minimum-height (min full-ext-height height-limit)))
; scaling factor for the stencil
(f (/ limited-height 5))
(f-w (+ 1 (* (1- f) width-adjustment))))
(cons f-w f)))
% Scale original time-signature width
#(define time-signature::scale-to-big-width-estimate
(grob-transformer
'stencil
(lambda (grob orig)
(let* ((bts (ly:grob-object grob 'big-time-signature))
(est (if (ly:grob? bts)
(big-time-signature::estimate-factors bts)
'(1 . 1))))
(if (> (car est) 1)
(ly:stencil-scale
orig
(car est) 1)
orig)))))
#(set! all-grob-descriptions
(sort
(cons
((@@ (lily) completize-grob-entry)
`(BigTimeSignature
. ((stencil . ,big-time-signature::print)
(Y-extent . ,big-time-signature::y-extent-from-elements)
(meta
. ((class . Spanner)
(interfaces . (spanner-interface))
(description . "A big time signature"))))))
all-grob-descriptions)
alist<?))
\layout {
\context {
\Global
\grobdescriptions #all-grob-descriptions
}
\context {
\Score
\override TimeSignature.stencil =
#time-signature::scale-to-big-width-estimate
}
}
%%% Create engraver for big time signatures
#(define (big-time-signature-engraver context)
(let ((time-sig '()))
(make-engraver
(acknowledgers
; collect time signature grobs
((time-signature-interface engraver grob source-engraver)
(set! time-sig (cons grob time-sig))))
((process-acknowledged engraver)
(if (not (null? time-sig)) ; if any time-signatures
; create BigTimeSignature spanner with time sigs at parents
(let
((spanner
(ly:engraver-make-spanner
engraver
'BigTimeSignature
'())))
(ly:spanner-set-bound! spanner UP (first time-sig))
(ly:spanner-set-bound! spanner DOWN (first time-sig))
; set one time signature as X-parent for proper alignment
(ly:grob-set-parent! spanner X (first time-sig))
(ly:grob-set-object!
spanner
'elements
(ly:grob-list->grob-array time-sig))
(for-each
(lambda (ts)
(ly:grob-set-object! ts 'big-time-signature spanner))
time-sig)))
(set! time-sig '())))))
%%%%% Examples
\paper {
print-all-headers = ##t
scoreTitleMarkup = \markup\larger\bold\fromproperty #'header:title
}
%%% Example (score level)
\score {
\header {
title = "Example 1: Whole System, standard width"
}
\layout {
\context {
\Score
\consists #big-time-signature-engraver
\override TimeSignature.transparent = ##t
}
}
<<
\new Staff {
\numericTimeSignature
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
>>
}
%%% Example (staff group level)
\score {
\header {
title = "Example 2: Per group, standard width"
}
\layout {
\context {
\StaffGroup
\consists #big-time-signature-engraver
\override TimeSignature.transparent = ##t
}
}
<<
\new Staff {
\numericTimeSignature
c''1 \time 3/4 d''2.
}
<<
\new StaffGroup <<
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
>>
>>
<<
\new StaffGroup <<
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
>>
>>
>>
}
%%% Example (staff group level, not full height)
\score {
\header {
title =
"Example 3: Per Group, limited height, 20% width adjustment"
}
\layout {
\context {
\StaffGroup
\consists #big-time-signature-engraver
\override TimeSignature.transparent = ##t
\override BigTimeSignature.height-limit = 16
\override BigTimeSignature.details.width-adjustment = 0.2
}
}
<<
\new Staff {
\numericTimeSignature
c''1 \time 3/4 d''2.
}
<<
\new StaffGroup <<
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
>>
>>
<<
\new StaffGroup <<
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
>>
>>
>>
}
%%% Example (staff group level, fixed height)
\score {
\header {
title =
"Example 4: Per Group, fixed height, 20% width adjustment"
}
\layout {
\context {
\StaffGroup
\consists #big-time-signature-engraver
\override TimeSignature.transparent = ##t
\override BigTimeSignature.height-limit = 16
\override BigTimeSignature.details.width-adjustment = 0.2
\override BigTimeSignature.details.min-height = 16
}
}
<<
\new Staff {
\numericTimeSignature
c''1 \time 3/4 d''2.
}
<<
\new StaffGroup <<
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
>>
>>
<<
\new StaffGroup <<
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2.
}
>>
>>
>>
}
\score {
\header {
title = \markup {
"Example 5: Whole system, comparison of"
\typewriter "details.width-adjustment"
}
}
\layout {
\context {
\Score
\consists #big-time-signature-engraver
\override TimeSignature.transparent = ##t
}
}
<<
\new Staff {
\numericTimeSignature
\override Score.BigTimeSignature.details.width-adjustment = 0
c''1^"0"
\override Score.BigTimeSignature.details.width-adjustment = 0.05
\time 3/4 d''2.^"0.05"
\override Score.BigTimeSignature.details.width-adjustment = 0.1
\time 2/4 e''2^"0.1"
\override Score.BigTimeSignature.details.width-adjustment = 0.15
\time 3/4 f''2.^"0.15"
\override Score.BigTimeSignature.details.width-adjustment = 0.2
\time 4/4 e''1^"0.2"
\override Score.BigTimeSignature.details.width-adjustment = 0.25
\time 3/4 d''2.^"0.25"
\override Score.BigTimeSignature.details.width-adjustment = 0.3
\time 2/4 c''2^"0.3"
\override Score.BigTimeSignature.details.width-adjustment = 0.35
\time 3/4 d''2.^"0.35"
}
\new Staff {
c''1 \time 3/4 d''2. \time 2/4 e''2 \time 3/4 f''2.
\time 4/4 e''1 \time 3/4 d''2. \time 2/4 c''2 \time 3/4 d''2.
}
\new Staff {
c''1 \time 3/4 d''2. \time 2/4 e''2 \time 3/4 f''2.
\time 4/4 e''1 \time 3/4 d''2. \time 2/4 c''2 \time 3/4 d''2.
}
>>
}