See https://gitlab.com/lilypond/lilypond/-/merge_requests/875 and https://gitlab.com/lilypond/lilypond/-/issues/703. It is a known problem of Lilypond assuming strings to be monotonic in pitch, which is not the case with the Ukulele. While this issue has been known for ages an there is a very simple solution to it it turns out that this breaks the way banjo users expect to use Lilypond, which is the reason why this was never fixed.
This means for one thing that the automatic choice of strings on something like the Ukulele is bugged, as can be seen in the second chord.
The other thing is that chordmode does not do what you think it does. But we could do a function for this:
#(define (normalize pitch)
(if (>= (ly:pitch-alteration pitch) 1)
(normalize (ly:pitch-transpose pitch (ly:make-pitch 0 1 -1)))
(if (<= (ly:pitch-alteration pitch) -1)
(normalize (ly:pitch-transpose pitch (ly:make-pitch 0 -1 1/2)))
pitch)))
#(define (semitones->pitch st chord)
(if (null? chord)
(normalize (ly:make-pitch 0 0 (/ st 2)))
(if (eqv? 0
(modulo (-
st
(ly:pitch-semitones (car chord)))
12))
(let* ((ref-pitch (ly:make-pitch 0
(ly:pitch-notename (car chord))
(ly:pitch-alteration (car chord))))
(ref-st (ly:pitch-semitones ref-pitch))
(st-diff (- st ref-st)))
(ly:make-pitch (/ st-diff 12)
(ly:pitch-notename (car chord))
(ly:pitch-alteration (car chord))))
(semitones->pitch st (cdr chord)))))
#(define (defn-to-frets defn)
(if (null? defn)
defn
(let ((string (cadar defn))
(fret (cond ((eqv? (caar defn) 'open) 0)
((eqv? (caar defn) 'place-fret) (caddar defn))
(else #f))))
(if fret
(cons (cons string fret) (defn-to-frets (cdr defn)))
(defn-to-frets (cdr defn))))))
translateToChords =
#(define-music-function (fretboard-table tuning music) (hash-table? list? ly:music?)
(define (iter music)
(let ((elts (ly:music-property music 'elements))
(elt (ly:music-property music 'element)))
(if (music-is-of-type? music 'event-chord)
(let* ((pitches (event-chord-pitches music))
(hash-key (cons tuning pitches))
(defn (hash-ref fretboard-table hash-key #f))
(dparts (if defn (defn-to-frets defn)))
(notes (event-chord-notes music))
(dur (if (not (null? notes)) (ly:music-property (car notes) 'duration))))
(if defn
(let* ((notes (map
(lambda (def)
(let* ((sno (car def))
(rpi (list-ref tuning (1- sno)))
(fret-no (cdr def))
(root-st (ly:pitch-semitones rpi))
(pitch (semitones->pitch (+ root-st fret-no)
pitches)))
(if (< fret-no 0)
#f
(make-music 'NoteEvent
'pitch pitch
'duration dur
'articulations
(list (make-music
'StringNumberEvent
'string-number
sno
'tweaks
'((stencil . #f))))))))
dparts))
(non-note-elts (filter (lambda (x) (not (music-is-of-type? x 'note-event)))
elts)))
(ly:music-set-property! music 'elements
(append (filter (lambda (x) x) notes)
non-note-elts)))))
(begin
(if (not (null? elt))
(iter elt))
(for-each iter elts)))))
(iter music)
music)
\include "predefined-ukulele-fretboards.ly"
chordmusic = \relative {
\chordmode {
g8[\accent c\tenuto a:m] f
}
}
<<
\new ChordNames {
\chordmusic
}
\new FretBoards \with {
stringTunings = #ukulele-tuning
} {
\chordmusic
}
\new Staff {
\translateToChords #default-fret-table #ukulele-tuning \chordmusic
}
\new TabStaff \with {
stringTunings = #ukulele-tuning
} {
\translateToChords #default-fret-table #ukulele-tuning \chordmusic
}
>>