Big time signatures: Difference between revisions

m New category
Tags: Mobile edit Mobile web edit
Lazy (talk | contribs)
Break long lines, add support for height limits and different width.
Line 3: Line 3:
# creating a new <code>BigTimeSignature</code> spanner grob that spans multiple time signatures and prints an enlarged signature over their extent, and
# creating a new <code>BigTimeSignature</code> spanner 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 <code>BigTimeSignature</code> spanner on them.
# creating an engraver that collects all time signatures, replacing them with a <code>BigTimeSignature</code> spanner on them.
It is possible to limit height of the <code>BigTimeSignature</code> by overriding <code>BigTimeSignature.height-limit</code>. 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: <code>BigTimeSignature.details.width-adjustment</code> 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%.


<lilypond version="2.24">
<lilypond version="2.24">
%%%%%%%%%%%%%%%% Big Time Signatures %%%%%%%%%%%%%%%%
% Version: 2                                        %
% 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.                                      %
%                                                  %
%  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::print grob): Calculate    %
%  extent of big time signature and create        %
%  scaled stencil.                                %
% * (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
%%% Define a new `BigTimeSignature` spanner
% Print big time sig. Scaling of width is done by scaling original stencil
#(define (big-time-signature::print grob)
#(define (big-time-signature::print grob)
   (let*
   (let*
     ((elts (ly:grob-object grob 'elements)) ; time-sig grobs
     ((height-limit (ly:grob-property grob 'height-limit +inf.0))
     (ref (ly:grob-common-refpoint-of-array grob elts Y)) ; common refpoint
    (elts (ly:grob-object grob 'elements)) ; time-sig grobs
     (exts (map (lambda (x) (ly:grob-extent x ref Y)) (ly:grob-array->list elts))) ; extents of all ts-grobs
    (elts-list (ly:grob-array->list elts))
     (full-ext (cons (apply min (map car exts)) (apply max (map cdr exts)))) ; full extent of ts column
    ; common refpoint
     (stc (ly:grob-property (ly:grob-array-ref elts 0) 'stencil)) ; the stencil of one time signature
     (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 (min full-ext-height height-limit))
    (limited-ext
      (cons (- full-ext-center (/ limited-height 2))
            (+ full-ext-center (/ limited-height 2))))
    ; the stencil of one time signature
     (stc (ly:grob-property (ly:grob-array-ref elts 0) 'stencil))
     (stc-ext-y (car exts)) ; the Y-extent of the time sig stencil
     (stc-ext-y (car exts)) ; the Y-extent of the time sig stencil
     (f (/ (interval-length full-ext) (interval-length stc-ext-y)))) ; scaling factor for the stencil
    (stc-ext-x (ly:stencil-extent stc X))
     (ly:grob-set-parent! grob Y ref) ; Y-parent should be System = ref already, but just in case make sure
      ; scaling factor for the stencil
     (ly:stencil-translate-axis ; shift resulting stencil to begin of time sig column
     (f (/ limited-height (interval-length stc-ext-y))))
     (ly:stencil-aligned-to (ly:stencil-scale stc 1 f) Y DOWN) ; scale stencil and make sure it is not shifted
     ; Y-parent should be System = ref already,
    (car full-ext) Y)))
    ; but just in case make sure
     (ly:grob-set-parent! grob Y ref)
    ; 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))
    (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 (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
#(set! all-grob-descriptions
Line 37: Line 168:
     \Global
     \Global
     \grobdescriptions #all-grob-descriptions
     \grobdescriptions #all-grob-descriptions
  }
  \context {
    \Score
    \override TimeSignature.stencil =
    #time-signature::scale-to-big-width-estimate
   }
   }
}
}
Line 46: Line 182:
     (make-engraver
     (make-engraver
       (acknowledgers
       (acknowledgers
       ((time-signature-interface engraver grob source-engraver) ; collect time signature grobs
      ; collect time signature grobs
       ((time-signature-interface engraver grob source-engraver)
         (set! time-sig (cons grob time-sig))))
         (set! time-sig (cons grob time-sig))))
       ((process-acknowledged engraver)
       ((process-acknowledged engraver)
       (if (not (null? time-sig)) ; if any time-signatures
       (if (not (null? time-sig)) ; if any time-signatures
           (let ; create BigTimeSignature spanner with time sigs at parents
           ; create BigTimeSignature spanner with time sigs at parents
             ((spanner (ly:engraver-make-spanner engraver 'BigTimeSignature '())))
          (let
             ((spanner
              (ly:engraver-make-spanner
              engraver
              'BigTimeSignature
              '())))
             (ly:spanner-set-bound! spanner UP (first time-sig))
             (ly:spanner-set-bound! spanner UP (first time-sig))
             (ly:spanner-set-bound! spanner DOWN (first time-sig))
             (ly:spanner-set-bound! spanner DOWN (first time-sig))
             (ly:grob-set-parent! spanner X (first time-sig)) ; set one time signature as X-parent for proper alignment
            ; set one time signature as X-parent for proper alignment
             (ly:grob-set-object! spanner 'elements (ly:grob-list->grob-array time-sig))))
             (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 '())))))
       (set! time-sig '())))))


%%%%% Examples
\paper {
  print-all-headers = ##t
  scoreTitleMarkup = \markup\larger\bold\fromproperty #'header:title
}


%%% Example (score level)
%%% Example (score level)
\score {
\score {
  \header {
    title = "Example 1: Whole System, standard width"
  }
   \layout {
   \layout {
     \context {
     \context {
Line 83: Line 241:
}
}


%%% 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
    }
  }


%%% Example (staff group level)
  <<
    \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 {
\score {
  \header {
    title =
    "Example 3: Per Group, limited height, 20% width adjustment"
  }
   \layout {
   \layout {
     \context {
     \context {
Line 91: Line 296:
       \consists #big-time-signature-engraver
       \consists #big-time-signature-engraver
       \override TimeSignature.transparent = ##t
       \override TimeSignature.transparent = ##t
      \override BigTimeSignature.height-limit = 15
      \override BigTimeSignature.details.width-adjustment = 0.2
     }
     }
   }
   }
Line 122: Line 329:
       >>
       >>
     >>
     >>
  >>
}
\score {
  \header {
    title = \markup {
      "Example 3: 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.
    }
   >>
   >>
}
}