1 (in-package :gsharp-drawing
)
3 (defclass x-y-width-mixin
()
4 (;; indicates the absolute y position of the system to which the
6 (system-y-position :accessor system-y-position
)
7 ;; the absolute x position of the object
8 (final-absolute-measure-xoffset :accessor final-absolute-measure-xoffset
)
9 (final-width :accessor final-width
)))
11 (define-stealth-mixin dbar
(x-y-width-mixin) bar
14 (define-stealth-mixin dmeasure
(x-y-width-mixin) measure
15 (;; an elasticity function that describes how the space right after
16 ;; the initial barline of the measure behaves as a function of the
17 ;; force that is applied to it.
18 (prefix-elasticity-function :accessor prefix-elasticity-function
)))
20 (define-stealth-mixin dstaff
() staff
21 ((yoffset :initform
0 :accessor staff-yoffset
)))
23 (define-presentation-method present
24 (object (type score-pane
:clef
) stream
(view textual-view
) &key
)
25 (format stream
"[~a clef on staff step ~a]" (name object
) (lineno object
)))
27 (define-presentation-method present
28 (object (type score-pane
:fiveline-staff
) stream
(view textual-view
) &key
)
29 (format stream
"[fiveline staff ~a]" (name object
)))
31 (define-presentation-method present
32 (object (type score-pane
:lyrics-staff
) stream
(view textual-view
) &key
)
33 (format stream
"[lyrics staff ~a]" (name object
)))
35 (defun key-signature-for-staff (staff measures
)
36 (let ((key-signatures (key-signatures staff
))
37 (barno (gsharp-numbering:number
(car (measure-bars (car measures
))))))
38 (or (and key-signatures
39 (find barno key-signatures
:from-end t
:test
#'>
40 :key
(lambda (x) (gsharp-numbering:number
(bar x
)))))
43 (defmethod draw-staff-and-clef (pane (staff fiveline-staff
) measures x1 x2
)
47 :name
,(name (clef staff
))
49 :staff-step
,(lineno (clef staff
)))
51 (let ((keysig (key-signature-for-staff staff measures
)))
52 (let ((yoffset (b-position (clef staff
))))
53 (loop for pitch in
'(6 2 5 1 4 0 3)
54 for line in
'(0 3 -
1 2 -
2 1 -
3)
55 for x from
(+ x1
10 (score-pane:staff-step
8)) by
(score-pane:staff-step
2)
56 while
(eq (aref (alterations keysig
) pitch
) :flat
)
57 do
(score-pane:draw-accidental pane
:flat x
(+ line yoffset
))))
58 (let ((yoffset (f-position (clef staff
))))
59 (loop for pitch in
'(3 0 4 1 5 2 6)
60 for line in
'(0 -
3 1 -
2 -
5 -
1 -
4)
61 for x from
(+ x1
10 (score-pane:staff-step
8)) by
(score-pane:staff-step
2.5)
62 while
(eq (aref (alterations keysig
) pitch
) :sharp
)
63 do
(score-pane:draw-accidental pane
:sharp x
(+ line yoffset
)))))
65 `((score-pane:fiveline-staff
)
69 (defmethod draw-staff-and-clef (pane (staff lyrics-staff
) measures x1 x2
)
71 `((score-pane:lyrics-staff
)
75 ;;; Return the final absolute x offset of a note. This value is
76 ;;; computed from the x offset of the cluster of the note and the
77 ;;; relative x offset of the note with respect to the cluster.
78 (defun final-absolute-note-xoffset (note)
79 (+ (final-absolute-element-xoffset (cluster note
)) (final-relative-note-xoffset note
)))
81 ;;; Return the final absolute x offset of the accidental of a note.
82 ;;; This value is computed from the x offset of the cluster of the
83 ;;; note and the relative x offset of the accidental of the note with
84 ;;; respect to the cluster.
85 (defun final-absolute-accidental-xoffset (note)
86 (+ (final-absolute-element-xoffset (cluster note
)) (final-relative-accidental-xoffset note
)))
88 (defun final-absolute-dot-xoffset (cluster)
89 (+ (final-absolute-element-xoffset cluster
) (score-pane:staff-step
(final-relative-dot-xoffset cluster
))))
93 ;;; Compute the elasticity of each timeline in each measure of the
94 ;;; measures of a system (line) by taking its duration to the power of
95 ;;; the spaceing style. This metric is arbitrarily normalized to the
96 ;;; duration of a whole note, which means that the force to apply to a
97 ;;; line is not comparable between two different lines. All we know
98 ;;; is that timelines with the same elasticity will grow and shrink in
99 ;;; parallel, and that proportions between two timelines of different
100 ;;; durations will be preserved.
101 (defun compute-elasticities (measures method
)
102 (loop for measure in measures
103 do
(loop with timelines
= (timelines measure
)
104 for i from
0 below
(flexichain:nb-elements timelines
)
105 for timeline
= (flexichain:element
* timelines i
)
106 do
(setf (elasticity timeline
)
107 (max (expt (duration timeline
) (spacing-style method
)) 0.0001)))))
109 ;;; FIXME: there should be an :around method that adds the value
110 ;;; return by the main method to the explicit horizontal offset that
111 ;;; the user wants to impose on an element, and the existence of this
112 ;;; around method should be documented.
113 ;;; FIXME: we should probably also allow for the user to introduce
114 ;;; explicit (positive or negative) bulges that will be added in by
115 ;;; the :around method, thus allowing the user to explicitly move two
116 ;;; adjacent elements further apart, or to bring them closer together.
117 (defgeneric left-bulge
(element pane
)
118 (:documentation
"The amount by which an element sticks out to the
119 left of the center of its timeline"))
121 ;;; FIXME: there should be an :around method that adds the value
122 ;;; return by the main method to the explicit horizontal offset that
123 ;;; the user wants to impose on an element, and the existence of this
124 ;;; around method should be documented.
125 ;;; FIXME: we should probably also allow for the user to introduce
126 ;;; explicit (positive or negative) bulges that will be added in by
127 ;;; the :around method, thus allowing the user to explicitly move two
128 ;;; adjacent elements further apart, or to bring them closer together.
129 (defgeneric right-bulge
(element pane
)
130 (:documentation
"The amount by which an element sticks out to the
131 right of the center of its timeline"))
133 (defmethod left-bulge ((element element
) pane
)
134 (score-pane:staff-step
1))
136 (defmethod left-bulge ((element lyrics-element
) pane
)
137 (+ (score-pane:staff-step
0.5)
138 (/ (text-size pane
(map 'string
'code-char
(text element
))) 2)))
140 (defmethod left-bulge ((element cluster
) pane
)
141 (+ (max (- (loop for note in
(notes element
)
142 when
(final-accidental note
)
143 minimize
(final-relative-accidental-xoffset note
)))
144 (if (and (non-empty-cluster-p element
)
145 (eq (final-stem-direction element
) :down
)
146 (element-has-suspended-notes element
))
147 (score-pane:staff-step
3)
148 (score-pane:staff-step
0)))
149 (score-pane:staff-step
2)))
151 (defmethod right-bulge ((element element
) pane
)
152 (score-pane:staff-step
1))
154 (defmethod right-bulge ((element lyrics-element
) pane
)
155 (+ (score-pane:staff-step
0.5)
156 (/ (text-size pane
(map 'string
'code-char
(text element
))) 2)))
158 (defmethod right-bulge ((element cluster
) pane
)
159 (if (and (non-empty-cluster-p element
)
160 (eq (final-stem-direction element
) :up
)
161 (element-has-suspended-notes element
))
162 (score-pane:staff-step
5)
163 (score-pane:staff-step
2)))
165 (defmethod right-bulge ((timesig time-signature
) pane
)
166 ;; FIXME: this is probably wrong; it should either compute the bulge
167 ;; properly, or else approximate using (length - 0.5) *
168 ;; typical-width-of-component
169 (* (length (time-signature-components timesig
))
170 (score-pane:staff-step
5)))
172 (defmethod right-bulge ((keysig key-signature
) pane
)
173 ;; FIXME: shares much code with DRAW-ELEMENT (KEY-SIGNATURE).
174 (let ((old-keysig (keysig keysig
)))
176 (loop with advance
= 0
177 for pitch in
'(6 2 5 1 4 0 3)
178 when
(and (eq (aref (alterations old-keysig
) pitch
) :flat
)
179 (not (eq (aref (alterations keysig
) pitch
)
181 do
(incf advance
(score-pane:staff-step
2))
182 finally
(incf bulge
(if (= advance
0) 0 (+ advance
(score-pane:staff-step
2)))))
183 (loop with advance
= 0
184 for pitch in
'(3 0 4 1 5 2 6)
185 when
(and (eq (aref (alterations old-keysig
) pitch
) :sharp
)
186 (not (eq (aref (alterations keysig
) pitch
) :sharp
)))
187 do
(incf advance
(score-pane:staff-step
2))
188 finally
(incf bulge
(if (= advance
0) 0 (+ advance
(score-pane:staff-step
2)))))
189 (loop for pitch in
'(6 2 5 1 4 0 3)
190 while
(eq (aref (alterations keysig
) pitch
) :flat
)
191 do
(incf bulge
(score-pane:staff-step
2)))
192 (loop for pitch in
'(3 0 4 1 5 2 6)
193 while
(eq (aref (alterations keysig
) pitch
) :sharp
)
194 do
(incf bulge
(score-pane:staff-step
2.5)))
197 ;;; As it turns out, the spacing algorithm would be very complicated
198 ;;; if we were to take into account exactly how elements with
199 ;;; arbitrarily many timelines between them might influence the
200 ;;; overall layout. Instead we apprixmate by obtaining a closest gap
201 ;;; only between adjacent timelines as follows: first, we consider
202 ;;; adjacent elements whose timelines are also adjacent (and there is
203 ;;; a special case for the last element of a layer), and set the
204 ;;; smallest gap between the timelines to the closest possible
205 ;;; distance between the two elements...
206 (defun compute-gaps-adjacent-timelines (bars method pane
)
207 (declare (ignore method
))
208 (loop for bar in bars
209 do
(loop for
(e1 e2
) on
(elements bar
)
210 for t1
= (timeline e1
)
212 (when (flexichain:flexi-last-p t1
)
213 (setf (smallest-gap t1
)
214 (max (smallest-gap t1
)
215 (right-bulge e1 pane
)))))
216 ((eq (flexichain:flexi-next t1
)
218 (setf (smallest-gap t1
)
219 (max (smallest-gap t1
)
220 (+ (right-bulge e1 pane
)
221 (left-bulge e2 pane
)))))))))
223 ;;; ... Then we consider adjacent elements whose timelines are
224 ;;; separated by at least one other timeline. If the sum of the
225 ;;; distances between individual timelines is greater than or equal to
226 ;;; the closest distance between the adjacent elements (which is
227 ;;; likely if we are talking melody), then there is nothing to do,
228 ;;; since the individual distances are more restrictive than that
229 ;;; imposed by the adjacent elements. If not, we try to distribute
230 ;;; the closest distance between the two adjacent elements over the
231 ;;; individual timelines proportionally to the elasticity of the
232 ;;; timlines. If in doing so, we find that some timeline already has
233 ;;; a smallest gap that is larger than the fraction of the closest
234 ;;; distance between adjacent elements that we attribute to it, then
235 ;;; that smallest gap is subtracted from the distance we need to
236 ;;; distribute, the timeline is removed from consideration, and we
237 ;;; start over. This process must terminate (or else, the sum of the
238 ;;; closest gaps must have been larger than the distance to distribute
239 ;;; in the first place) with at least one timeline to distribute over.
240 ;;; There is a special case here, which occurs when all the
241 ;;; elasticites of the timelines to be considered is zero. In this
242 ;;; case, instead of distributing proportionally to the elasticities
243 ;;; of individual timelies, we distribute evenly between the timelines.
244 (defun compute-gaps-separated-timelines (bars method pane
)
245 (declare (ignore method
))
246 (flet ((handle-timelines (timelines element-gap
)
247 (let ((sum-gap (reduce #'+ timelines
:key
#'smallest-gap
))
248 (sum-elasticity (reduce #'+ timelines
:key
#'elasticity
)))
249 (unless (> sum-gap element-gap
)
250 (if (zerop sum-elasticity
)
251 (loop for timeline
= (find (/ element-gap
(length timelines
))
255 until
(null timeline
)
256 do
(decf element-gap
(smallest-gap timeline
))
257 do
(setf timelines
(remove timeline timelines
:test
#'eq
))
258 finally
(let ((gap (/ element-gap
(length timelines
))))
259 (loop for timeline in timelines
260 do
(setf (smallest-gap timeline
) gap
))))
261 (loop for timeline
= (let ((gap/elasticity
(/ element-gap sum-elasticity
)))
262 (find-if (lambda (timeline)
263 (> (smallest-gap timeline
)
264 (* (elasticity timeline
) gap
/elasticity
)))
266 until
(null timeline
)
267 do
(decf element-gap
(smallest-gap timeline
))
268 do
(decf sum-elasticity
(elasticity timeline
))
269 do
(setf timelines
(remove timeline timelines
:test
#'eq
))
270 finally
(let ((gap/elasticity
(/ element-gap sum-elasticity
)))
271 (loop for timeline in timelines
272 do
(setf (smallest-gap timeline
)
273 (* (elasticity timeline
) gap
/elasticity
))))))))))
274 (loop for bar in bars
275 do
(loop for
(e1 e2
) on
(elements bar
)
276 for t1
= (timeline e1
)
278 (unless (flexichain:flexi-last-p t1
)
279 (let ((timelines (loop for tl
= t1 then
(flexichain:flexi-next tl
)
281 until
(flexichain:flexi-last-p tl
))))
282 (handle-timelines timelines
(right-bulge e1 pane
)))))
283 ((not (eq (flexichain:flexi-next t1
)
285 (let ((timelines (loop for tl
= t1 then
(flexichain:flexi-next tl
)
286 until
(eq tl
(timeline e2
))
288 (handle-timelines timelines
(+ (right-bulge e1 pane
)
289 (left-bulge e2 pane
))))))))))
291 (defun compute-gaps (measures method pane
)
292 (loop for measure in measures
293 ;; initially, look only at adjacent elements whose
294 ;; corrsponding timelines are also adjacent, and at the last
295 ;; element of a bar, provided that its timeline is also the
296 ;; last one in the measure
297 do
(compute-gaps-adjacent-timelines (measure-bars measure
) method pane
)
299 ;; then look at adjacent elements whose corresponding
300 ;; timelines are NOT adjacent, or the last element of a bar
301 ;; whose corresponding timeline is not the last one in the meaure
302 do
(compute-gaps-separated-timelines (measure-bars measure
) method pane
)))
304 ;;; When this function is called, each timeline has an elasticity and
305 ;;; a smallest gap to the next adjacent timline (or to the end of the
306 ;;; measure). These values, together with an elasticity function at
307 ;;; the beginning of a measure, are used to compute the total
308 ;;; elasticity function of a measure.
309 (defun compute-elasticity-functions (measures method pane
)
310 (loop for measure in measures
311 do
(setf (prefix-elasticity-function measure
)
313 (max (min-width method
)
314 (if (zerop (flexichain:nb-elements
(timelines measure
)))
316 (loop for element in
(elements (flexichain:element
* (timelines measure
) 0))
317 maximize
(left-bulge element pane
))))))
318 (make-elementary-elasticity prefix-width
0.0001)))
319 do
(loop with result
= (prefix-elasticity-function measure
)
320 with timelines
= (timelines measure
)
321 for i from
0 below
(flexichain:nb-elements timelines
)
322 for timeline
= (flexichain:element
* timelines i
)
326 (make-elementary-elasticity (smallest-gap timeline
) (elasticity timeline
))))
327 finally
(setf (elasticity-function measure
) result
)))
328 (reduce #'add-elasticities measures
:key
#'elasticity-function
))
330 (defun single-whole-rest-in-bar-p (element)
331 (let* ((bar (bar element
))
332 (elements (elements bar
)))
333 (and (null (cdr elements
))
334 (typep element
'rest
)
335 (member (notehead element
) '(:long
:breve
:whole
)))))
337 (defun compute-measure-coordinates (measure x y force
)
338 (loop with timelines
= (timelines measure
)
339 for i from
0 below
(flexichain:nb-elements timelines
)
340 for timeline
= (flexichain:element
* timelines i
)
341 and xx
= (+ x
(size-at-force (prefix-elasticity-function measure
) force
))
342 then
(+ xx
(max (smallest-gap timeline
)
343 (* force
(elasticity timeline
))))
344 do
(loop for element in
(elements timeline
)
345 do
(setf (final-absolute-element-xoffset element
)
346 (if (single-whole-rest-in-bar-p element
)
347 (round (+ x
(/ (size-at-force (elasticity-function measure
) force
) 2)))
348 (round (+ xx
(score-pane:staff-step
(xoffset element
))))))))
349 (loop for bar in
(measure-bars measure
)
350 do
(compute-bar-coordinates bar x y
(size-at-force (elasticity-function measure
) force
))))
352 (defun draw-measure (pane measure
)
353 (loop for bar in
(measure-bars measure
) do
354 (if (gsharp-cursor::cursors
(slice bar
))
356 (score-pane:with-light-glyphs pane
(draw-bar pane bar
))))
357 (let ((first-bar (car (measure-bars measure
))))
358 (let ((x (final-absolute-measure-xoffset first-bar
))
359 (y (system-y-position first-bar
))
360 (width (final-width first-bar
))
361 (staves (staves (buffer (segment (layer (slice first-bar
)))))))
362 (score-pane:draw-bar-line pane
(+ x width
)
363 (+ y
(- (score-pane:staff-step
8)))
364 (+ y
(staff-yoffset (car (last staves
))))))))
366 (defun compute-system-coordinates (measures x y force
)
367 (loop for measure in measures
368 do
(compute-measure-coordinates measure x y force
)
369 do
(incf x
(size-at-force (elasticity-function measure
) force
))))
371 (defun draw-tie (pane bars n1 n2
)
372 ;; FIXME: we'll want to draw ties between (nothing) and n2 eventually
373 (declare (type note n1
) (type (or note null
) n2
))
374 (let ((x1 (+ (final-absolute-note-xoffset n1
) (score-pane:staff-step
1.5)))
375 (x2 (if (typep n2
'note
)
376 (- (final-absolute-note-xoffset n2
) (score-pane:staff-step
1.5))
377 (+ (final-absolute-note-xoffset n1
) (score-pane:staff-step
4.5))))
378 (pos (note-position n1
)))
379 (if (eq (final-stem-direction (cluster n1
)) :up
)
380 (score-pane:with-vertical-score-position
(pane (staff-yoffset (staff n1
)))
381 (if (gsharp-cursor::cursors
(slice (car bars
)))
382 (score-pane:draw-tie-down pane x1 x2
(if (oddp pos
) (1- pos
) pos
))
383 (score-pane:with-light-glyphs pane
384 (score-pane:draw-tie-down pane x1 x2
(if (oddp pos
) (1- pos
) pos
)))))
385 (score-pane:with-vertical-score-position
(pane (staff-yoffset (staff n1
)))
386 (if (gsharp-cursor::cursors
(slice (car bars
)))
387 (score-pane:draw-tie-up pane x1 x2
(if (oddp pos
) (1+ pos
) pos
))
388 (score-pane:with-light-glyphs pane
389 (score-pane:draw-tie-up pane x1 x2
(if (oddp pos
) (1+ pos
) pos
))))))))
391 (defun notes-tieable (n1 n2
)
392 (and (= (pitch n1
) (pitch n2
))
393 (eq (staff n1
) (staff n2
))
394 (eq (accidentals n1
) (accidentals n2
))))
396 ;;; draw the ties in BARS starting at BAR and at most LENGTH bars
397 (defun draw-ties (pane bars bar length
)
398 (loop until
(eq bar
(car bars
))
400 (score-pane:with-vertical-score-position
401 (pane (system-y-position (car bars
)))
402 (loop with elements
= (mapcan (lambda (bar) (copy-seq (elements bar
)))
403 (loop for bar in bars
406 for
(e1 e2
) on elements
407 do
(when (typep e1
'cluster
)
408 (loop for n1 in
(notes e1
)
409 do
(when (tie-right n1
)
410 (loop for n2 in
(and (typep e2
'cluster
) (notes e2
))
411 do
(when (and (tie-left n2
)
412 (notes-tieable n1 n2
))
413 (draw-tie pane bars n1 n2
)
415 finally
(draw-tie pane bars n1 nil
))))))))
417 (defun draw-system (pane measures
)
418 (with-new-output-record (pane)
419 (loop with length
= (length measures
)
420 for bar in
(measure-bars (car measures
))
421 do
(draw-ties pane
(bars (slice bar
)) bar length
))
422 (loop for measure in measures do
423 (draw-measure pane measure
))))
425 (defun draw-staves (pane staves measures x y right-edge
)
426 (loop for staff in staves do
427 (score-pane:with-vertical-score-position
428 (pane (+ y
(staff-yoffset staff
)))
429 (if (member staff
(staves (layer (slice (bar *cursor
*)))))
430 (draw-staff-and-clef pane staff measures x right-edge
)
431 (score-pane:with-light-glyphs pane
432 (draw-staff-and-clef pane staff measures x right-edge
))))))
434 (defun compute-and-draw-system (pane buffer staves measures method x y timesig-offset right-edge
)
435 (compute-elasticities measures method
)
436 (compute-gaps measures method pane
)
437 (let* ((e-fun (compute-elasticity-functions measures method pane
))
438 ;; FIXME: it would be much better to compress the system
439 ;; proportionally, so that every smallest gap gets shrunk
440 ;; by the same percentage
441 (force (if (> (zero-force-size e-fun
) (line-width method
))
443 (force-at-size e-fun
(line-width method
)))))
444 (compute-system-coordinates measures
445 (+ x
(left-offset buffer
) timesig-offset
) y
447 (draw-system pane measures
)
448 (score-pane:draw-bar-line pane x
449 (+ y
(- (score-pane:staff-step
8)))
450 (+ y
(staff-yoffset (car (last staves
)))))
451 (draw-staves pane staves measures x y right-edge
))
453 (defun compute-timesig-offset (staves measures
)
454 (max (* (score-pane:staff-step
2)
455 (loop for staff in staves
457 (if (typep staff
'fiveline-staff
)
458 (count :flat
(alterations (key-signature-for-staff staff measures
)))
460 (* (score-pane:staff-step
2.5)
461 (loop for staff in staves
463 (if (typep staff
'fiveline-staff
)
464 (count :sharp
(alterations (key-signature-for-staff staff measures
)))
467 (defun split (sequence n method
)
468 (labels ((sequence-size (start end
)
469 (natural-width method
470 (reduce (lambda (seq-cost element
)
471 (combine-cost method seq-cost element
))
472 sequence
:start start
:end end
473 :initial-value nil
)))
474 (split-aux (sequence start end n
)
476 (let ((width (sequence-size start end
)))
477 (values (list (subseq sequence start end
)) width width
))
478 (let* ((nn (floor n
2))
479 (m (floor (* (- end start
) nn
) n
)))
480 (multiple-value-bind (best-left minl maxl
)
481 (split-aux sequence start
(+ start m
) nn
)
482 (multiple-value-bind (best-right minr maxr
)
483 (split-aux sequence
(+ start m
) end
(- n nn
))
484 (let* ((best-min (min minl minr
))
485 (best-max (max maxl maxr
))
486 (best-cost (/ (- best-max best-min
) 2))
487 (best-splits (append best-left best-right
)))
488 (cond ((and (< minl minr
)
491 while
(and (< minl minr
)
493 (>= (- end start m
) (- n nn
)))
494 do
(multiple-value-bind (left new-minl new-maxl
)
495 (split-aux sequence start
(+ start m
) nn
)
496 (multiple-value-bind (right new-minr new-maxr
)
497 (split-aux sequence
(+ start m
) end
(- n nn
))
502 (let ((cost (/ (- (max maxl maxr
) (min minl minr
)) 2)))
503 (when (< cost best-cost
)
504 (setf best-min
(min minl minr
)
505 best-max
(max maxl maxr
)
507 best-splits
(append left right
))))))))
511 while
(and (> minl minr
)
514 do
(multiple-value-bind (left new-minl new-maxl
)
515 (split-aux sequence start
(+ start m
) nn
)
516 (multiple-value-bind (right new-minr new-maxr
)
517 (split-aux sequence
(+ start m
) end
(- n nn
))
522 (let ((cost (/ (- (max maxl maxr
) (min minl minr
)) 2)))
523 (when (< cost best-cost
)
524 (setf best-min
(min minl minr
)
525 best-max
(max maxl maxr
)
527 best-splits
(append left right
)))))))))
528 (values best-splits best-min best-max
))))))))
529 (split-aux sequence
0 (length sequence
) n
)))
531 (defun layout-page (measures n method
)
532 (if (<= (length measures
) n
)
533 (mapcar #'list measures
)
534 (split measures n method
)))
536 (defmacro dopages
((measures buffer
) &body body
)
537 `(gsharp-measure::new-map-over-obseq-subsequences
538 (lambda (,measures
) ,@body
)
541 (defun cursor-in-measures-p (cursor measures
)
542 (member-if (lambda (measure) (member (bar cursor
) (measure-bars measure
)
546 (defun method-for-timesig (method timesig-offset
)
547 (make-measure-cost-method (min-width method
) (spacing-style method
)
548 (- (line-width method
) timesig-offset
)
549 (lines-per-page method
)))
551 (defun draw-page (pane buffer x y staves maxmethod page-measures
)
552 (let* ((systems-per-page (gsharp-measure::systems-per-page buffer
))
553 (measure-seqs (layout-page page-measures systems-per-page maxmethod
)))
554 (dolist (measures measure-seqs
)
555 (let* ((toffset (compute-timesig-offset staves measures
))
556 (method (method-for-timesig (buffer-cost-method buffer
) toffset
)))
557 (compute-and-draw-system pane buffer staves measures method
558 x y toffset
(right-edge buffer
))
559 (incf y
(+ 20 (* 70 (length staves
))))))))
561 (defmethod draw-buffer (pane (buffer buffer
) *cursor
* x y
)
562 (score-pane:with-staff-size
(gsharp-buffer::rastral-size buffer
)
563 (let* ((staves (staves buffer
))
564 (max-timesig-offset (* (score-pane:staff-step
2.5) 7))
565 (method (method-for-timesig
566 (buffer-cost-method buffer
) max-timesig-offset
)))
567 (loop for staff in staves
568 for offset from
0 by
70 do
569 (setf (staff-yoffset staff
) offset
))
570 (dopages (page-measures buffer
)
571 (when (cursor-in-measures-p *cursor
* page-measures
)
572 (draw-page pane buffer x y staves method page-measures
))))))
574 (defmethod print-buffer (pane (buffer buffer
) *cursor
* x y
)
575 (score-pane:with-staff-size
(gsharp-buffer::rastral-size buffer
)
576 (let* ((staves (staves buffer
))
577 (max-timesig-offset (* (score-pane:staff-step
2.5) 7))
578 (method (method-for-timesig
579 (buffer-cost-method buffer
) max-timesig-offset
)))
580 (loop for staff in staves
581 for offset from
0 by
70 do
582 (setf (staff-yoffset staff
) offset
))
584 (dopages (page-measures buffer
)
587 (draw-page pane buffer x y staves method page-measures
)
588 (setq first nil
))))))
590 (define-stealth-mixin xelement
() element
591 ((final-absolute-xoffset :accessor final-absolute-element-xoffset
)))
593 (define-stealth-mixin velement
() melody-element
594 (;; the position, in staff steps, of the end of the stem
595 ;; that is not attached to a note, independent of the
596 ;; staff on which it is located
597 (final-stem-position :accessor final-stem-position
)
598 ;; the yoffset of the staff relative to which the end of the
600 (final-stem-yoffset :initform
0 :accessor final-stem-yoffset
)
601 ;; the yoffset of the staff that contains the top note of
603 (top-note-staff-yoffset :accessor top-note-staff-yoffset
)
604 ;; the yoffset of the staff that contains the bottom note of
606 (bot-note-staff-yoffset :accessor bot-note-staff-yoffset
)))
608 (define-stealth-mixin welement
() lyrics-element
611 ;;; Compute and store several important pieces of information
612 ;;; about an element:
613 ;;; * the y-offset of the staff containing the top note.
614 ;;; * the y-offset of the staff containing the bottom note.
615 (defun compute-top-bot-yoffset (element)
616 (if (and (typep element
'cluster
) (notes element
))
617 (let ((top-note (top-note (notes element
)))
618 (bot-note (bot-note (notes element
))))
619 (setf (bot-note-staff-yoffset element
) (staff-yoffset (staff bot-note
))
620 (top-note-staff-yoffset element
) (staff-yoffset (staff top-note
))))
621 (setf ;; clearly wrong. should be taken from element or layer.
622 (top-note-staff-yoffset element
) 0
623 (bot-note-staff-yoffset element
) 0)))
625 (defun compute-stem-length (element)
626 (let* ((top-note-pos (top-note-pos element
))
627 (bot-note-pos (bot-note-pos element
))
628 (top-note (reduce (lambda (n1 n2
)
629 (cond ((< (staff-yoffset (staff n1
))
630 (staff-yoffset (staff n2
)))
632 ((> (staff-yoffset (staff n1
))
633 (staff-yoffset (staff n2
)))
635 ((> (note-position n1
)
640 (bot-note (reduce (lambda (n1 n2
)
641 (cond ((> (staff-yoffset (staff n1
))
642 (staff-yoffset (staff n2
)))
644 ((< (staff-yoffset (staff n1
))
645 (staff-yoffset (staff n2
)))
647 ((< (note-position n1
)
652 (length (if (eq (final-stem-direction element
) :up
)
653 (cond ((<= top-note-pos -
3) (- 4 top-note-pos
))
654 ((<= top-note-pos
3) 7)
655 ((= top-note-pos
4) 6)
657 (cond ((>= bot-note-pos
11) (- bot-note-pos
4))
658 ((>= bot-note-pos
4) 7)
659 ((= bot-note-pos
3) 6)
661 (nb-flags (max (rbeams element
) (lbeams element
))))
663 (setf length
(max length
664 (+ (if (eq (final-stem-direction element
) :up
) 6 7)
665 (* 2 (max 0 (- nb-flags
2)))))))
666 (setf (final-stem-yoffset element
)
667 (staff-yoffset (staff (if (eq (final-stem-direction element
) :up
)
668 top-note bot-note
))))
669 (setf (final-stem-position element
)
670 (if (eq (final-stem-direction element
) :up
)
671 (+ top-note-pos length
)
672 (- bot-note-pos length
)))))
674 ;;; the dominating note among a bunch of notes is the
675 ;;; one that is closest to the beam, i.e. the one
676 ;;; the one that is closest to the end of the stem that
677 ;;; is not attached to a notehead.
678 (defun dominating-note (notes stem-direction
)
679 (reduce (lambda (n1 n2
)
680 (let ((yoff1 (staff-yoffset (staff n1
)))
681 (yoff2 (staff-yoffset (staff n2
))))
682 (if (eq stem-direction
:up
)
687 (if (> (pitch n1
) (pitch n2
)) n1 n2
)))
692 (if (< (pitch n1
) (pitch n2
)) n1 n2
))))))
695 (defun cluster-p (element)
696 (typep element
'cluster
))
698 (defun map-over-cluster-pairs (fun list
)
699 (loop for sublist on list
700 do
(when (cluster-p (car sublist
))
701 (let ((second (find-if #'cluster-p
(cdr sublist
))))
703 (funcall fun
(car sublist
) second
))))))
705 (defun draw-beam-group (pane elements
)
706 (let ((e (car elements
)))
707 (when (typep e
'gsharp-buffer
::staffwise-element
)
708 (assert (null (cdr elements
)))
709 (return-from draw-beam-group
710 (draw-element pane e
(final-absolute-element-xoffset e
)))))
711 (mapc #'compute-top-bot-yoffset elements
)
712 (if (null (cdr elements
))
713 (let ((element (car elements
)))
714 (when (or (typep element
'rest
) (notes element
))
715 (when (non-empty-cluster-p element
)
716 (compute-stem-length element
))
717 (draw-element pane element
)))
718 (let* ((stem-direction (final-stem-direction (car elements
)))
720 (loop for element in elements
721 when
(non-empty-cluster-p element
)
722 collect
(dominating-note (notes element
) stem-direction
)))
724 (staff (dominating-note dominating-notes stem-direction
)))
725 (positions (mapcar (lambda (n)
726 (if (eq (staff n
) dominating-staff
)
728 (if (eq stem-direction
:up
) -
1000 1000)))
730 (x-positions (mapcar (lambda (element)
731 (/ (final-absolute-element-xoffset element
) (score-pane:staff-step
1)))
733 (nb-beams (mapcar (lambda (element)
734 (max (lbeams element
) (rbeams element
)))
736 (beaming (beaming-single (mapcar #'list positions x-positions nb-beams
) stem-direction
))
737 (max-nb-beams (reduce #'max nb-beams
))
738 (min-nb-beams (reduce #'min nb-beams
)))
739 (destructuring-bind ((ss1 . offset1
) (ss2 . offset2
)) beaming
740 (let* ((y1 (+ ss1
(* 1/2 offset1
)))
741 (y2 (+ ss2
(* 1/2 offset2
)))
742 (x1 (final-absolute-element-xoffset (car elements
)))
743 (x2 (final-absolute-element-xoffset (car (last elements
))))
744 (slope (/ (- y2 y1
) (- x2 x1
))))
745 (if (eq stem-direction
:up
)
746 (loop for element in elements do
747 (setf (final-stem-position element
)
748 (+ y1
(* slope
(- (final-absolute-element-xoffset element
) x1
))))
749 (setf (final-stem-yoffset element
)
750 (staff-yoffset dominating-staff
)))
751 (loop for element in elements do
752 (setf (final-stem-position element
)
753 (+ y1
(* slope
(- (final-absolute-element-xoffset element
) x1
))))
754 (setf (final-stem-yoffset element
)
755 (staff-yoffset dominating-staff
)))))
756 (score-pane:with-vertical-score-position
(pane (staff-yoffset dominating-staff
))
757 (if (eq stem-direction
:up
)
758 (score-pane:with-notehead-right-offsets
(right up
)
759 (declare (ignore up
))
760 (loop repeat min-nb-beams
763 do
(score-pane:draw-beam pane
764 (+ (final-absolute-element-xoffset (car elements
)) right
) (- ss1 ss
) (+ offset1 offset
)
765 (+ (final-absolute-element-xoffset (car (last elements
))) right
) (- ss2 ss
) (+ offset2 offset
)))
766 (let ((region +nowhere
+))
767 (loop for beams from
(1+ min-nb-beams
) to max-nb-beams
768 for ss from
(* 2 min-nb-beams
) by
2
769 for offset from min-nb-beams
770 do
(map-over-cluster-pairs
772 (cond ((and (>= (rbeams e1
) beams
) (>= (lbeams e2
) beams
))
775 (make-rectangle* (+ (final-absolute-element-xoffset e1
) right
) -
10000
776 (+ (final-absolute-element-xoffset e2
) right
) 10000))))
777 ((>= (rbeams e1
) beams
)
780 (make-rectangle* (+ (final-absolute-element-xoffset e1
) right
) -
10000
781 (+ (final-absolute-element-xoffset e1
) right
(score-pane:staff-step
2)) 10000))))
782 ((>= (lbeams e2
) beams
)
785 (make-rectangle* (+ (final-absolute-element-xoffset e2
) right
(score-pane:staff-step -
2)) -
10000
786 (+ (final-absolute-element-xoffset e2
) right
) 10000))))
789 (with-drawing-options (pane :clipping-region region
)
790 (score-pane:draw-beam pane
791 (+ (final-absolute-element-xoffset (car elements
)) right
) (- ss1 ss
) (+ offset1 offset
)
792 (+ (final-absolute-element-xoffset (car (last elements
))) right
) (- ss2 ss
) (+ offset2 offset
))))))
793 (score-pane:with-notehead-left-offsets
(left down
)
794 (declare (ignore down
))
795 (loop repeat min-nb-beams
798 do
(score-pane:draw-beam pane
799 (+ (final-absolute-element-xoffset (car elements
)) left
) (+ ss1 ss
) (- offset1 offset
)
800 (+ (final-absolute-element-xoffset (car (last elements
))) left
) (+ ss2 ss
) (- offset2 offset
)))
801 (let ((region +nowhere
+))
802 (loop for beams from
(1+ min-nb-beams
) to max-nb-beams
803 for ss from
(* 2 min-nb-beams
) by
2
804 for offset from min-nb-beams
805 do
(map-over-cluster-pairs
807 (cond ((and (>= (rbeams e1
) beams
) (>= (lbeams e2
) beams
))
810 (make-rectangle* (+ (final-absolute-element-xoffset e1
) left
) -
10000
811 (+ (final-absolute-element-xoffset e2
) left
) 10000))))
812 ((>= (rbeams e1
) beams
)
815 (make-rectangle* (+ (final-absolute-element-xoffset e1
) left
) -
10000
816 (+ (final-absolute-element-xoffset e1
) left
(score-pane:staff-step
2)) 10000))))
817 ((>= (lbeams e2
) beams
)
820 (make-rectangle* (+ (final-absolute-element-xoffset e2
) left
(score-pane:staff-step -
2)) -
10000
821 (+ (final-absolute-element-xoffset e2
) left
) 10000))))
824 (with-drawing-options (pane :clipping-region region
)
825 (score-pane:draw-beam pane
826 (+ (final-absolute-element-xoffset (car elements
)) left
) (+ ss1 ss
) (- offset1 offset
)
827 (+ (final-absolute-element-xoffset (car (last elements
))) left
) (+ ss2 ss
) (- offset2 offset
))))))))
828 (loop for element in elements do
829 (draw-element pane element nil
))))))
831 (defun draw-the-cursor (pane cursor cursor-element last-note
)
832 (let* ((staff (car (staves (layer cursor
))))
834 (sy (system-y-position bar
))
835 (yoffset (- (gsharp-drawing::staff-yoffset staff
))))
836 (let ((region (pane-viewport-region pane
)))
838 ;; FIXME: adjusting the viewport at this point leads to ugly
839 ;; jumps in the display when going across pages, as the page
840 ;; is first laid out and drawn, then the viewport is moved.
841 ;; If we instead cleared the pane, laid out the page, adjusted
842 ;; the viewport, and finally drew the page (and cursor) then
843 ;; that jump would probably go away.
845 ;; FIXME: this calculation only takes account of the centre of
846 ;; the cursor. Refactor this whole DRAW-THE-CURSOR function
847 ;; so that it's easy to take account of the vertical extent of
849 (unless (< (bounding-rectangle-min-y region
)
851 (bounding-rectangle-max-y region
))
852 (let ((maxy (- (bounding-rectangle-max-y pane
) (bounding-rectangle-height region
))))
853 (scroll-extent pane
0 (max 0 (min maxy
854 (- sy
(floor (bounding-rectangle-height region
) 2)))))))))
856 (flet ((draw-cursor (x)
857 (if (typep staff
'fiveline-staff
)
858 (let* ((clef (clef staff
))
859 (bottom-line (bottom-line clef
))
860 (lnote-offset (score-pane:staff-step
(- last-note bottom-line
))))
862 x
(+ sy
(- (+ (score-pane:staff-step
12) yoffset
)))
863 x
(+ sy
(- (+ (score-pane:staff-step -
4) yoffset
)))
866 (- x
1) (+ sy
(- (+ (score-pane:staff-step -
3.4) yoffset lnote-offset
)))
867 (- x
1) (+ sy
(- (+ (score-pane:staff-step
3.6) yoffset lnote-offset
)))
870 (+ x
1) (+ sy
(- (+ (score-pane:staff-step -
3.4) yoffset lnote-offset
)))
871 (+ x
1) (+ sy
(- (+ (score-pane:staff-step
3.6) yoffset lnote-offset
)))
873 (progn (draw-line* pane
874 (+ x
1) (+ sy
(- (+ (score-pane:staff-step
2) yoffset
)))
875 (+ x
1) (+ sy
(- (+ (score-pane:staff-step -
2) yoffset
)))
878 (- x
1) (+ sy
(- (+ (score-pane:staff-step
2) yoffset
)))
879 (- x
1) (+ sy
(- (+ (score-pane:staff-step -
2) yoffset
)))
881 (score-pane:with-staff-size
(gsharp-buffer::rastral-size
(buffer cursor
))
882 (let* ((x (final-absolute-measure-xoffset bar
))
883 (width (final-width bar
))
884 (elements (elements bar
)))
885 (if (null cursor-element
)
886 (draw-cursor (/ (+ (if (null elements
)
888 (final-absolute-element-xoffset (car (last elements
))))
890 (loop for element in elements
891 and xx
= x then
(final-absolute-element-xoffset element
) do
892 (when (eq element cursor-element
)
893 (draw-cursor (/ (+ xx
(final-absolute-element-xoffset element
)) 2))))))))))
895 (defun compute-bar-coordinates (bar x y width
)
896 (setf (system-y-position bar
) y
897 (final-absolute-measure-xoffset bar
) x
898 (final-width bar
) width
))
900 (defmethod draw-bar (pane (bar melody-bar
))
901 (score-pane:with-vertical-score-position
902 (pane (system-y-position bar
))
903 (loop for group in
(beam-groups (elements bar
))
904 do
(draw-beam-group pane group
))))
906 (defmethod draw-bar (pane (bar lyrics-bar
))
907 (score-pane:with-vertical-score-position
908 (pane (system-y-position bar
))
909 (let ((elements (elements bar
)))
910 (loop for element in elements
911 do
(draw-element pane element
)))))
913 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
917 (defgeneric draw-element
(pane element
&optional flags
))
919 (defmethod draw-element :around
(pane element
&optional flags
)
921 (dolist (annotation (annotations element
))
922 (draw-element-annotation pane element annotation
)))
924 (defgeneric draw-element-annotation
(pane element annotation
)
925 (:method
(pane element annotation
)
926 (warn "unknown annotation ~S for ~S" annotation element
)))
928 ;;; FIXME: these methods work and have the right vertical behaviour;
929 ;;; the horizontal centering of the dot and the tenuto mark are all
931 (defmethod draw-element-annotation
932 (pane (element cluster
) (annotation (eql :staccato
)))
933 (let ((direction (final-stem-direction element
))
934 (x (final-absolute-element-xoffset element
)))
935 (if (eq direction
:up
)
936 (score-pane:with-vertical-score-position
(pane (bot-note-staff-yoffset element
))
937 (score-pane:with-notehead-right-offsets
(dx dy
)
938 (score-pane:with-notehead-left-offsets
(ddx ddy
)
939 (let ((pos (- (bot-note-pos element
) 2)))
940 (when (and (<= 0 pos
) (evenp pos
))
942 (score-pane:draw-dot pane
(+ x
(/ (+ dx ddx
) 2)) pos
)))))
943 (score-pane:with-vertical-score-position
(pane (top-note-staff-yoffset element
))
944 (score-pane:with-notehead-right-offsets
(dx dy
)
945 (score-pane:with-notehead-left-offsets
(ddx ddy
)
946 (let ((pos (+ (top-note-pos element
) 2)))
947 (when (and (<= pos
8) (evenp pos
))
949 (score-pane:draw-dot pane
(+ x
(/ (+ dx ddx
) 2)) pos
))))))))
951 (defmethod draw-element-annotation
952 (pane (element cluster
) (annotation (eql :tenuto
)))
953 (let ((direction (final-stem-direction element
))
954 (x (final-absolute-element-xoffset element
)))
955 (if (eq direction
:up
)
956 (score-pane:with-vertical-score-position
(pane (bot-note-staff-yoffset element
))
957 (score-pane:with-notehead-right-offsets
(dx dy
)
958 (score-pane:with-notehead-left-offsets
(ddx ddy
)
959 (let ((pos (- (bot-note-pos element
) 2)))
960 (when (and (<= 0 pos
) (evenp pos
))
962 (draw-rectangle* pane
(+ x ddx
) (1- (score-pane:staff-step
(- pos
)))
963 (+ x dx
) (1+ (score-pane:staff-step
(- pos
))))))))
964 (score-pane:with-vertical-score-position
(pane (top-note-staff-yoffset element
))
965 (score-pane:with-notehead-right-offsets
(dx dy
)
966 (score-pane:with-notehead-left-offsets
(ddx ddy
)
967 (let ((pos (+ (bot-note-pos element
) 2)))
968 (when (and (<= pos
8) (evenp pos
))
970 (draw-rectangle* pane
(+ x ddx
) (1- (score-pane:staff-step
(- pos
)))
971 (+ x dx
) (1+ (score-pane:staff-step
(- pos
)))))))))))
973 (defmethod note-difference ((note1 note
) (note2 note
))
974 (- (pitch note1
) (pitch note2
)))
976 (defun draw-ledger-lines (pane x notes
)
977 (score-pane:with-vertical-score-position
(pane (staff-yoffset (staff (car notes
))))
978 (let* ((positions (mapcar #'note-position notes
))
979 (top-note-pos (reduce #'max positions
))
980 (bot-note-pos (reduce #'min positions
)))
981 (loop for pos from
10 to top-note-pos by
2
982 do
(score-pane:draw-ledger-line pane x pos
))
983 (loop for pos from -
2 downto bot-note-pos by
2
984 do
(score-pane:draw-ledger-line pane x pos
)))))
986 (defun flags-drawn-p (element)
987 (let ((nb (max (rbeams element
) (lbeams element
))))
988 (and (> nb
0) (eq (notehead element
) :filled
) nb
)))
990 (defun draw-flags (pane element x direction pos
)
991 (let ((nb (flags-drawn-p element
)))
993 (if (eq direction
:up
)
994 (score-pane:with-notehead-right-offsets
(right up
)
995 (declare (ignore up
))
996 (score-pane:draw-flags-down pane nb
(+ x right
) pos
))
997 (score-pane:with-notehead-left-offsets
(left down
)
998 (declare (ignore down
))
999 (score-pane:draw-flags-up pane nb
(+ x left
) pos
))))))
1001 (defun draw-dots (pane nb-dots x dot-xoffset dot-pos
)
1003 (let ((staff-step (score-pane:staff-step
1)))
1004 (loop repeat nb-dots
1005 for xx from dot-xoffset by staff-step do
1006 (score-pane:draw-dot pane xx dot-pos
)))))
1008 (defun draw-note (pane note notehead nb-dots x pos dot-xoffset dot-pos
)
1009 (score-pane:with-vertical-score-position
(pane (staff-yoffset (staff note
)))
1010 (score-pane:draw-notehead pane notehead x pos
)
1011 (when (final-accidental note
)
1012 (score-pane:draw-accidental pane
(final-accidental note
) (final-absolute-accidental-xoffset note
) pos
))
1013 (draw-dots pane nb-dots x dot-xoffset dot-pos
)))
1015 (defparameter *main-selected-note-colour
* +blue-violet
+)
1016 (defun draw-notes (pane notes dots notehead dot-xoffset
)
1017 (loop for note in notes
1019 (with-drawing-options (pane :ink
(if (and (gsharp::cur-notep
)
1020 (eq note
(gsharp::cur-note
))
1021 (not (typep pane
'clim-postscript
::postscript-stream
)))
1022 *main-selected-note-colour
* +black
+))
1023 (draw-note pane note notehead dots
(final-absolute-note-xoffset note
) (note-position note
) dot-xoffset
(final-absolute-dot-ypos note
)))))
1025 (defun element-has-suspended-notes (element)
1026 (not (apply #'= (mapcar #'final-relative-note-xoffset
(notes element
)))))
1028 ;;; draw a cluster. The stem direction and the stem position have
1029 ;;; already been computed.
1030 ;;; 1. Group notes by staff.
1031 ;;; 2. Draw the notes in each group
1032 ;;; 3. If necessary, draw ledger lines for notes in a group
1033 ;;; 4. Draw the stem, if any
1034 (defmethod draw-element (pane (element cluster
) &optional
(flags t
))
1035 (with-new-output-record (pane)
1036 (unless (null (notes element
))
1037 (let* ((direction (final-stem-direction element
))
1038 (stem-pos (final-stem-position element
))
1039 (stem-yoffset (final-stem-yoffset element
))
1040 (groups (group-notes-by-staff (notes element
)))
1041 (x (final-absolute-element-xoffset element
))
1043 (let ((basic-xoffset (+ (score-pane:staff-step
2)
1044 (reduce #'max
(mapcar #'final-absolute-note-xoffset
(notes element
))))))
1045 (if (and flags
(eq direction
:up
) (flags-drawn-p element
))
1046 (max basic-xoffset
(+ (score-pane:staff-step
4) x
))
1049 (score-pane:with-vertical-score-position
(pane stem-yoffset
)
1050 (draw-flags pane element x direction stem-pos
)))
1051 (loop for group in groups do
1052 (draw-notes pane group
(dots element
) (notehead element
) dot-xoffset
)
1053 (draw-ledger-lines pane x group
))
1054 (unless (member (notehead element
) '(:whole
:breve
))
1055 (if (eq direction
:up
)
1056 (score-pane:draw-right-stem
1058 (- (bot-note-staff-yoffset element
) (score-pane:staff-step
(bot-note-pos element
)))
1059 (- stem-yoffset
(score-pane:staff-step stem-pos
)))
1060 (score-pane:draw-left-stem
1062 (- (top-note-staff-yoffset element
) (score-pane:staff-step
(top-note-pos element
)))
1063 (- stem-yoffset
(score-pane:staff-step stem-pos
)))))))))
1065 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1069 (defmethod draw-element (pane (element rest
) &optional
(flags t
))
1070 (declare (ignore flags
))
1071 (let ((x (final-absolute-element-xoffset element
)))
1072 (score-pane:with-vertical-score-position
(pane (staff-yoffset (staff element
)))
1073 (score-pane:draw-rest pane
(undotted-duration element
) x
(staff-pos element
))
1074 (draw-dots pane
(dots element
) x
(+ x
(score-pane:staff-step
2)) (1+ (staff-pos element
))))))
1076 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1080 (defmethod draw-element (pane (element lyrics-element
) &optional
(flags t
))
1081 (declare (ignore flags
))
1082 (let ((x (final-absolute-element-xoffset element
)))
1083 (score-pane:with-vertical-score-position
(pane (staff-yoffset (staff element
)))
1084 (with-text-family (pane :serif
)
1085 (draw-text* pane
(map 'string
'code-char
(text element
))
1086 x
0 :align-x
:center
)))))
1088 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1090 ;;; Key signature element
1092 (defmethod draw-element (pane (keysig key-signature
) &optional flags
)
1093 (declare (ignore flags
))
1094 (let ((staff (staff keysig
))
1095 (old-keysig (keysig keysig
))
1096 (x (final-absolute-element-xoffset keysig
)))
1097 (score-pane:with-vertical-score-position
(pane (staff-yoffset staff
))
1098 (let ((yoffset (b-position (clef staff
))))
1099 (loop with advance
= 0
1100 for pitch in
'(6 2 5 1 4 0 3)
1101 for line in
'(0 3 -
1 2 -
2 1 -
3)
1102 when
(and (eq (aref (alterations old-keysig
) pitch
) :flat
)
1103 (not (eq (aref (alterations keysig
) pitch
)
1105 do
(score-pane:draw-accidental
1106 pane
:natural
(+ x advance
) (+ line yoffset
))
1107 and do
(incf advance
(score-pane:staff-step
2))
1108 finally
(incf x
(if (= advance
0) 0 (+ advance
(score-pane:staff-step
2))))))
1109 (let ((yoffset (f-position (clef staff
))))
1110 (loop with advance
= 0
1111 for pitch in
'(3 0 4 1 5 2 6)
1112 for line in
'(0 -
3 1 -
2 -
5 -
1 -
4)
1113 when
(and (eq (aref (alterations old-keysig
) pitch
) :sharp
)
1114 (not (eq (aref (alterations keysig
) pitch
) :sharp
)))
1115 do
(score-pane:draw-accidental pane
:natural
(+ x advance
) (+ line yoffset
))
1116 and do
(incf advance
(score-pane:staff-step
2))
1117 finally
(incf x
(if (= advance
0) 0 (+ advance
(score-pane:staff-step
2))))))
1119 (let ((yoffset (b-position (clef staff
))))
1120 (loop for pitch in
'(6 2 5 1 4 0 3)
1121 for line in
'(0 3 -
1 2 -
2 1 -
3)
1122 for x from x by
(score-pane:staff-step
2)
1123 while
(eq (aref (alterations keysig
) pitch
) :flat
)
1124 do
(score-pane:draw-accidental pane
:flat x
(+ line yoffset
))))
1125 (let ((yoffset (f-position (clef staff
))))
1126 (loop for pitch in
'(3 0 4 1 5 2 6)
1127 for line in
'(0 -
3 1 -
2 -
5 -
1 -
4)
1128 for x from x by
(score-pane:staff-step
2.5)
1129 while
(eq (aref (alterations keysig
) pitch
) :sharp
)
1130 do
(score-pane:draw-accidental pane
:sharp x
(+ line yoffset
)))))))
1132 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
1134 ;;; Time signature element
1136 (defmethod draw-element (pane (timesig time-signature
) &optional
(flags t
))
1137 (declare (ignore flags
))
1138 (let ((staff (staff timesig
))
1139 (x (final-absolute-element-xoffset timesig
)))
1140 (score-pane:with-vertical-score-position
(pane (staff-yoffset staff
))
1141 (dolist (component (time-signature-components timesig
))
1142 (score-pane:draw-time-signature-component pane component x
)))))