1 ;;;; fret-diagrams.scm --
3 ;;;; source file of the GNU LilyPond music typesetter
5 ;;;; (c) 2004 Carl D. Sorensen <c_sorensen@byu.edu>
8 (define (fret-parse-string definition-string)
9 "parse a fret diagram string and return an alist with the appropriate values"
16 (fret-range (list 1 fret-count))
21 (items (string-split definition-string #\;)))
22 (let parse-item ((myitems items))
23 (if (not (null? (cdr myitems)))
24 (let ((test-string (car myitems)))
25 (case (car (string->list (substring test-string 0 1)))
26 ((#\f) (set! finger-code (get-numeric-from-key test-string)))
27 ((#\t) (set! thickness (get-numeric-from-key test-string)))
28 ((#\c) (set! barre-list (cons* (numerify (string-split (substring test-string 2) #\-))
30 ((#\h) (set! fret-count (get-numeric-from-key test-string)))
31 ((#\w) (set! string-count (get-numeric-from-key test-string)))
32 ((#\d) (set! dot-size (get-numeric-from-key test-string)))
33 ((#\p) (set! position (get-numeric-from-key test-string)))
35 (let ((this-list (string-split test-string #\-)))
37 (if (string->number (cadr this-list))
38 (set! dot-list (cons* (numerify this-list) dot-list))
39 (set! xo-list (cons* (numerify this-list) xo-list))))))
40 (parse-item (cdr myitems)))))
41 ; calculate fret-range
42 (let ((maxfret 0) (minfret 99))
43 (let updatemax ((fret-list dot-list))
46 (let ((fretval (cadar fret-list)))
47 (if (> fretval maxfret) (set! maxfret fretval))
48 (if (< fretval minfret) (set! minfret fretval))
49 (updatemax (cdr fret-list)))))
50 (if (> maxfret fret-count)
51 (set! fret-range (list minfret
52 (let ((upfret (- (+ minfret fret-count) 1)))
53 (if (> maxfret upfret) maxfret upfret)))))
54 ; subtract fret from dots
55 (set! dot-list (subtract-base-fret (- (car fret-range) 1) dot-list)))
56 (acons "string-count" string-count
57 (acons "dot-size" dot-size
58 (acons "position" position
59 (acons "finger-code" finger-code
60 (acons "fret-range" fret-range
61 (acons "thickness" thickness
62 (acons "barre-list" barre-list
63 (acons "dot-list" dot-list
64 (acons "xo-list" xo-list '())))))))))))
66 (define (subtract-base-fret base-fret dot-list)
70 (let ((this-list (car dot-list)))
71 (cons* (list (car this-list) (- (cadr this-list) base-fret) (if (null? (cddr this-list))
74 (subtract-base-fret base-fret (cdr dot-list))))))
76 (define (draw-strings string-count fret-range th size)
77 (let* ((fret-count (+ (- (cadr fret-range) (car fret-range)) 1))
78 (sl (* (+ fret-count 1) size))
79 (half-thickness (* th 0.5))
80 (string-stencil (ly:make-stencil (list 'draw-line th 0 0 0 sl)
81 (cons (- half-thickness) half-thickness)
82 (cons (- half-thickness) (+ sl half-thickness)))))
83 (if (= string-count 1)
85 (ly:stencil-combine-at-edge
86 (draw-strings (- string-count 1) fret-range th size) 0 1
90 (define (draw-fret-lines fret-count string-count th size)
91 (let* ((fret-length (* (- string-count 1) size))
92 (half-thickness (* th 0.5))
93 (fret-line (ly:make-stencil (list 'draw-line th 0 size fret-length size)
95 (cons (- size half-thickness) (+ size half-thickness)))))
98 (ly:stencil-combine-at-edge fret-line Y UP
99 (draw-fret-lines (- fret-count 1) string-count th size)
102 (define (draw-frets paper fret-range string-count th size)
103 (let* ((fret-count (+ (- (cadr fret-range) (car fret-range)) 1))
104 (fret-length (* (- string-count 1) size))
105 (half-thickness (* th 0.5))
106 (base-fret (car fret-range)))
107 (ly:stencil-combine-at-edge
108 (draw-fret-lines fret-count string-count th size) Y UP
110 (draw-fret-lines 1 string-count (* th 2) size)
111 (draw-fret-lines 1 string-count th size))
114 (define (draw-dots paper string-count fret-range size dot-size position finger-code dot-list)
115 "Make dots for fret diagram."
116 (let* ((dot-radius (* size dot-size))
117 (fret-count (+ (- (cadr fret-range) (car fret-range) 1)))
118 (mypair (car dot-list))
119 (restlist (cdr dot-list))
120 (xpos (* (- string-count (car mypair)) size))
121 (ypos (* (+ 4 (- fret-count (cadr mypair) position )) size))
122 (finger (caddr mypair))
123 (font (ly:paper-get-font paper `(((font-magnification . ,(* 0.8 size))(font-name . "cmss8")
124 (font-encoding Tex-text)))))
125 (font2 (ly:paper-get-font paper `(((font-magnification . ,(* (* 2 dot-size) size))(font-name . "cmss8")
126 (font-encoding Tex-text)))))
127 (font3 (ly:paper-get-font paper `(((font-magnification . ,(* (* 3 dot-size) size))(font-name . "cmss8")
128 (font-encoding Tex-text)))))
129 (extent (cons (- (* size 0.25)) (* size 0.25)))
130 (dotstencil (if (or (eq? finger nil)(eq? finger-code 0))
131 (ly:make-stencil (list 'dot xpos ypos dot-radius ) extent extent)
132 (if (eq? finger-code 1)
133 ; TODO -- Get nice circled numbers in the font, instead of this kludge
135 (ly:stencil-translate-axis
136 (ly:stencil-translate-axis
137 (fontify-text font2 (number->string finger)) (- xpos (* size 0.3)) X)
138 (- ypos (* 1 dot-size size)) Y)
139 (ly:stencil-translate-axis
140 (ly:stencil-translate-axis
141 (fontify-text font3 "O") (- xpos (* 2.2 dot-size size)) X)
142 (- ypos (* 1.7 dot-size size)) Y))
143 (if (eq? finger-code 2)
145 (ly:make-stencil (list 'dot xpos ypos dot-radius ) extent extent)
146 (ly:stencil-translate-axis
147 (ly:stencil-translate-axis
148 (fontify-text font (number->string finger)) (- xpos (* size 0.3)) X)
152 (ly:stencil-add (draw-dots paper string-count fret-range size dot-size position finger-code restlist)
155 (define (draw-xo paper string-count fret-range size xo-list)
156 "Put x and o on chord diagram."
157 (let* ((dot-radius (* size 0.25))
158 (fret-count (+ (- (cadr fret-range) (car fret-range) 1)))
159 (font (ly:paper-get-font paper `(((font-size . ,(* -5 (+ 1 (* 2.6 (- 1 size)))))(font-family . music)))))
160 (mypair (car xo-list))
161 (restlist (cdr xo-list))
162 ;TODO -- get better glyphs in font to use for x (mute string) and o (open string)
163 ; Perhaps explore just using sans-serif font?
164 (glyph-name (if (char=? (cadr mypair) #\x) "noteheads-2cross"
166 (tmpdot (if (char=? (cadr mypair) #\x) 0 (* size 0.25)))
167 (xpos (if (char=? (cadr mypair) #\x)
168 (- (* (- string-count (car mypair)) size) (* .35 size) )
169 (* (- string-count (car mypair)) size)))
170 (ypos (* (+ 3.5 fret-count) size))
171 (extent (cons (- (* size 0.25)) (* size 0.25)))
172 (glyph-stencil (ly:stencil-translate-axis
173 (ly:stencil-translate-axis (ly:find-glyph-by-name font glyph-name) ypos Y)
178 (draw-xo paper string-count fret-range size restlist)
181 (define (make-bezier-sandwich-list left right bottom height thickness)
182 (let* ((width (+ (- right left) 1))
183 (x1 (+ (* width 0.1) left))
184 (x2 (- right (* width 0.1)))
185 (bottom-control-point-height (+ bottom (- height thickness)))
186 (top-control-point-height (+ bottom height)))
187 ; order of points is: left cp low, right cp low, right end low, left end low
188 ; right cp high, left cp high, left end high, right end high.
189 (list (cons x1 bottom-control-point-height) (cons x2 bottom-control-point-height) (cons right bottom) (cons left bottom)
190 (cons x2 top-control-point-height) (cons x1 top-control-point-height) (cons left bottom) (cons right bottom))))
192 (define (draw-barre paper string-count fret-range size barre-list)
193 "Create barre indications for a chord diagram"
194 (if (not (null? barre-list))
195 (let* ((string1 (caar barre-list))
196 (string2 (cadar barre-list))
197 (fret (caddar barre-list))
198 (bottom (* size (+ 1.5 (- (cadr fret-range) fret))))
199 (left (* size (- string-count string1)))
200 (right (* size (- string-count string2)))
201 (bezier-list (make-bezier-sandwich-list left right bottom (* size 0.5) (* size 0.1)))
202 (sandwich-stencil (ly:make-stencil (list 'bezier-sandwich `(quote ,bezier-list) (* size 0.1) )
204 (cons 0 (+ bottom (* size 0.8))))))
205 (if (not (null? (cdr barre-list)))
206 (ly:stencil-add sandwich-stencil
207 (draw-barre paper string-count fret-range size (cdr barre-list)))
208 sandwich-stencil ))))
210 (define (label-fret paper string-count fret-range size)
211 "Label the base fret on a fret diagram"
212 (let ((base-fret (car fret-range))
213 (fret-count (+ (- (cadr fret-range) (car fret-range)) 1))
214 (font (ly:paper-get-font paper `(((font-magnification . ,(* 0.8 size))(font-name . "cmss8")
215 (font-encoding Tex-text))))))
216 (ly:stencil-translate-axis
217 (ly:stencil-translate-axis (fontify-text font (if (> base-fret 1)
218 (format #f "~(~:@r~)" base-fret)
219 " ")) (* (- string-count 0.5) size) X)
220 (* (- fret-count 0.2) size) Y)))
222 (define (get-numeric-from-key keystring)
223 "Get the numeric value from a key of the form k:val"
224 (string->number (substring keystring 2 (string-length keystring) )))
226 (define (numerify mylist)
227 "Convert string values to numeric or character"
230 (let ((numeric-value (string->number (car mylist))))
232 (cons* numeric-value (numerify (cdr mylist)))
233 (cons* (car (string->list (car mylist))) (numerify (cdr mylist)))))))
236 (define (make-fret-diagram paper size definition-string)
237 "Make a fret diagram"
238 (let* ((parameters (fret-parse-string definition-string))
239 (string-count (cdr (assoc "string-count" parameters)))
240 (fret-range (cdr (assoc "fret-range" parameters)))
241 (finger-code (cdr (assoc "finger-code" parameters)))
242 (dot-size (cdr (assoc "dot-size" parameters)))
243 (position (cdr (assoc "position" parameters)))
244 (dot-list (cdr (assoc "dot-list" parameters)))
245 (xo-list (cdr (assoc "xo-list" parameters)))
246 (line-thickness (cdr (assoc "thickness" parameters)))
247 (barre-list (cdr (assoc "barre-list" parameters)))
248 (fret-diagram-stencil (ly:stencil-add
249 (draw-strings string-count fret-range line-thickness size)
250 (draw-frets paper fret-range string-count line-thickness size))))
251 (if (not (null? dot-list))
252 (set! fret-diagram-stencil (ly:stencil-add
253 (draw-dots paper string-count fret-range size dot-size position finger-code dot-list)
254 fret-diagram-stencil)))
255 (if (not (null? xo-list))
256 (set! fret-diagram-stencil (ly:stencil-add
257 (draw-xo paper string-count fret-range size xo-list)
258 fret-diagram-stencil)))
259 (if (not (null? barre-list))
260 (set! fret-diagram-stencil (ly:stencil-add
261 (draw-barre paper string-count fret-range size barre-list)
262 fret-diagram-stencil)))
263 (set! fret-diagram-stencil (ly:stencil-add fret-diagram-stencil (label-fret paper string-count fret-range size)))
264 (ly:stencil-align-to! fret-diagram-stencil X -.4)
265 fret-diagram-stencil))
267 (def-markup-command (fret-diagram paper props size definition-string)
269 "Syntax: \\fret-diagram size definition-string
270 eg: \\markup \\fret-diagram #0.75 #\"6-x;5-x;4-o;3-2;2-3;1-2;\"
271 for fret spacing 3/4 of staff space, D chord diagram
272 Syntax rules for @var{definition-string}:
273 Diagram items are separated by semicolons.
275 t:number -- set the line thickness (in staff spaces). Default 0.05
276 h:number -- set the height of the diagram in frets. Default 4
277 w:number -- set the width of the diagram in strings. Default 6
278 f:number -- set fingering label type
279 (0 = none, 1 = in circle on string, 2 = below string) Default 0
280 d:number -- set radius of dot, in terms of fret spacing. Default 0.25
281 p:number -- set the position of the dot in the fret space. 0.5 is centered; 1 is on lower fret bar,
282 0 is on upper fret bar. Default 0.6
283 c:string1-string2-fret -- include a barre mark from string1 to string2 on fret
284 string-fret -- place a dot on string at fret. If fret is o, string is identified
285 as open. If fret is x, string is identified as muted.
286 string-fret-fingering -- place a dot on string at fret, and label with fingering as
288 Note: There is no limit to the number of fret indications per string."
289 (make-fret-diagram paper size definition-string))