]> git.donarmstrong.com Git - lilypond.git/blob - scm/tablature.scm
Add predefined ukulele fretboards
[lilypond.git] / scm / tablature.scm
1 ;;;; tablature.scm
2 ;;;;
3 ;;;; source file of the GNU LilyPond music typesetter
4 ;;;;
5 ;;;; (c) 2009 Marc Hohl <marc@hohlart.de>
6
7 ;; default tunings for common string instruments
8 ;; guitar tunings
9 (define-public guitar-tuning '(4 -1 -5 -10 -15 -20))
10 (define-public guitar-seven-string-tuning '(4 -1 -5 -10 -15 -20 -25))
11 (define-public guitar-drop-d-tuning '(4 -1 -5 -10 -15 -22))
12 (define-public guitar-open-g-tuning '(2 -1 -5 -10 -17 -22))
13 (define-public guitar-open-d-tuning '(2 -3 -6 -10 -15 -22))
14 (define-public guitar-dadgad-tuning '(2 -3 -7 -10 -15 -22))
15 (define-public guitar-lute-tuning '(4 -1 -6 -10 -15 -20))
16 (define-public guitar-asus4-tuning '(4 -3 -8 -10 -15 -20))
17 ;; bass tunings
18 (define-public bass-tuning '(-17 -22 -27 -32))
19 (define-public bass-four-string-tuning '(-17 -22 -27 -32))
20 (define-public bass-drop-d-tuning '(-17 -22 -27 -34))
21 (define-public bass-five-string-tuning '(-17 -22 -27 -32 -37))
22 (define-public bass-six-string-tuning '(-12 -17 -22 -27 -32 -37))
23 ;; mandolin
24 (define-public mandolin-tuning '(16 9 2 -5))
25 ;; tunings for 5-string banjo
26 (define-public banjo-open-g-tuning '(2 -1 -5 -10 7))
27 (define-public banjo-c-tuning '(2 -1 -5 -12 7))
28 (define-public banjo-modal-tuning '(2 0 -5 -10 7))
29 (define-public banjo-open-d-tuning '(2 -3 -6 -10 9))
30 (define-public banjo-open-dm-tuning '(2 -3 -6 -10 9))
31 ;; convert 5-string banjo tuning to 4-string by removing the 5th string
32 (define-public (four-string-banjo tuning)
33   (reverse (cdr (reverse tuning))))
34 ;; ukulele tunings
35 (define-public ukulele-tuning '(9 4 0 7)) ;ukulele  a' e' c' g'
36 (define-public ukulele-d-tuning '(11 6 2 9)) ;ukulele d tuning, b' fis' d' a'
37 (define-public ukulele-tenor-tuning '(-5 0 4 9)) ;tenor ukulele, g c' e' a'
38 (define-public ukulele-baritone-tuning '(-10 -5 -1 4)) ;baritone ukulele, d g b e'
39
40
41 ;; for more control over glyph-name calculations,
42 ;; we use a custom callback for tab note heads
43 ;; which will ignore 'style = 'do
44 (define-public (tab-note-head::calc-glyph-name grob)
45   (let ((style (ly:grob-property grob 'style)))
46
47     (case style
48       ((cross) "2cross"))))
49
50 ;; ensure we only call note head callback when
51 ;; 'style = 'cross
52 (define-public (tab-note-head::whiteout-if-style-set grob)
53   (let ((style (ly:grob-property grob 'style)))
54
55     (if (and (symbol? style)
56              (eq? style 'cross))
57         (stencil-whiteout (ly:note-head::print grob))
58         (ly:text-interface::print grob))))
59
60 ;; definitions for the "moderntab" clef:
61 ;; the "moderntab" clef will be added to the list of known clefs,
62 ;; so it can be used as any other clef: \clef "moderntab"
63 (add-new-clef "moderntab" "markup.moderntab" 0 0 0)
64
65 ;; define sans serif-style tab-Clefs as a markup:
66 (define-builtin-markup-command (customTabClef
67                                 layout props num-strings staff-space)
68   (integer? number?)
69   music
70   ()
71   "Draw a tab clef sans-serif style."
72   (define (square x) (* x x))
73   (let* ((scale-factor (/ staff-space 1.5))
74          (font-size (- (* num-strings 1.5 scale-factor) 7))
75          (base-skip (* (square (+ (* num-strings 0.195) 0.4)) scale-factor)))
76
77     (interpret-markup layout props
78                       (markup #:vcenter #:bold
79                               #:override (cons 'font-family 'sans)
80                               #:fontsize font-size
81                               #:override (cons 'baseline-skip base-skip)
82                               #:left-align #:center-column ("T" "A" "B")))))
83
84 ;; this function decides which clef to take
85 (define-public (clef::print-modern-tab-if-set grob)
86   (let ((glyph (ly:grob-property grob 'glyph)))
87
88     ;; which clef is wanted?
89     (if (string=? glyph "markup.moderntab")
90         ;; if it is "moderntab", we'll draw it
91         (let* ((staff-symbol (ly:grob-object grob 'staff-symbol))
92                (line-count (ly:grob-property staff-symbol 'line-count))
93                (staff-space (ly:staff-symbol-staff-space grob)))
94
95           (grob-interpret-markup grob (make-customTabClef-markup line-count
96                                                                  staff-space)))
97         ;; otherwise, we simply use the default printing routine
98         (ly:clef::print grob))))
99
100 ;; if stems are drawn, it is nice to have a double stem for
101 ;; (dotted) half notes to distinguish them from quarter notes:
102 (define-public (tabvoice::draw-double-stem-for-half-notes grob)
103   (let ((stem (ly:stem::print grob)))
104
105     ;; is the note a (dotted) half note?
106     (if (= 1 (ly:grob-property grob 'duration-log))
107         ;; yes -> draw double stem
108         (ly:stencil-combine-at-edge stem X RIGHT stem 0.5)
109         ;; no -> draw simple stem
110         stem)))
111
112 ;; as default, the glissando line between fret numbers goes
113 ;; upwards, here we have a function to correct this behavior:
114 (define-public (glissando::calc-tab-extra-dy grob)
115   (let* ((original (ly:grob-original grob))
116          (left-bound (ly:spanner-bound original LEFT))
117          (right-bound (ly:spanner-bound original RIGHT))
118          (left-pitch (ly:event-property (event-cause left-bound) 'pitch))
119          (right-pitch (ly:event-property (event-cause right-bound) 'pitch)))
120
121     (if (< (ly:pitch-semitones right-pitch) (ly:pitch-semitones left-pitch))
122         -0.75
123         0.75)))
124
125 ;; for ties in tablature, fret numbers that are tied to should be invisible,
126 ;; except for 'tied to' numbers after a line break;; these will be
127 ;; parenthesized (thanks to Neil for his solution):
128 (define-public (parenthesize-tab-note-head grob)
129   ;; Helper function to parenthesize tab noteheads,
130   ;; since we can't use ParenthesesItem at this stage
131   ;; This is basically the same as the C++ function
132   ;; in accidental.cc, converted to Scheme
133   (let* ((font (ly:grob-default-font grob))
134          (open (stencil-whiteout
135                 (ly:font-get-glyph font "accidentals.leftparen")))
136          (close (stencil-whiteout
137                  (ly:font-get-glyph font "accidentals.rightparen")))
138          (me (ly:text-interface::print grob)))
139
140     (ly:stencil-combine-at-edge
141      (ly:stencil-combine-at-edge me X LEFT open) X RIGHT close)))
142
143 ;; ParenthesesItem doesn't work very well for TabNoteHead, since
144 ;; the parentheses are too small and clash with the staff-lines
145 ;; Define a callback for the 'stencils property which will tweak
146 ;; the parentheses' appearance for TabNoteHead
147 (define-public (parentheses-item::calc-tabstaff-parenthesis-stencils grob)
148   ;; the grob we want to parenthesize
149   (let ((victim (ly:grob-array-ref (ly:grob-object grob 'elements) 0)))
150
151     ;; check whether it's a note head
152     (if (grob::has-interface victim 'note-head-interface)
153         (begin
154           ;; tweak appearance before retrieving
155           ;; list of stencils '(left-paren right-paren)
156           ;; get the font-size from victim (=TabNoteHead) to handle
157           ;; grace notes properly
158           (ly:grob-set-property! grob 'font-size
159                                  (ly:grob-property victim 'font-size))
160           (ly:grob-set-property! grob 'padding 0)
161           ;; apply whiteout to each element of the list
162           (map stencil-whiteout
163                (parentheses-item::calc-parenthesis-stencils grob)))
164         (parentheses-item::calc-parenthesis-stencils grob))))
165
166 ;; the handler for ties in tablature; according to TabNoteHead #'details,
167 ;; the 'tied to' note is handled differently after a line break
168 (define-public (tie::handle-tab-note-head grob)
169   (let* ((original (ly:grob-original grob))
170          (tied-tab-note-head (ly:spanner-bound grob RIGHT))
171          (siblings (if (ly:grob? original)
172                        (ly:spanner-broken-into original) '())))
173
174     (if (and (>= (length siblings) 2)
175              (eq? (car (last-pair siblings)) grob))
176         ;; tie is split -> get TabNoteHead #'details
177         (let* ((details (ly:grob-property tied-tab-note-head 'details))
178                (tied-properties (assoc-get 'tied-properties details '()))
179                (tab-note-head-parenthesized (assoc-get 'parenthesize tied-properties #t))
180                ;; we need the begin-of-line entry in the 'break-visibility vector
181                (tab-note-head-visible
182                 (vector-ref (assoc-get 'break-visibility
183                                        tied-properties #(#f #f #t)) 2)))
184
185           (if tab-note-head-visible
186               ;; tab note head is visible
187               (if tab-note-head-parenthesized
188                   (ly:grob-set-property! tied-tab-note-head 'stencil
189                                          (lambda (grob)
190                                            (parenthesize-tab-note-head grob))))
191               ;; tab note head is invisible
192               (ly:grob-set-property! tied-tab-note-head 'transparent #t)))
193
194         ;; tie is not split -> make fret number invisible
195         (ly:grob-set-property! tied-tab-note-head 'transparent #t))))
196
197 ;; repeat ties occur within alternatives in a repeat construct;
198 ;; TabNoteHead #'details handles the appearance in this case
199 (define-public (repeat-tie::handle-tab-note-head grob)
200   (let* ((tied-tab-note-head (ly:grob-object grob 'note-head))
201          (details (ly:grob-property tied-tab-note-head 'details))
202          (repeat-tied-properties (assoc-get 'repeat-tied-properties details '()))
203          (tab-note-head-visible (assoc-get 'note-head-visible repeat-tied-properties #t))
204          (tab-note-head-parenthesized (assoc-get 'parenthesize repeat-tied-properties #t)))
205
206     (if tab-note-head-visible
207         ;; tab note head is visible
208         (if tab-note-head-parenthesized
209             (ly:grob-set-property! tied-tab-note-head 'stencil
210                                    (lambda (grob)
211                                      (parenthesize-tab-note-head grob))))
212         ;; tab note head is invisible
213         (ly:grob-set-property! tied-tab-note-head 'transparent #t))))
214
215 ;; the slurs should not be too far apart from the corresponding fret number, so
216 ;; we move the slur towards the TabNoteHeads:
217 (define-public (slur::draw-tab-slur grob)
218   ;; TODO: use a less "brute-force" method to decrease
219   ;; the distance between the slur ends and the fret numbers
220   (let* ((staff-space (ly:staff-symbol-staff-space grob))
221          (control-points (ly:grob-property grob 'control-points))
222          (new-control-points (map
223                               (lambda (p)
224                                 (cons (car p)
225                                       (- (cdr p)
226                                          (* staff-space
227                                             (ly:grob-property grob 'direction)
228                                             0.35))))
229                               control-points)))
230
231     (ly:grob-set-property! grob 'control-points new-control-points)
232     (ly:slur::print grob)))