Jump to content

Figured bass centered horizontally on note heads

From LilyPond wiki
Revision as of 19:14, 12 July 2026 by Lemzwerg (talk | contribs) (Created page with "Due to [https://gitlab.com/lilypond/lilypond/-/work_items/3172 LilyPond issue #3172], bass figures are currently not centered automatically on whole notes. Below you can find a solution (contributed by [https://lists.gnu.org/archive/html/lilypond-user/2026-06/msg00250.html Tina Petzel]) that works around this issue by * providing a replacement function <code>c-format-bass-figure</code> for the <code>figuredBassFormatter</code> property to center the base digit, * creati...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Due to LilyPond issue #3172, bass figures are currently not centered automatically on whole notes. Below you can find a solution (contributed by Tina Petzel) that works around this issue by

  • providing a replacement function c-format-bass-figure for the figuredBassFormatter property to center the base digit,
  • creating a small engraver called figures-to-note-column-engraver that changes the X parent of bass figures to NoteColumn grobs, and
  • setting some bass figure properties to center-align them with main note heads.

A convenience macro \CenteredFiguredBass is defined to do these tasks.

There are limitations, though. It is necessary that figures-to-note-column-engraver lives in a context that is an ancestor of both Rhythmic_column_engraver (typically part of Voice) and Figured_bass_engraver. Examples:

\new Staff \with { \CenteredFiguredBass }
<<
  { bass music }
  \figuremode { figured bass }
>>
\new StaffGroup \with { \CenteredFiguredBass }
<<
  \new Staff { ... }
  \new FiguredBass { ... }
>>

In the rare case that you have multiple voices in the bass staff you might want to move the Figured_bass_engraver to Voice.

\version "2.26"

#(set-global-staff-size 26)

#(define-public (c-format-bass-figure figure event context)
   (let* (;; User properties controlling the figured bass layout.
          (figbass-alist
           (ly:context-property context 'figuredBassPlusStrokedAlist))
          (alt-dir
           (ly:context-property context 'figuredBassAlterationDirection))
          (plus-dir
           (ly:context-property context 'figuredBassPlusDirection))
          (augmented (ly:event-property event 'augmented))
          (number-one (make-number-markup "1"))

          ;; The digit(s), horizontally positioned, or #f.
          (fig-markup
           (if (number? figure)
               (make-center-align-markup
		(cond
                 ((eq? #t (ly:event-property event 'diminished))
                  (make-slashed-digit-markup figure))
                 ((eq? #t (ly:event-property event 'augmented-slash))
                  ;; Use specially stroked digit if available and wanted.
                     (or (and-let* (((<= 6 figure 9))
                                    (glyph (assv-ref figbass-alist figure)))
			   (make-musicglyph-markup glyph))
			 (make-backslashed-digit-markup figure)))
                 ((eq? #t augmented)
                  ;; Use special digit with plus if available and wanted.
                  (or (and-let* (((>= 5 figure 2))
                                 ((eqv? plus-dir RIGHT))
                                 (glyph (assv-ref figbass-alist figure)))
			(set! augmented #f)
			(make-musicglyph-markup glyph))
                      (make-number-markup (number->string figure 10))))
                 (else (make-number-markup (number->string figure 10)))))
               #f))

          (alt (ly:event-property event 'alteration))
          (alt-bracket (ly:event-property event 'alteration-bracket #f))
          ;; The alteration, probably bracketed but not positioned yet,
          ;; or #f.
          (alt-markup
           (if (number? alt)
               (let* ((acc (assoc-get alt (@@ (lily) figbass-accidental-alist)))
                      (acc-markup
                       (if acc
                           (make-number-markup (ly:wide-char->utf-8 acc))
                           (begin
                             (ly:warning
                              (G_ "no accidental glyph found for alteration ~a")
                              alt)
                             ;; fallback
			     "?"))))
                 (if alt-bracket
                     (make-bracket-markup acc-markup)
                     acc-markup))
               #f))

          (plus-markup (if (eq? #t augmented)
                           (make-number-markup "+")
                           #f)))

     (if (and (not alt-markup) alt-bracket)
         (ly:programming-error
          "Cannot put brackets around non-existent bass figure alteration."))

     ;; We treat a solitary alteration similarly to digits.
     (if (and (not fig-markup) alt-markup)
         (begin
           (set! fig-markup
                 (make-center-align-markup alt-markup))
           (set! alt-markup #f)))

     ;; We treat a solitary plus similarly to digits (but enlarged).
     (if (and (not fig-markup) plus-markup)
         (begin
           (set! fig-markup
                 (make-align-on-other-markup
                  Y
                  CENTER number-one
                  CENTER
		  (make-center-align-markup
		   (make-fontsize-markup 3 plus-markup))))
           (set! plus-markup #f)))

     ;; The alteration gets attached either to the left or the right of
     ;; the digit(s).
     (if alt-markup
         (set! fig-markup
               (make-put-adjacent-markup
		X (if (number? alt-dir)
                      alt-dir
                      LEFT)
		fig-markup
		(make-pad-x-markup 0.1 alt-markup))))

     ;; Ditto for the plus mark.
     (if plus-markup
         (set! fig-markup
               (if fig-markup
                   (make-put-adjacent-markup
                    X (if (number? plus-dir)
                          plus-dir
                          LEFT)
                    fig-markup plus-markup)
                   plus-markup)))

     (if (markup? fig-markup)
         (make-fontsize-markup -5 fig-markup)
         (make-null-markup))))

#(define (figures-to-note-column-engraver context)
   (let ((figs '()) (nc #f))
     (make-engraver
      (acknowledgers
       ((bass-figure-interface engraver grob source)
        (set! figs (cons grob figs)))
       ((note-column-interface engraver grob source)
        (set! nc grob)))
      ((process-acknowledged engraver)
       (if nc
           (for-each
            (lambda (g)
              (ly:grob-set-parent! g X nc))
            figs))
       (set! nc #f)
       (set! figs '())))))

CenteredFiguredBass = \with {
  \consists #figures-to-note-column-engraver
  \override BassFigure.X-align-on-main-noteheads = ##t
  \override BassFigure.parent-alignment-X = #CENTER
  \override BassFigure.X-offset =
    #ly:self-alignment-interface::aligned-on-x-parent
  figuredBassFormatter = #c-format-bass-figure
}


%%%%%%%%%%%%%%%%%%%%%%%%%%%


\new Staff \with { \CenteredFiguredBass }
<<
  { c'1 4 8 8 }
  \figuremode {
    \override Staff.BassFigureAlignment.stacking-dir = #DOWN
    <15- 7+ 6/ 4\+ 3- _+ _\+>1 4 8 8
  }
>>

\new StaffGroup \with { \CenteredFiguredBass }
<<
  % To make the figured bass stuff appear in the source file
  % before the music we have to initialize the `Staff` context
  % first (and fill it later).
  \new Staff = "mystaff"
  \figuremode {
    \override Staff.BassFigureAlignment.stacking-dir = #UP
    % Work around issue #6552, fixing bracket positions.
    \override Staff.BassFigureAlignmentPositioning
                   .outside-staff-priority = ##f
    <3 [5 7]>8
      <3\+ [5/] 7/ [9 11]>
    <3+ 5- 7!>
      <3 _! 5 _- 7>
    <3 _\+ 5 _ 7>
      <3 6/ >
    <3 6\\ >
      <6> |
    <3 [5 7]>1
  }
  \context Staff = "mystaff" {
    \clef bass
    c4 c c'8 c c16 c c c | c1
  }
>>

\new StaffGroup \with { \CenteredFiguredBass }
<<
  \new Staff = "mystaff" {
    \clef bass
    c4 c f,8 c c16 c c c | c1
  }
  \figuremode {
    \override Staff.BassFigureAlignment.stacking-dir = #UP
    \override Staff.BassFigureAlignmentPositioning.direction = #DOWN
    % Work around issue #6552, fixing bracket positions.
    \override Staff.BassFigureAlignmentPositioning
                   .outside-staff-priority = ##f
    <3 [5 7]>8
      <3\+ [5/] 7/ [9 11]>
    <3+ 5- 7!>
      <3 _! 5 _- 7>
    <3 _\+ 5 _ 7>
      <3 6/ >
    <3 6\\ >
      <6> |
    <3 [5 7]>1
  }
>>