Print ChordNames with same root and different bass as slash and bass-note

To print subsequent ChordNames only differing in its bass note as slash and bass note use the here defined engraver. The behaviour may be controlled in detail by the chordChanges context property.

#(define Bass_changes_equal_root_engraver
  (lambda (ctx)
  "For sequential @code{ChordNames} with same root, but different bass, the root
markup is dropped: D D/C D/B  -> D /C /B
The behaviour may be controlled by setting the @code{chordChanges}
    (let ((chord-pitches '())
          (last-chord-pitches '())
          (bass-pitch #f))
        ((initialize this-engraver)
          (let ((chord-note-namer (ly:context-property ctx 'chordNoteNamer)))
            ;; Set 'chordNoteNamer, respect user setting if already done
            (ly:context-set-property! ctx 'chordNoteNamer
              (if (procedure? chord-note-namer)
          ((note-event this-engraver event)
            (let* ((pitch (ly:event-property event 'pitch))
                   (pitch-name (ly:pitch-notename pitch))
                   (pitch-alt (ly:pitch-alteration pitch))
                   (bass (ly:event-property event 'bass #f))
                   (inversion (ly:event-property event 'inversion #f)))
            ;; Collect notes of the chord
            ;;  - to compare inversed chords we need to collect the bass note
            ;;    as usual member of the chord, whereas an added bass must be
            ;;    treated separate from the usual chord-notes
            ;;  - notes are stored as pairs containing their
            ;;    pitch-name (an integer), i.e. disregarding their octave and
            ;;    their alteration
            (cond (bass (set! bass-pitch pitch))
                    (set! bass-pitch pitch)
                    (set! chord-pitches
                          (cons (cons pitch-name pitch-alt) chord-pitches)))
                    (set! chord-pitches
                          (cons (cons pitch-name pitch-alt) chord-pitches)))))))
          ((chord-name-interface this-engraver grob source-engraver)
            (let ((chord-changes (ly:context-property ctx 'chordChanges #f)))
              ;; If subsequent chords are equal apart from their bass,
              ;; reset the 'text-property.
              ;; Equality is done by comparing the sorted lists of this chord's
              ;; elements and the previous chord. Sorting is needed because
              ;; inverted chords may have a different order of pitches.
              ;; `chord-changes' needs to be true
              (if (and bass-pitch
                         (sort chord-pitches car<)
                         (sort last-chord-pitches car<)))
                  (ly:grob-set-property! grob 'text
                        (ly:context-property ctx 'slashChordSeparator)
                        ((ly:context-property ctx 'chordNoteNamer)
                         (ly:context-property ctx 'chordNameLowercaseMinor))))))
              (set! last-chord-pitches chord-pitches)
              (set! chord-pitches '())
              (set! bass-pitch #f))))
        ((finalize this-engraver)
          (set! last-chord-pitches '()))))))

myChords = \chordmode {

  \set chordChanges = ##t
  d2:m d:m/cis

  \set chordChanges = ##f

  \set chordChanges = ##t
  \once \set chordChanges = ##f
  e2/gis e/+gis e e:m/f d:m d:m/cis d:m/c
  \set chordChanges = ##f

  \new ChordNames
    \with { \consists #Bass_changes_equal_root_engraver }
  \new Staff \myChords

[image of music]

LilyPond snippets v2.25.17 (development-branch).