3

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

2
+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
  }
>>
5
  • Scheme is hard to understand. How does your code work? Mar 27 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 at 9:59
  • Thanks! This works in my simpler example, but I lose manual beams.
    – shuky
    Mar 30 at 1:54
  • @shuky Yes, I suppose you would. I rework it a bit to keep Beaming and such.
    – Lazy
    Mar 30 at 16:21
  • 1
    @shuky So, I just reworked it so that manual Beaming and articulations should work.
    – Lazy
    Mar 30 at 18:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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