]> git.donarmstrong.com Git - lilypond.git/blob - scm/harp-pedals.scm
Run grand-replace for 2010.
[lilypond.git] / scm / harp-pedals.scm
1 ;;;; This file is part of LilyPond, the GNU music typesetter.
2 ;;;;
3 ;;;; Copyright (C) 2008--2010 Reinhold Kainhofer <reinhold@kainhofer.com>
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
20 (define-markup-command (harp-pedal layout props definition-string) (string?)
21   #:category instrument-specific-markup ; markup type for the documentation!
22   #:properties ((size 1.2)
23                 (harp-pedal-details '())
24                 (thickness 0.5))
25   "Make a harp pedal diagram.
26
27 Possible elements in @var{definition-string}:
28
29 @table @code
30 @item ^
31 pedal is up
32 @item -
33 pedal is neutral
34 @item v
35 pedal is down
36 @item |
37 vertical divider line
38 @item o
39 the following pedal should be circled (indicating a change)
40 @end table
41
42 The function also checks if the string has the typical form of three
43 pedals, then the divider and then the remaining four pedals. If not it
44 prints out a warning. However, in any case, it will also print each symbol
45 in the order as given. This means you can place the divider (even multiple
46 dividers) anywhere you want, but you'll have to live with the warnings.
47
48 The appearance of the diagram can be tweaked inter alia using the size property
49 of the TextScript grob (@code{\\override Voice.TextScript #'size = #0.3}) for
50 the overall, the thickness property
51 (@code{\\override Voice.TextScript #'thickness = #3}) for the line thickness of
52 the horizontal line and the divider. The remaining configuration (box sizes,
53 offsets and spaces) is done by the harp-pedal-details  list of properties
54 (@code{\\override Voice.TextScript #'harp-pedal-details #'box-width = #1}).
55 It contains the following settings: @code{box-offset} (vertical shift of the
56 box center for up/down pedals), @code{box-width}, @code{box-height},
57 @code{space-before-divider} (the spacing between two boxes before the
58 divider) and @code{space-after-divider} (box spacing after the divider).
59
60 @lilypond[verbatim,quote]
61 \\markup \\harp-pedal #\"^-v|--ov^\"
62 @end lilypond
63 "
64   (let* ((pedal-list (harp-pedals-parse-string definition-string))
65         (details (begin (harp-pedal-check pedal-list) harp-pedal-details))
66         (dy (* size (assoc-get 'box-offset details 0.8))) ; offset of the box center from the line
67         (line-width (* (ly:output-def-lookup layout 'line-thickness)
68                        (chain-assoc-get 'thickness props 0.5)))
69         (box-width (* size (assoc-get 'box-width details 0.4)))
70         (box-hheight (* size (/ (assoc-get 'box-height details 1.0) 2))) ; half the box-height, saves some divisions by 2
71         (spacebeforedivider (* size (assoc-get 'space-before-divider details 0.8))) ; full space between boxes before the first divider
72         (spaceafterdivider (* size (assoc-get 'space-after-divider details 0.8))) ; full space between boxes
73         (circle-thickness (* (ly:output-def-lookup layout 'line-thickness)
74                        (assoc-get 'circle-thickness details 0.5)))
75         (circle-x-padding (* size (assoc-get 'circle-x-padding details 0.15)))
76         (circle-y-padding (* size (assoc-get 'circle-y-padding details 0.2)))
77         (box-x-dimensions (lambda (prev-x p space) (cons (+ prev-x space)
78                                                    (+ prev-x space box-width))))
79         (box-y-dimensions (lambda (prev-x p space) (cons (- (* p dy) box-hheight)
80                                                          (+ (* p dy) box-hheight))))
81         (divider-stencil (lambda (xpos) (make-line-stencil line-width
82                                                      xpos (- 0 dy box-hheight)
83                                                      xpos (+ dy box-hheight))))
84         (result (let process-pedal  ((remaining pedal-list)
85                                      (prev-x 0)
86                                      (stencils '())
87                                      (circled #f)
88                                      (space spacebeforedivider))
89           ; Terminal condition of the recursion, return (final-x . stencil-list)
90           (if (null? remaining)
91             (cons (+ prev-x space) (reverse stencils))
92
93             (case (car remaining)
94               ((1 0 -1)  ; Pedal up/neutral/down
95                   (let* ((p (car remaining))
96                         (stencil (make-filled-box-stencil
97                                    (box-x-dimensions prev-x p space)
98                                    (box-y-dimensions prev-x p space)))
99                         (pedal-stencil 
100                           (if circled 
101                               (oval-stencil stencil circle-thickness 
102                                             circle-x-padding circle-y-padding)
103                               stencil))
104                         (new-prev-x (+ prev-x space box-width)))
105                     (process-pedal (cdr remaining) new-prev-x 
106                                    (cons pedal-stencil stencils) #f space)))
107               ((#\|)  ; Divider line
108                   (let* ((xpos (+ prev-x space))
109                          (stencil (divider-stencil xpos))
110                          (new-prev-x (+ prev-x space)))
111                     (process-pedal (cdr remaining) new-prev-x 
112                                    (cons stencil stencils) 
113                                    circled spaceafterdivider)))
114               ((#\o)  ; Next pedal should be circled
115                   (process-pedal (cdr remaining) prev-x stencils #t space))
116               (else
117                   (ly:warning "Unhandled entry in harp-pedal: ~a" 
118                               (car remaining))
119                   (process-pedal (cdr remaining) 
120                                  prev-x stencils circled space))))))
121         (final-x (car result))
122         (stencils (cdr result)))
123     ; Add the horizontal line and combine all stencils:
124     (apply ly:stencil-add
125       (cons
126         (make-line-stencil line-width 0 0 final-x 0)
127         stencils))))
128
129 ;; Parse the harp pedal definition string into list of directions (-1/0/1), #\o and #\|
130 (define (harp-pedals-parse-string definition-string)
131  "Parse a harp pedals diagram string and return a list containing 1, 0, -1, #\\o or #\\|"
132   (map (lambda (c)
133     (case c
134       ((#\^) 1)
135       ((#\v) -1)
136       ((#\-) 0)
137       ((#\| #\o) c)
138       (else c)))
139     (string->list definition-string)))
140
141
142 ;; Analyze the pedal-list: Return (pedalcount . (divider positions))
143 (define (harp-pedal-info pedal-list)
144   (let check ((pedals pedal-list)
145               (pedalcount 0)
146               (dividerpositions '()))
147     (if (null? pedals)
148       (cons pedalcount (reverse dividerpositions))
149
150       (case (car pedals)
151         ((-1 0 1) (check (cdr pedals) (+ pedalcount 1) dividerpositions))
152         ((#\|)    (check (cdr pedals) pedalcount (cons pedalcount dividerpositions)))
153         (else     (check (cdr pedals) pedalcount dividerpositions))))))
154
155
156 ;; Sanity checks, spit out warning if pedal-list violates the conventions
157 (define (harp-pedal-check pedal-list)
158   "Perform some sanity checks for harp pedals (7 pedals, divider after third)"
159   (let ((info (harp-pedal-info pedal-list)))
160     ; 7 pedals:
161     (if (not (equal? (car info) 7))
162       (ly:warning "Harp pedal diagram contains ~a pedals rather than the usual 7." (car info)))
163     ; One divider after third pedal:
164     (if (null? (cdr info))
165       (ly:warning "Harp pedal diagram does not contain a divider (usually after third pedal).")
166       (if (not (equal? (cdr info) '(3)))
167         (ly:warning "Harp pedal diagram contains dividers at positions ~a. Normally, there is only one divider after the third pedal." (cdr info))))))