4

I'm pretty new to music and trying to use lilypond. I want to write some chords and have the tabstaff match up with the chordnames and fretboards on a ukulele.

Here's my lilypond file:

\version "2.20.0"
\include "predefined-ukulele-fretboards.ly"

chordmusic = \relative {
  \chordmode {
    g4 c a:m f
  }
}
<<
  \new ChordNames {
    \chordmusic
  }
  \new FretBoards \with {
    stringTunings = #ukulele-tuning
  } {
    \chordmusic
  }
  {
    \chordmusic
  }
  \new TabStaff \with {
    stringTunings = #ukulele-tuning
  } {
    \chordmusic
  }
>>

I'm expecting that bottom TabStaff to match up with the G chord shown in the fretboard. I looked around for a similar include, predefined-ukulele-fretboards.ly for the tabstaff.

lilypond render

Is lilypond just expecting me to duplicate the chords?

Thanks!

1 Answer 1

3
+50

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
  }
>>
2
  • Scheme is hard to understand. How does your code work? Mar 27, 2022 at 4:35
  • 1
    @ElementsinSpace The code looks if we have a FB diagram for the chord. If so we extract the notes (up to enharmonicity) from the diagram. Then we try to guess from the chord the enharmonic spelling of the notes. If not, we leave the chord unchanged.
    – Lazy
    Mar 27, 2022 at 9:59

Your Answer

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

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