Displaying bar numbers on a separate staff
Appearance
Defines the BarNumberStaff context, to be used as a more prominent measure counter in between of staff groups in large scores.
\version "2.24.0"
%% http://lsr.di.unimi.it/LSR/Item?id=651
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Store the following until
%% %% end of include file
%% as BarNumberStaff-1.0.ily.
%% To use it in your projects, write
%% \include "<path-to-file/>BarNumberStaff-1.0.ily"
%% to define the BarNumberStaff context described below.
%%
%% Cheers,
%% Alexander
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% BarNumberStaff
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Version 1.0
%% 2009, Alexander Kobel (www.a-kobel.de)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Defines the
%% BarNumberStaff
%% context, to be used as a more prominent measure counter
%% in between of staff groups in large scores.
%%
%% To use it, insert
%% \new BarNumberStaff { ... }
%% into your score, where { ... } is a combination of
%% MultiMeasureRests, RehearsalMarks and optionally skips and
%% attached TextScripts.
%% Every MultiMeasureRest is translated to the bar number of
%% the current measure, RehearsalMarks and TextScripts are
%% printed as usual.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% This should also work with older versions, probably up
%% to <= 2.10. Please report if this is the case.
%% However, note that Joe Neeman's new vertical spacing
%% algorithm greatly improves the default influence of a
%% BarNumberStaff to the overall score layout, thus version
%% >= 2.13.x is recommended (see lines 213 - 231).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Internals
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% BarNumberStaves use MultiMeasureRests to render bar numbers.
%% The underlying concept is as follows:
%%
%% A global measure counter is initialized to 0.
%% Conceptually, each time a MultiMeasureRestNumber is rendered, the
%% global measure counter is examined, incremented, and used as 'text.
%% To handle compressed full measure rests, the 'text callback of
%% MultiMeasureRestNumber hijacks the 'measure-count property of the
%% parental MultiMeasureRest to know how many bars have been compressed.
%%
%% To allow several scores per file, or several BarNumberStaves per score,
%% without forcing the user to reset the global counter or initialize
%% several 'text callbacks per BarNumberStaff, the callback further
%% checks the 'when moment of the NonMusicalPaperColumn of the rest,
%% and stores both the moment and the respective bar number in another
%% global variable. If a moment before the cached value is encountered,
%% a new score is assumed and the global state is reset. If the same
%% moment is encountered, another BarNumberStaff calls the bar number
%% at the same position, so the increment of the global measure counter
%% has already been done.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
#(define global-measure-count 0)
#(define measure-at-when (cons (ly:make-moment 0 0) 1))
#(define (bar-number-staff-text-callback grob)
(let* (
(parent-rest (ly:grob-parent grob Y))
(measure-count (ly:grob-property parent-rest 'measure-count))
(parent-NMPC (ly:grob-parent parent-rest X)) ;; NonMusicalPaperColumn
(when (ly:grob-property parent-NMPC 'when))
(moment=? (lambda (a b)
(let ((diff (ly:moment-sub a b)))
(and (zero? (ly:moment-main-numerator diff))
(zero? (ly:moment-grace-numerator diff))))))
(start-measure
(if (ly:moment<? (car measure-at-when) when)
(1+ global-measure-count)
(if (moment=? (car measure-at-when) when)
(cdr measure-at-when)
1)))
(end-measure (+ start-measure (1- measure-count)))
)
(if (> start-measure end-measure)
;; Probably in a partial, but a MultiMeasureRest can't be positioned
;; well there. Thus commit suicide.
(ly:grob-suicide! grob))
(set! measure-at-when (cons when start-measure))
(set! global-measure-count end-measure)
(if (= start-measure end-measure)
(number->string start-measure)
(string-append
(number->string start-measure) " – " (number->string end-measure)))))
#(define (bar-number-staff-plain-stencil grob)
(let* (;; determine maximum Y-extent of digits
(target-Y-extent
(ly:stencil-extent (grob-interpret-markup grob "0123456789") Y)))
;; return the current text, with Y-extent enlarged to include all digits
;; centered for better alignment with RehearsalMarks and TextScripts,
;; and better fit of box-stencil around them
(ly:stencil-add
(ly:make-stencil "" '(0 . 0) target-Y-extent)
(ly:text-interface::print grob))))
#(define (bar-number-staff-boxed-stencil grob)
(box-stencil (bar-number-staff-plain-stencil grob) 0.125 0.5))
#(define (bar-number-staff-circled-stencil grob)
(circle-stencil (bar-number-staff-plain-stencil grob) 0.125 0.5))
\layout {
\context {
\type Engraver_group
\name BarNumberStaff
\alias Voice
%% This in conceptually more elegant than \alias Staff,
%% because Voice is a bottom context.
\consists Output_property_engraver
\consists Mark_engraver
\consists Multi_measure_rest_engraver
\consists Text_engraver
\consists Script_row_engraver
\consists Separating_line_group_engraver
\consists Font_size_engraver
\consists Collision_engraver
\consists Axis_group_engraver
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% MultiMeasureRests are not to be shown themselves,
%% but serve as a positioning anchor for the
%% MultiMeasureRestNumbers showing the bar numbers.
\override MultiMeasureRest.staff-position = #0
\override MultiMeasureRest.Y-offset = #0
\override MultiMeasureRest.Y-extent = #'(+inf.0 . -inf.0)
\override MultiMeasureRest.transparent = ##t
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% The MultiMeasureRestNumbers do the actual work
%% of showing bar numbers.
%% Initialize fonts.
\override MultiMeasureRestNumber.font-encoding = #'latin1
\override MultiMeasureRestNumber.font-family = #'roman
\override MultiMeasureRestNumber.font-series = #'bold
\override MultiMeasureRestNumber.font-shape = #'upright
\override MultiMeasureRestNumber.font-size = #1
%% The nasty tweak
\override MultiMeasureRestNumber.text = #bar-number-staff-text-callback
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Predefined stencils:
%% - bar-number-staff-plain-stencil
%% - bar-number-staff-boxed-stencil
%% - bar-number-staff-circled-stencil
%% (use with care; in particular, does not work well with
%% \compressEmptyMeasures)
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\override MultiMeasureRestNumber.stencil = #bar-number-staff-plain-stencil
%% Positioning and spacing
\override MultiMeasureRestNumber.extra-spacing-width = #'(0 . 0)
\override MultiMeasureRestNumber.Y-offset = #0
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Allow RehearsalMarks and TextScripts to be printed in the
%% BarNumberStaff.
\override RehearsalMark.break-align-symbols =
#(lambda (grob)
(if (= (ly:item-break-dir grob) 1)
'(time-signature key-signature staff-bar clef left-edge)
'(staff-bar)))
\override RehearsalMark.Y-offset = #0
\override RehearsalMark.outside-staff-priority = 0
rehearsalMarkFormatter = #format-mark-box-letters
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% BarNumberStaves are intended to be used between StaffGroups,
%% where no SpanBar occurs. In case of collisions, either use
%% a whiteout stencil for the RehearsalMarks, or try
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% \override RehearsalMark.self-alignment-X = #LEFT
%% \override RehearsalMark.X-offset = #1
%%
%% \override RehearsalMark.stencil =
%% #(lambda (grob) (stencil-whiteout-box (ly:text-interface::print grob)))
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\override TextScript.Y-offset = #0
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Vertical spacing
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% BarNumberStaves should not disturb the vertical spacing
%% of a score, thus the defaults are to fit them very tightly
%% into the score. Your mileage may vary, though - feel free
%% to adjust the settings as you wish.
%% Joe Neeman's new vertical spacing algorithm in version 2.13
%% greatly simplifies a tight positioning of the BarNumberStaff.
%% No substantial effort has been made to achieve the same
%% quality in <= 2.12.2.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% %% version 2.12.2
%
% \override VerticalAxisGroup.minimum-Y-extent = #'(0 . 0)
%
% %% requires version 2.13.x
% \override VerticalAxisGroup.staff-affinity = #CENTER
% \override VerticalAxisGroup.inter-staff-spacing =
% #'((space . 5) (padding . 1.5) (minimum-distance . 1.5))
%
% %% (Updaters note: due to some changes in 2.14.x the following is used)
\override VerticalAxisGroup.staff-staff-spacing =
#'((basic-distance . 1)
(minimum-distance . 1)
(padding . 3)
(stretchability . 3))
}
\context { \Score \accepts BarNumberStaff }
\context { \ChoirStaff \accepts BarNumberStaff }
\context { \GrandStaff \accepts BarNumberStaff }
\context { \PianoStaff \accepts BarNumberStaff }
\context { \StaffGroup \accepts BarNumberStaff }
}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% end of include file
%%
%% example follows:
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
marks = {
\partial 2. s2.
s1 s1 \mark \default
s1 s1 \mark \default
s1*3 \bar "|."
}
music = { \partial 2. c'4 c' c' | \repeat unfold 20 c'4 c'\breve }
\score {
<<
\new BarNumberStaff <<
\marks
{ \compressEmptyMeasures \partial 2. s2. | R1*7 }
>>
\new StaffGroup <<
\new Staff \with { instrumentName = "Tromba" } \music
\new GrandStaff <<
\new Staff \with { instrumentName = "Violino I" } \music
\new Staff \with { instrumentName = "Violino II" } \music
>>
\new Staff \with { instrumentName = "Viola" }
{ \clef alto \music }
>>
\new BarNumberStaff <<
\marks
{ \compressEmptyMeasures \partial 2. s2. | R1*7 }
>>
\new ChoirStaff <<
\new Staff \with { instrumentName = "Soprano" } \music
\new Staff \with { instrumentName = "Alto" }
{ \clef alto \music }
\new Staff \with { instrumentName = "Tenore" }
{ \clef tenor \music }
\new Staff \with { instrumentName = "Basso" }
{ \clef bass \music }
>>
\new BarNumberStaff {
\compressEmptyMeasures
\partial 2. f'''2.^"Notes in BarNumberStaves are silently ignored…"
-\markup { \dynamic p " Foo?" }
| R1 f,,-\markup { \dynamic ff \bold " Bar!" } R
| r_"…as well as standard rests." R R1*2
}
\new PianoStaff \with { instrumentName = "Continuo" } <<
\new Staff \music
\new Staff { \clef bass \music }
>>
\new Staff { \clef bass \music }
>>
\layout {
\context {
\Score
\override NonMusicalPaperColumn.line-break-permission = ##f
\remove Bar_number_engraver
\remove Mark_engraver
}
}
}