2 ;;; Several of these functions are very flaky WRT EOF, and that should
3 ;;; eventually be fixed. This is all just a quick hack. Most of this
4 ;;; could be converted to a very data-driven style of programming.
6 ;;; Other things that should be checked/fixed:
7 ;;; - durations should get tweaked (say, by parse-music-section) if
8 ;;; we're inside a triplet or tuplet figure.
9 ;;; - haven't figured out yet who should deal with specifying an
10 ;;; initial tempo if we don't find one before the first note. I
11 ;;; have a feeling I should just have this code insert a tempo
12 ;;; set event on any channel where we get a duration-dependant
13 ;;; event before any tempo is set.
15 ;;; (an abashed) Julian Squires <tek@wiw.org> / 2004
20 ;;;; CONSTANTS AND PARAMETERS.
22 (defparameter *channel-select-characters
* "ABCDEFGHIJ")
23 (defparameter *duration-digits
* "0123456789")
24 (defparameter *note-characters
* "c_d_ef_g_a_b")
25 (defparameter *whitespace-characters
* #(#\Space
#\Newline
#\|
))
27 (defconstant +octave-size
+ 12)
29 (defparameter *staccato-base-division
* 1/8)
30 (defparameter *default-duration
* (make-duration 4))
31 (defparameter *default-octave
* 4)
32 (defparameter *default-staccato
* 1)
33 (defparameter *default-tempo
* 120)
36 ;;;; CLASSES AND DATA STRUCTURES.
39 ((denominator :reader duration-denominator
)
40 ;; other modifiers here
41 (dots :reader duration-dots
)))
43 (defun make-duration (denominator &optional
(dots 0))
45 (let ((duration (make-instance 'duration
)))
46 (setf (slot-value duration
'denominator
) denominator
)
47 (setf (slot-value duration
'dots
) dots
)
50 (defmethod print-object ((obj duration
) stream
)
51 (print-unreadable-object (obj stream
:type t
)
52 (princ (duration-denominator obj
) stream
)
53 (dotimes (i (duration-dots obj
))
57 (defclass music-command
()
58 ((type :reader music-command-type
)
59 (value :reader music-command-value
)))
61 (defun make-tempo-command (tempo)
62 (let ((cmd (make-instance 'music-command
)))
63 (setf (slot-value cmd
'type
) :tempo
)
64 (setf (slot-value cmd
'value
) tempo
)
67 (defun make-staccato-command (staccato)
68 (let ((cmd (make-instance 'music-command
)))
69 (setf (slot-value cmd
'type
) :staccato
)
70 (setf (slot-value cmd
'value
) staccato
)
73 ;; This might become a special macro-command later.
74 (defun make-arpeggio-command (n)
75 (let ((cmd (make-instance 'music-command
)))
76 (setf (slot-value cmd
'type
) :arpeggio
)
77 (setf (slot-value cmd
'value
) n
)
81 (defclass note
(music-command)
82 ((tone :reader note-tone
)
83 (duration :reader note-duration
))
84 (:documentation
"Notes encapsulate an absolute pitch (the TONE slot)
85 and a relative length (the DURATION slot). DURATION is relative to
86 the current channel tempo."))
88 (defun make-note (tone duration
)
89 (let ((note (make-instance 'note
)))
90 (setf (slot-value note
'type
) :note
)
91 (setf (slot-value note
'tone
) tone
)
92 (setf (slot-value note
'duration
) duration
)
95 (defmethod print-object ((obj note
) stream
)
96 (print-unreadable-object (obj stream
:type t
)
97 (princ (note-tone obj
) stream
)
98 (princ #\Space stream
)
99 (princ (note-duration obj
) stream
)))
103 ((octave :accessor channel-octave
)
104 (tempo :accessor channel-tempo
)
105 (staccato :accessor channel-staccato
)
106 (duration :accessor channel-default-duration
)
107 (loop-point :accessor channel-loop-point
)
108 (data-stream :accessor channel-data-stream
)))
110 (defun make-channel ()
111 (let ((channel (make-instance 'channel
)))
112 (setf (channel-octave channel
) *default-octave
*)
113 (setf (channel-tempo channel
) *default-tempo
*)
114 (setf (channel-staccato channel
) *default-staccato
*)
115 (setf (channel-default-duration channel
) *default-duration
*)
116 (setf (channel-data-stream channel
) nil
)
121 ;;;; LOW-LEVEL PARSE/LEX ROUTINES.
123 (defun digit-to-int (char)
124 (- (char-code char
) (char-code #\
0)))
126 (defun clarify-duration (duration channel
)
128 (setf (channel-default-duration channel
) duration
)
129 (channel-default-duration channel
)))
131 (defun expect-int (stream)
132 ;; if the next character is a digit, read digits until the next
133 ;; character is not a digit.
134 (do ((next-char #1=(peek-char nil stream
) #1#)
136 ((not (find next-char
*duration-digits
*)) int
)
137 (let ((digit (digit-to-int (read-char stream
))))
139 (setf int
(+ (* int
10) digit
))
142 (defun expect-duration (stream)
143 (let ((duration (make-duration (expect-int stream
)))
144 ;; if the next character is a dot, read dots until the next
145 ;; character is not a dot.
146 (dots (do ((next-char #2=(peek-char nil stream
) #2#)
147 (number-of-dots 0 (1+ number-of-dots
)))
148 ((char/= next-char
#\.
) number-of-dots
)
149 (read-char stream
))))
152 (setf (slot-value duration
'dots
) dots
))
155 (defun calculate-tone (char accidentals octave
)
156 (let ((tone-value (* +octave-size
+ octave
)))
159 ((char= char
(schar *note-characters
* i
)) i
)
160 (assert (< i
(length *note-characters
*)))))
161 (incf tone-value accidentals
)
164 (defun read-accidentals (stream)
165 (do ((next-char #1=(peek-char nil stream
) #1#)
167 ((char/= next-char
#\
+ #\-
) accidentals
)
168 (if (char= (read-char stream
) #\
+)
170 (decf accidentals
))))
172 (defun expect-note (stream)
173 (let* ((note-char (read-char stream
))
174 (accidentals (read-accidentals stream
))
175 (duration (expect-duration stream
)))
177 ;; this function should always be called when we know there's a
178 ;; note character next.
179 (assert (find note-char
*note-characters
*))
181 (values note-char accidentals duration
)))
183 (defun expect-rest (stream)
184 (let ((rest-char (read-char stream
))
185 (duration (expect-duration stream
)))
187 (if (char= rest-char
#\r)
188 (values :rest duration
)
189 (values :wait duration
))))
191 (defun expect-channels (stream)
192 (do ((next-char #1=(peek-char nil stream
) #1#)
194 ((not (find next-char
*channel-select-characters
*)) channels
)
196 (push (- (char-code (read-char stream
))
197 (char-code (char *channel-select-characters
* 0)))
200 (defun eat-whitespace-and-barlines (stream)
201 (do ((next-char #1=(peek-char nil stream
) #1#))
202 ((not (find next-char
*whitespace-characters
*)))
206 (defmacro mv-push
(source destination key
)
207 `(do ((d ,destination
(cdr d
))
210 (push (car s
) (,key
(car d
)))))
213 ;;;; HIGH-LEVEL PARSE ROUTINES.
215 (defun parse-music-section (stream channels
)
216 "Reads a music section from stream; returns at EOF or if a section
217 change is detected. Writes data and property changes to channels.
218 Highly intolerant of malformed inputs."
220 (music-parse-internal stream channels
)
223 (setf (channel-data-stream c
) (reverse (channel-data-stream c
)))))
225 (defun music-parse-internal (stream channels
)
226 (do ((current-channels nil
)
227 (next-char #1=(peek-char nil stream
) #1#))
229 ;; Channel selection characters.
230 (cond ((find next-char
*channel-select-characters
*)
231 (setf current-channels nil
)
232 (dolist (c (expect-channels stream
))
233 (push (nth c channels
) current-channels
)))
236 ((char= next-char
#\o
)
237 (assert current-channels
)
239 (let ((octave (expect-int stream
)))
240 (dolist (c current-channels
)
241 (setf (channel-octave c
) octave
))))
243 ((char= next-char
#\
<)
244 (assert current-channels
)
246 (dolist (c current-channels
)
247 (decf (channel-octave c
))))
249 ((char= next-char
#\
>)
250 (assert current-channels
)
252 (dolist (c current-channels
)
253 (incf (channel-octave c
))))
256 ((find next-char
*note-characters
*)
257 (assert current-channels
)
258 (multiple-value-bind (note-char accidentals duration
)
260 (dolist (c current-channels
)
261 (push (make-note (calculate-tone note-char
264 (clarify-duration duration c
))
265 (channel-data-stream c
)))))
267 ((or (char= next-char
#\r) (char= next-char
#\w
))
268 (assert current-channels
)
269 (multiple-value-bind (note-type duration
)
271 (dolist (c current-channels
)
272 (push (make-note note-type
273 (clarify-duration duration c
))
274 (channel-data-stream c
)))))
277 ((char= next-char
#\t)
278 (assert current-channels
)
280 (let ((tempo (expect-int stream
)))
281 (dolist (c current-channels
)
282 (push (make-tempo-command tempo
)
283 (channel-data-stream c
))
284 (setf (channel-tempo c
) tempo
))))
285 ((char= next-char
#\
#)
289 ((char= next-char
#\q
)
290 (assert current-channels
)
292 (let ((staccato (* *staccato-base-division
* (expect-int stream
))))
293 (dolist (c current-channels
)
294 (push (make-staccato-command staccato
)
295 (channel-data-stream c
))
296 (setf (channel-staccato c
) staccato
))))
299 ((char= next-char
#\
@)
300 (assert current-channels
)
301 (parse-macro-invocation stream current-channels
))
304 ((char= next-char
#\
;)
308 (t (format nil
"~&Ignored character: ~A"
309 (read-char stream
))))
310 (eat-whitespace-and-barlines stream
)))
313 (defun parse-macro-invocation (stream channels
)
315 (let ((next-char (peek-char nil stream
)))
317 (cond ((char= next-char
#\a)
319 (let ((arp-num (expect-int stream
)))
321 (push (make-arpeggio-command arp-num
)
322 (channel-data-stream c
)))))
324 (t (format nil
"~&Ignored macro invocator: @~A"
325 (read-char stream
))))))