]> git.donarmstrong.com Git - lilypond.git/blob - scm/tablature.scm
Merge branch 'lilypond/translation' of ssh://git.sv.gnu.org/srv/git/lilypond into...
[lilypond.git] / scm / tablature.scm
1 ;;;; This file is part of LilyPond, the GNU music typesetter.
2 ;;;;
3 ;;;; Copyright (C) 2009--2010 Marc Hohl <marc@hohlart.de>
4 ;;;;
5 ;;;; LilyPond is free software: you can redistribute it and/or modify
6 ;;;; it under the terms of the GNU General Public License as published by
7 ;;;; the Free Software Foundation, either version 3 of the License, or
8 ;;;; (at your option) any later version.
9 ;;;;
10 ;;;; LilyPond is distributed in the hope that it will be useful,
11 ;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
12 ;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 ;;;; GNU General Public License for more details.
14 ;;;;
15 ;;;; You should have received a copy of the GNU General Public License
16 ;;;; along with LilyPond.  If not, see <http://www.gnu.org/licenses/>.
17
18
19 ;; for more control over glyph-name calculations,
20 ;; we use a custom callback for tab note heads
21 ;; which will ignore 'style = 'do
22 (define-public (tab-note-head::calc-glyph-name grob)
23   (let ((style (ly:grob-property grob 'style)))
24
25     (case style
26       ((cross) "2cross"))))
27
28 ;; ensure we only call note head callback when
29 ;; 'style = 'cross
30 (define-public (tab-note-head::whiteout-if-style-set grob)
31   (let ((style (ly:grob-property grob 'style)))
32
33     (if (and (symbol? style)
34              (eq? style 'cross))
35         (stencil-whiteout (ly:note-head::print grob))
36         (tab-note-head::print grob))))
37
38 ;; definitions for the "moderntab" clef:
39 ;; the "moderntab" clef will be added to the list of known clefs,
40 ;; so it can be used as any other clef: \clef "moderntab"
41 (add-new-clef "moderntab" "markup.moderntab" 0 0 0)
42
43 ;; define sans serif-style tab-Clefs as a markup:
44 (define-markup-command (customTabClef
45                                 layout props num-strings staff-space)
46   (integer? number?)
47   #:category music
48   "Draw a tab clef sans-serif style."
49   (define (square x) (* x x))
50   (let* ((scale-factor (/ staff-space 1.5))
51          (font-size (- (* num-strings 1.5 scale-factor) 7))
52          (base-skip (* (square (+ (* num-strings 0.195) 0.4)) scale-factor)))
53
54     (interpret-markup layout props
55                       (markup #:vcenter #:bold
56                               #:override (cons 'font-family 'sans)
57                               #:fontsize font-size
58                               #:override (cons 'baseline-skip base-skip)
59                               #:left-align #:center-column ("T" "A" "B")))))
60
61 ;; this function decides which clef to take
62 (define-public (clef::print-modern-tab-if-set grob)
63   (let ((glyph (ly:grob-property grob 'glyph)))
64
65     ;; which clef is wanted?
66     (if (string=? glyph "markup.moderntab")
67         ;; if it is "moderntab", we'll draw it
68         (let* ((staff-symbol (ly:grob-object grob 'staff-symbol))
69                (line-count (if (ly:grob? staff-symbol)
70                                (ly:grob-property staff-symbol 'line-count)
71                                0))
72                (staff-space (ly:staff-symbol-staff-space grob)))
73
74           (grob-interpret-markup grob (make-customTabClef-markup line-count
75                                                                  staff-space)))
76         ;; otherwise, we simply use the default printing routine
77         (ly:clef::print grob))))
78
79 ;; if stems are drawn, it is nice to have a double stem for
80 ;; (dotted) half notes to distinguish them from quarter notes:
81 (define-public (tabvoice::draw-double-stem-for-half-notes grob)
82   (let ((stem (ly:stem::print grob)))
83
84     ;; is the note a (dotted) half note?
85     (if (= 1 (ly:grob-property grob 'duration-log))
86         ;; yes -> draw double stem
87         (ly:stencil-combine-at-edge stem X RIGHT stem 0.5)
88         ;; no -> draw simple stem
89         stem)))
90
91 ;; as default, the glissando line between fret numbers goes
92 ;; upwards, here we have a function to correct this behavior:
93 (define-public (glissando::calc-tab-extra-dy grob)
94   (let* ((original (ly:grob-original grob))
95          (left-bound (ly:spanner-bound original LEFT))
96          (right-bound (ly:spanner-bound original RIGHT))
97          (left-pitch (ly:event-property (event-cause left-bound) 'pitch))
98          (right-pitch (ly:event-property (event-cause right-bound) 'pitch)))
99
100     (if (< (ly:pitch-semitones right-pitch) (ly:pitch-semitones left-pitch))
101         -0.75
102         0.75)))
103
104 ;; the handler for ties in tablature; according to TabNoteHead #'details,
105 ;; the 'tied to' note is handled differently after a line break
106 (define-public (tie::handle-tab-note-head grob)
107   (let* ((original (ly:grob-original grob))
108          (tied-tab-note-head (ly:spanner-bound grob RIGHT))
109          (spanner-start (ly:grob-property tied-tab-note-head 'span-start #f))
110          (siblings (if (ly:grob? original)
111                        (ly:spanner-broken-into original) '())))
112
113     (if spanner-start
114         ;; tab note head is right bound of a tie and left of spanner,
115         ;; -> parenthesize it at all events
116         (begin
117           (ly:grob-set-property! tied-tab-note-head 'display-cautionary #t)
118           (ly:grob-set-property! tied-tab-note-head 'stencil tab-note-head::print))
119         ;; otherwise, check whether tie is split:
120         (if (and (>= (length siblings) 2)
121                  (eq? (car (last-pair siblings)) grob))
122             ;; tie is split -> get TabNoteHead #'details
123             (let* ((details (ly:grob-property tied-tab-note-head 'details))
124                    (tied-properties (assoc-get 'tied-properties details '()))
125                    (tab-note-head-parenthesized (assoc-get 'parenthesize tied-properties #t))
126                    ;; we need the begin-of-line entry in the 'break-visibility vector
127                    (tab-note-head-visible
128                     (vector-ref (assoc-get 'break-visibility
129                                            tied-properties #(#f #f #t)) 2)))
130
131               (if tab-note-head-visible
132                   ;; tab note head is visible
133                   (if tab-note-head-parenthesized
134                       (begin
135                         (ly:grob-set-property! tied-tab-note-head 'display-cautionary #t)
136                         (ly:grob-set-property! tied-tab-note-head 'stencil tab-note-head::print)))
137                   ;; tab note head is invisible
138                  (ly:grob-set-property! tied-tab-note-head 'transparent #t)))
139
140             ;; tie is not split
141             (ly:grob-set-property! tied-tab-note-head 'transparent #t)))))
142
143
144
145 ;; repeat ties occur within alternatives in a repeat construct;
146 ;; TabNoteHead #'details handles the appearance in this case
147 (define-public (repeat-tie::handle-tab-note-head grob)
148   (let* ((tied-tab-note-head (ly:grob-object grob 'note-head))
149          (spanner-start (ly:grob-property tied-tab-note-head 'span-start #f)))
150     (if spanner-start
151         ;; tab note head is between a tie and a slur/glissando
152         ;; -> parenthesize it at all events
153         (begin
154           (ly:grob-set-property! tied-tab-note-head 'display-cautionary #t)
155           (ly:grob-set-property! tied-tab-note-head 'stencil tab-note-head::print))
156         ;; otherwise check 'details
157         (let* ((details (ly:grob-property tied-tab-note-head 'details))
158                (repeat-tied-properties (assoc-get 'repeat-tied-properties details '()))
159                (tab-note-head-visible (assoc-get 'note-head-visible repeat-tied-properties #t))
160                (tab-note-head-parenthesized (assoc-get 'parenthesize repeat-tied-properties #t)))
161
162         (if tab-note-head-visible
163             ;; tab note head is visible
164             (if tab-note-head-parenthesized
165                 (begin
166                   (ly:grob-set-property! tied-tab-note-head 'display-cautionary #t)
167                   (ly:grob-set-property! tied-tab-note-head 'stencil tab-note-head::print)))
168             ;; tab note head is invisible
169             (ly:grob-set-property! tied-tab-note-head 'transparent #t))))))
170
171 ;; the slurs should not be too far apart from the corresponding fret number, so
172 ;; we move the slur towards the TabNoteHeads; moreover, if the left fret number is
173 ;; the right-bound of a tie, we'll set it in parentheses:
174 (define-public (slur::draw-tab-slur grob)
175   ;; TODO: use a less "brute-force" method to decrease
176   ;; the distance between the slur ends and the fret numbers
177   (let* ((original (ly:grob-original grob))
178          (left-bound (ly:spanner-bound original LEFT))
179          (left-tab-note-head (ly:grob-property left-bound 'cause))
180          (staff-space (ly:staff-symbol-staff-space grob))
181          (control-points (ly:grob-property grob 'control-points))
182          (new-control-points (map
183                               (lambda (p)
184                                 (cons (car p)
185                                       (- (cdr p)
186                                          (* staff-space
187                                             (ly:grob-property grob 'direction)
188                                             0.35))))
189                              control-points)))
190
191     (ly:grob-set-property! grob 'control-points new-control-points)
192     (ly:slur::print grob)))
193
194 ;; The glissando routine works similarly to the slur routine; if the
195 ;; fret number is "tied to", it should become parenthesized.
196 (define-public (glissando::draw-tab-glissando grob)
197   (let* ((original (ly:grob-original grob))
198          (left-tab-note-head (ly:spanner-bound original LEFT))
199          (cautionary (ly:grob-property left-tab-note-head 'display-cautionary #f)))
200
201     (and cautionary
202          ;; increase left padding to avoid collision between
203          ;; closing parenthesis and glissando line
204          (ly:grob-set-nested-property! grob '(bound-details left padding) 0.5))
205     (ly:line-spanner::print grob)))
206
207 ;; for \tabFullNotation, the stem tremolo beams are too big in comparison to
208 ;; normal staves; this wrapper function scales accordingly:
209 (define-public (stem-tremolo::calc-tab-width grob)
210   (let ((width (ly:stem-tremolo::calc-width grob))
211         (staff-space (ly:staff-symbol-staff-space grob)))
212     (/ width staff-space)))
213
214
215 ;; a callback for custom fret labels
216 (define-public ((tab-note-head::print-custom-fret-label fret) grob)
217   (ly:grob-set-property! grob 'text fret)
218   (tab-note-head::print grob))
219
220 (define-public (tab-note-head::print grob)
221   (define (is-harmonic? grob)
222     (let ((arts (ly:event-property (event-cause grob) 'articulations)))
223       (not (null? (filter (lambda (a)
224                             (ly:in-event-class? a 'harmonic-event))
225                           arts)))))
226
227   (let* ((cautionary (ly:grob-property grob 'display-cautionary #f))
228          (details (ly:grob-property grob 'details '()))
229          (harmonic-props (assoc-get 'harmonic-properties details '()))
230          (harmonic-angularity (assoc-get 'angularity harmonic-props 2))
231          (harmonic-half-thick (assoc-get 'half-thickness harmonic-props 0.075))
232          (harmonic-padding (assoc-get 'padding harmonic-props 0))
233          (harmonic-proc (assoc-get 'procedure harmonic-props parenthesize-stencil))
234          (harmonic-width (assoc-get 'width harmonic-props 0.25))
235          (cautionary-props (assoc-get 'cautionary-properties details '()))
236          (cautionary-angularity (assoc-get 'angularity cautionary-props 2))
237          (cautionary-half-thick (assoc-get 'half-thickness cautionary-props 0.075))
238          (cautionary-padding (assoc-get 'padding cautionary-props 0))
239          (cautionary-proc (assoc-get 'procedure cautionary-props parenthesize-stencil))
240          (cautionary-width (assoc-get 'width cautionary-props 0.25))
241          (output-grob (ly:text-interface::print grob))
242          (ref-grob (grob-interpret-markup grob "8"))
243          (offset-factor (assoc-get 'head-offset details 3/5))
244          (column-offset (* offset-factor
245                            (interval-length
246                              (ly:stencil-extent
247                                (grob-interpret-markup grob "8")
248                                X)))))
249
250     (if (is-harmonic? grob)
251         (set! output-grob (harmonic-proc output-grob
252                                          harmonic-half-thick
253                                          harmonic-width
254                                          harmonic-angularity
255                                          harmonic-padding)))
256     (if cautionary
257         (set! output-grob (cautionary-proc output-grob
258                                            cautionary-half-thick
259                                            cautionary-width
260                                            cautionary-angularity
261                                            cautionary-padding)))
262     (ly:stencil-translate-axis (centered-stencil output-grob)
263                                column-offset
264                                X)))
265
266 ;; Harmonic definitions
267
268 (define node-positions
269   ;; for the node on m/n-th of the string length, we get the corresponding
270   ;; (exact) fret position by calculating p=(-12/log 2)*log(1-(m/n));
271   ;; since guitarists normally use the forth fret and not the 3.8th, here
272   ;; are rounded values, ordered by
273   ;; 1/2
274   ;; 1/3 2/3
275   ;; 1/4 2/4 3/4 etc.
276   ;; The value for 2/4 is irrelevant in practical, bacause the string sounds
277   ;; only one octave higher, not two, but since scheme normalizes the fractions
278   ;; anyway, these values are simply placeholders for easier indexing.
279   ;; According to the arithmetic sum, the position of m/n is at 1/2*(n-2)(n-1)+(m-1)
280   ;; if we start counting from zero
281   (vector 12
282            7   19
283            5   12    24
284            4    9    16   28
285            3    7    12   19    31
286            2.7  5.8  9.7  14.7  21.7  33.7
287            2.3  5    8    12    17    24    36
288            2    4.4  7    10    14    19    26  38 ))
289
290 (define partial-pitch
291   (vector '(0 0 0)
292           '(1 0 0)
293           '(1 4 0)
294           '(2 0 0)
295           '(2 2 0)
296           '(2 4 0)
297           '(2 6 -1/2)
298           '(3 0 0)
299           '(3 1 0)))
300
301 (define fret-partials
302   '(("0" . 0)
303     ("12" . 1)
304     ("7" . 2)
305     ("19" . 2)
306     ("5" . 3)
307     ("24" . 3)
308     ("4" . 4)
309     ("9" . 4)
310     ("16" . 4)
311     ("3" . 5)
312     ("2.7" . 6)
313     ("2.3" . 7)
314     ("2" . 8)))
315
316 (define-public (ratio->fret ratio)
317   "Calculate a fret number given @var{ratio} for the harmonic."
318   (let* ((nom (numerator ratio))
319          (den (denominator ratio))
320          (index (+ (* (- den 2)
321                       (- den 1)
322                       1/2)
323                    nom -1)))
324      (number->string (vector-ref node-positions index))))
325
326 (define-public (ratio->pitch ratio)
327   "Calculate a pitch given @var{ratio} for the harmonic."
328   (let* ((partial (1- (denominator ratio)))
329          (pitch (vector-ref partial-pitch partial)))
330
331   (ly:make-pitch (first pitch)
332                  (second pitch)
333                  (third pitch))))
334
335 (define-public (fret->pitch fret)
336   "Calculate a pitch given @var{fret} for the harmonic."
337   (let* ((partial (assoc-get fret fret-partials 0))
338          (pitch (vector-ref partial-pitch partial)))
339
340   (ly:make-pitch (first pitch)
341                  (second pitch)
342                  (third pitch))))
343
344 (define-public (calc-harmonic-pitch pitch music)
345   "Calculate the harmonic pitches in @var{music} given
346 @var{pitch} as the non-harmonic pitch."
347   (let ((es (ly:music-property music 'elements))
348         (e (ly:music-property music 'element))
349         (p (ly:music-property music 'pitch)))
350     (cond
351       ((pair? es)
352        (ly:music-set-property! music 'elements
353                                (map (lambda (x) (calc-harmonic-pitch pitch x)) es)))
354       ((ly:music? e)
355        (ly:music-set-property! music 'element (calc-harmonic-pitch pitch e)))
356       ((ly:pitch? p)
357        (begin
358          (set! p (ly:pitch-transpose p pitch))
359          (ly:music-set-property! music 'pitch p))))
360     music))
361
362 (define-public (make-harmonic mus)
363   "Convert music variable @var{mus} to harmonics."
364   (let ((elts (ly:music-property mus 'elements))
365         (elt (ly:music-property mus 'element)))
366        (cond
367         ((pair? elts)
368          (map make-harmonic elts))
369         ((ly:music? elt)
370          (make-harmonic elt))
371         ((music-is-of-type? mus 'note-event)
372          (set! (ly:music-property mus 'articulations)
373                (append
374                  (ly:music-property mus 'articulations)
375                  (list (make-music 'HarmonicEvent))))))
376        mus))