1 (in-package :gsharp-beaming
)
3 ;;; The beaming function takes a list of the form:
4 ;;; ((p1 x1) (p2 x2) ... (pn xn))
5 ;;; where p1 through pn are staff positions (bottom line is 0,
6 ;;; increas upwards by 1 for each staff step) and x1 through xn
7 ;;; are x positions for the clusters given in the same unit as the
8 ;;; positions, i.e., staff steps
10 ;;; The result of the computation is a VALID BEAMING. Such a beaming
11 ;;; is represented as a list of two elements representing the left and
12 ;;; the right end of the primary beam, respectively. Each element is
13 ;;; a cons of two integers, the fist representing the staff line where
14 ;;; the lower line is numbered 0, and so on in steps of two so that
15 ;;; the upper one is numbered 8. The second of the two integers
16 ;;; represents the position of the beam with respect to the staff
17 ;;; line, where 0 means straddle, 1 means sit and -1 means hang. This
18 ;;; representation makes it easy to transform the constellation by
21 ;;; Take two vertical positions and compute the beam slant and beam
22 ;;; position for the beam connecting them. A position of zero means
23 ;;; the bottom of the staff. Positive integers count up 1/2 space so
24 ;;; that C on a staff with a G-clef gets to have number -2. Negative
25 ;;; numbers go the other way. This function assumes that pos2 >= pos1,
26 ;;; and that the two notes are sufficiently far apart that the slant
27 ;;; is going to be acceptably small.
28 (defun beaming-single-stemsup-rising-twonotes (pos1 pos2
)
29 (let ((d (- pos2 pos1
))
36 (cond ((<= pos2 -
3) (case d
37 (0 `((4 . -
1) (4 . -
1)))
38 (1 `((4 . -
1) (4 .
0)))
39 (t `((4 . -
1) (4 .
1)))))
41 (0 `((4 .
0) (4 .
0)))
42 (1 `((4 . -
1) (4 .
0)))
43 (t `((4 . -
1) (4 .
1)))))
45 (0 `((6 . -
1) (6 . -
1)))
46 (1 `((4 .
0) (4 .
1)))
47 (t `((4 . -
1) (4 .
1)))))
48 ((<= pos2
8) (if (evenp pos2
)
61 ((evenp pos2
) (list (case d
72 ((3 4 5 6) `(,s3 . -
1))
76 (defun beaming-double-stemsup-rising-twonotes (pos1 pos2
)
77 (let ((d (- pos2 pos1
))
82 (cond ((<= pos2 -
3) (case d
83 (0 `((4 . -
1) (4 . -
1)))
84 (t `((4 . -
1) (4 .
0)))))
86 (0 `((4 .
0) (4 .
0)))
87 (t `((4 . -
1) (4 .
0)))))
88 ((evenp pos2
) (list (case d
95 (0 `((,s7 . -
1) (,s7 . -
1)))
96 (1 `((,s7 . -
1) (,s7 .
0)))
97 (2 `((,s5 . -
1) (,s7 . -
1)))
98 (t `((,s5 . -
1) (,s7 .
0))))))))
100 (defun reflect-pos (pos)
101 (destructuring-bind (p x b
) pos
104 (defun reflect-bpos (pos)
105 (cons (- 8 (car pos
)) (- (cdr pos
))))
107 ;;; take two points of the form (pos x b), where pos is a vertical
108 ;;; position (in staff-steps), x is a horizontal position (also in
109 ;;; staff-steps), and b is the number of beams at that position and
110 ;;; compute a valid beaming for the two points. To do so, first call
111 ;;; the function passed as an argument on the two vertical positions.
112 ;;; If the slant thus obtained is too high, repeat with a slightly
113 ;;; higher vertical position of the first point.
114 (defun beaming-two-points (p1 p2 fun
)
115 (let* ((beaming (funcall fun
(car p1
) (car p2
)))
117 (right (cadr beaming
))
120 (y1 (+ (car left
) (* 0.5 (cdr left
))))
121 (y2 (+ (car right
) (* 0.5 (cdr right
))))
122 (slant (/ (- y2 y1
) (abs (- x2 x1
)))))
123 (if (> slant
#.
(tan (/ (* 18 pi
) 180)))
124 (progn (incf (car p1
)) (beaming-two-points p1 p2 fun
))
129 ;;; Take a list of the form ((p1 x1 b1) (p2 x2 b2) ... (pn xn bn)),
130 ;;; (where pi is a vertical position, xi is a horizontal position
131 ;;; (both measured in staff-steps), and bi is the number of stems at
132 ;;; that position), a stem direction, and a function to compute a
133 ;;; valid slant of two notes sufficiently far apart, compute a valid
134 ;;; beaming. First reflect the positions vertically and horizontally
135 ;;; until the last note is higher than the first and the stems are up.
136 ;;; Then compute a valid beaming using only the first and last
137 ;;; elements of the list. Finally, move the beaming up vertically
138 ;;; until each stem it as least 2.5 staff steps long.
139 (defun beaming-general (positions stem-direction fun
)
140 (let* ((first (car positions
))
141 (last (car (last positions
)))
144 (cond ((> (car first
) (car last
))
145 (reverse (beaming-general (reverse positions
) stem-direction fun
)))
146 ((eq stem-direction
:down
)
147 (mapcar #'reflect-bpos
(beaming-general (mapcar #'reflect-pos positions
) :up fun
)))
148 (t (let* ((beaming (beaming-two-points first last fun
))
150 (right (cadr beaming
))
151 (y1 (+ (car left
) (* 0.5 (cdr left
))))
152 (y2 (+ (car right
) (* 0.5 (cdr right
))))
153 (slope (/ (- y2 y1
) (- x2 x1
)))
154 (minstem (reduce #'min positions
156 (destructuring-bind (p x b
) pos
157 (- (+ y1
(* (- x x1
) slope
)) p
(* 2 (1- b
)))))))
158 (increment (* 2 (ceiling (/ (max 0 (- 5 minstem
)) 2)))))
159 `((,(+ (car left
) increment
) .
,(cdr left
))
160 (,(+ (car right
) increment
) .
,(cdr right
))))))))
162 (defun beaming-single (positions stem-direction
)
163 (beaming-general positions stem-direction
#'beaming-single-stemsup-rising-twonotes
))
165 (defun beaming-double (positions stem-direction
)
166 (beaming-general positions stem-direction
#'beaming-double-stemsup-rising-twonotes
))