5

On the guitar it is common, both, to follow staff notation, as well as to strum chords given by their names. Using Lilypond, I am trying to notate explicit notes in staff notation and named chords with an indication of rhythm, both in a single staff. This is the output I want to achieve (corresponding MWE given below): notes in staff notation, followed by chord names over unpitched rhythm

I split the guitar voice into two logical voices: ExplicitNotes for explicit notes and NamedChords for chords. ExplicitNotes contains silence, when the music happens in NamedChords; and vice versa. This makes it easy to combine staff notation and chord names.

\version "2.18.2"

ExplicitNotes = { \clef "treble_8" g4 a b c' | s1 }

NamedChords = \chordmode{ s1 | g4:m q8 c q2 }

StrummedChords =        { s1 | g4   g8 c c2 }
%%% Code duplication with \NamedChords, since every chord is strummed once.

\score { <<
    \new ChordNames \with { chordChanges = ##t } \NamedChords
    \new Staff <<
        \new Voice \ExplicitNotes
        \new Voice \with { \consists "Pitch_squash_engraver" } { \improvisationOn \StrummedChords } %%% workaround: code duplication; good result
        %\new Voice \with { \consists "Pitch_squash_engraver" } { \improvisationOn \NamedChords }   %%% nicer code; multiple noteheads
    >>
>> }

Notating the rhythm of the chords in addition to the chord names is tricky, though. If I apply the Pitch_squash_engraver and \improvisationOn directly to NamedChords, the multiple notes from each chord are treated as colliding noteheads. You can see this by compiling the MWE with line 15 uncommented instead of the preceeding line.

My pragmatic workaround is to duplicate NamedChords. The variable StrummedChords is a copy of NamedChords, in which chord-specific syntax is omitted. As StrummedChords consists only of single notes, Pitch_squash_engraver and \improvisationOn yield the intended result. Yet this workaround is not pleasing. The information of harmony and rhythm should be stored in a single variable and it should be possible to avoid this code duplication with LilyPond.

A proper solution could be either

  • to reduce the chords to single notes automatically, e.g. to root notes, – probably one could compute StrummedChords from NamedChords with a (Scheme?) function, or
  • to mess with LilyPond's notehead-collision mechanisms (\override NoteColumn.ignore-collision = ##t does not do the trick).

2 Answers 2

7

The problem is that a chord consists of multiple notes, and squashing the pitch will only squash everything to the same pitch, but it does not remove notes from a chord. Thus you have multiple NoteHeads. Consider this modification that simply reduces chords like <pitch1 pitch2 ...> to <pitch1>:

\version "2.22"

ExplicitNotes = { \clef "treble_8" g4 a b c' | s1 }

NamedChords = \chordmode{ s1 | g4:m q8 c q2 }

firstNoteOfChord =
#(define-music-function (music) (ly:music?)
   (define (iter mus)
     (let ((elt (ly:music-property mus 'element))
           (elts (ly:music-property mus 'elements)))
       (map iter elts)
       (if (not (null? elt)) (iter elt))
       (if (and (music-is-of-type? mus 'event-chord) (not (null? elts)))
           (ly:music-set-property! mus 'elements (list (car elts))))))
   (iter music)
   music)

StrummedChords =        { s1 | g4   g8 c c2 }
%%% Code duplication with \NamedChords, since every chord is strummed once.

\score { <<
    \new ChordNames \with { chordChanges = ##t } \NamedChords
    \new Staff <<
        \new Voice \ExplicitNotes
        \new Voice \with { \consists "Pitch_squash_engraver" } { \improvisationOn \firstNoteOfChord \NamedChords }   %%% nicer code; multiple noteheads
    >>
>> }

But then this is a somewhat complicated way of switching between improvisation and regular notes (you do not want to switch Voice for each case, do you?), so instead of using pitch Squashing, why not simply force the positions of the NoteHeads like this?

\version "2.22"

ExplicitNotes = { \clef "treble_8" g4 a b c' }

NamedChords = \chordmode{ g4:m q8 c q2 }

firstNoteOfChord =
#(define-music-function (music) (ly:music?)
   (define (iter mus)
     (let ((elt (ly:music-property mus 'element))
           (elts (ly:music-property mus 'elements)))
       (map iter elts)
       (if (not (null? elt)) (iter elt))
       (if (and (music-is-of-type? mus 'event-chord) (not (null? elts)))
           (ly:music-set-property! mus 'elements (list (car elts))))))
   (iter music)
   music)

myImproOn = {
  \improvisationOn
  \temporary\override NoteHead.Y-offset = #0
}

myImproOff = {
  \improvisationOff
  \revert NoteHead.Y-offset
}

\score { <<
    \new Staff = "music" {
        \ExplicitNotes
        \myImproOn
        <<
          \firstNoteOfChord \NamedChords
          \new ChordNames \with {
            chordChanges = ##t
            alignAboveContext = "music" }
          \NamedChords
        >>
        \myImproOff
        \ExplicitNotes
        \myImproOn
        <<
          \firstNoteOfChord \NamedChords
          \new ChordNames \with {
            chordChanges = ##t
            alignAboveContext = "music" }
          \NamedChords
        >>
        \myImproOff
    }
>> }

This we can even automatise to get this:

\version "2.22"

ExplicitNotes = { \clef "treble_8" g4 a b c' }

NamedChords = \chordmode{ g4:m q8 c q2 }

firstNoteOfChord =
#(define-music-function (music) (ly:music?)
   (define (iter mus)
     (let ((elt (ly:music-property mus 'element))
           (elts (ly:music-property mus 'elements)))
       (map iter elts)
       (if (not (null? elt)) (iter elt))
       (if (and (music-is-of-type? mus 'event-chord) (not (null? elts)))
           (ly:music-set-property! mus 'elements (list (car elts))))))
   (iter music)
   music)

myImproOn = {
  \improvisationOn
  \temporary\override NoteHead.Y-offset = #0
}

myImproOff = {
  \improvisationOff
  \revert NoteHead.Y-offset
}

myImprovSection =
#(define-music-function (cont-source mus) (string? ly:music?)
   #{
     \myImproOn
     <<
       % \firstNoteOfChord will change the music object, so we need to deep copy it!
       \firstNoteOfChord #(ly:music-deep-copy mus)
       \new ChordNames \with {
            chordChanges = ##t
            alignAboveContext = #cont-source
       } { #mus }
     >>
     \myImproOff
   #})

\score { <<
    \new Staff = "music" {
        \ExplicitNotes
        \myImprovSection "music" \NamedChords
        \ExplicitNotes
        \myImprovSection "music" \NamedChords
    }
>> }

Note that the "music" argument to \myImprovSection is relevant to specify which Staff the ChordNames should be aligned above. This is not necessary if this is always the current Staff, in which case we could obtain this name from the current Staff context automatically. But if we have multiple Staves we might want to be able to specify where this one should go. We can make this argument optional though like this:

\version "2.22"

ExplicitNotes = { \clef "treble_8" g4 a b c' }

NamedChords = \chordmode{ g4:m q8 c q2 }

firstNoteOfChord =
#(define-music-function (music) (ly:music?)
   (define (iter mus)
     (let ((elt (ly:music-property mus 'element))
           (elts (ly:music-property mus 'elements)))
       (map iter elts)
       (if (not (null? elt)) (iter elt))
       (if (and (music-is-of-type? mus 'event-chord) (not (null? elts)))
           (ly:music-set-property! mus 'elements (list (car elts))))))
   (iter music)
   music)

myImproOn = {
  \improvisationOn
  \temporary\override NoteHead.Y-offset = #0
}

myImproOff = {
  \improvisationOff
  \revert NoteHead.Y-offset
}

myImprovSection =
#(define-music-function (context mus) ((string? . #f) ly:music?)
   (let ((staff-name context))
     (define (record-staffname context)
       (set! staff-name (ly:context-id (ly:context-find context 'Staff))))
     (define (set-align-above context)
       (ly:context-set-property! context 'alignAboveContext staff-name))
     #{
       $(if (not context) #{ \applyContext #record-staffname #})
       \myImproOn
       <<
         % \firstNoteOfChord will change the music object, so we need to deep copy it!
         \firstNoteOfChord #(ly:music-deep-copy mus)
         \new ChordNames \with {
              chordChanges = ##t
              #(ly:make-context-mod `((apply ,set-align-above)))
         } { #mus }
       >>
       \myImproOff
     #}))

\score {
  <<
    \new Staff = "staffA" {
        \ExplicitNotes
        \NamedChords
        \ExplicitNotes
        \myImprovSection \NamedChords
        
        % versus
        
        \ExplicitNotes
        \NamedChords
        \ExplicitNotes
        \myImprovSection "staffA" \NamedChords
    }
    \new Staff = "staffB" {
        \ExplicitNotes
        \myImprovSection \NamedChords
        \ExplicitNotes
        \NamedChords

        % versus

        \ExplicitNotes
        \myImprovSection "staffA" \NamedChords
        \ExplicitNotes
        \NamedChords
    }
  >> }
2
  • 1
    Wow, very neat! Apparently it does not work in version 2.18. I'll upgrade later and check it again. The error is: /usr/share/lilypond/2.18.2/scm/ly-syntax-constructors.scm:56:23: In expression (apply (ly:music-function-extract fun) parser ...): /usr/share/lilypond/2.18.2/scm/ly-syntax-constructors.scm:56:23: Wrong number of arguments to #<procedure #f (context mus)>
    – rfbw
    Dec 13, 2022 at 11:19
  • 1
    @rfbw Ah, sorry. Prior to some point in 2.19 all music function had to have mandatory arguments parser location. For compatibility reasons we can still add them, but we do not have to. So just change both #(define-music-function (...) to #(define-music-function (parser location ...) and it should work on 2.18. That being said, 2.18.2 is nearly 9 years old now, we expect 2.24 to release this month. If possible consider upgrading as you’ll benefit from lots of new functionality, syntax improvements and bugfixes.
    – Lazy
    Dec 13, 2022 at 12:17
6

You could try

\version "2.18.2"

ExplicitNotes = { \clef "treble_8" g4 a b c' | s1 }

NamedChords = \chordmode{ s1 | g4:m q8 c q2 }

\score { <<
    \new ChordNames \with { chordChanges = ##t } \NamedChords
    \new Staff <<
        \new Voice \ExplicitNotes
        \new Voice \with { \consists "Pitch_squash_engraver" }
    { \improvisationOn \musicMap #event-chord-reduce \NamedChords }
    >>
  >> }

From version 2.20 on, you could use the \reduceChords music function instead of \musicMap #event-chord-reduce. And really, version 2.18.2 ? That's kind of ancient.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.