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