m@24
|
1 (cl:in-package #:amuse)
|
m@24
|
2
|
m@89
|
3 ;;; monody
|
m@89
|
4
|
m@143
|
5 (defmethod ensure-monody ((m standard-monody))
|
m@89
|
6 (let ((result t))
|
m@89
|
7 (dotimes (i (1- (length m)) result)
|
m@89
|
8 ;; assumes the events are time ordered which (since monody is a
|
m@89
|
9 ;; subclass of time-ordered-constituent) they ought to be.
|
m@89
|
10 (let ((e1 (elt m i))
|
m@89
|
11 (e2 (elt m (1+ i))))
|
m@89
|
12 (unless (or (before e1 e2) (meets e1 e2))
|
m@89
|
13 (setf result nil))))))
|
m@89
|
14
|
c@109
|
15 ;;; diatonic pitch (represented using MIPS)
|
m@81
|
16
|
c@109
|
17 (defmethod asa-pitch-string ((mp diatonic-pitch))
|
c@106
|
18 (mips:p-pn (list (%p-pc mp) (%p-pm mp))))
|
c@106
|
19
|
c@109
|
20 (defmethod diatonic-pitch-octave ((mp diatonic-pitch))
|
c@106
|
21 (let* ((asa-string (asa-pitch-string mp))
|
c@106
|
22 (start (position-if #'digit-char-p asa-string)))
|
c@106
|
23 (values (parse-integer asa-string :start start))))
|
m@86
|
24
|
c@109
|
25 (defmethod diatonic-pitch-accidental ((mp diatonic-pitch))
|
c@106
|
26 (let* ((asa-string (asa-pitch-string mp))
|
c@106
|
27 (start 1)
|
c@106
|
28 (end (position-if #'digit-char-p asa-string))
|
c@106
|
29 (malist '((#\n . 0) (#\s . +1) (#\f . -1)))
|
c@106
|
30 (multiplier (cdr (assoc (char asa-string 1) malist))))
|
c@106
|
31 (* multiplier (- end start))))
|
c@106
|
32
|
c@109
|
33 (defmethod diatonic-pitch-name ((mp diatonic-pitch))
|
c@106
|
34 (elt (asa-pitch-string mp) 0))
|
m@86
|
35
|
c@109
|
36 (defmethod middle-c ((mp diatonic-pitch))
|
j@187
|
37 (make-diatonic-pitch #\C 0 4))
|
m@81
|
38
|
c@109
|
39 (defmethod midi-pitch-number ((mp diatonic-pitch))
|
c@106
|
40 (+ (%p-pc mp) 21))
|
c@106
|
41
|
c@109
|
42 (defmethod octave ((mp diatonic-pitch))
|
c@106
|
43 (1- (floor (midi-pitch-number mp) 12)))
|
c@106
|
44
|
c@109
|
45 (defmethod diatonic-pitch ((mp diatonic-pitch))
|
m@81
|
46 mp)
|
m@81
|
47
|
c@109
|
48 (defmethod print-object ((o diatonic-pitch) stream)
|
c@106
|
49 (print-unreadable-object (o stream :type t)
|
c@106
|
50 (let ((asa-string (asa-pitch-string o)))
|
c@106
|
51 (write asa-string :stream stream))))
|
m@81
|
52
|
c@111
|
53 (defmethod asa-interval-string ((mpi diatonic-pitch-interval))
|
c@111
|
54 (mips:pi-pin (%diatonic-pitch-interval-span mpi)))
|
c@111
|
55
|
c@111
|
56 (defmethod print-object ((o diatonic-pitch-interval) stream)
|
c@111
|
57 (print-unreadable-object (o stream :type t)
|
c@111
|
58 (let ((asa-string (asa-interval-string o)))
|
c@111
|
59 (write asa-string :stream stream))))
|
c@111
|
60
|
m@81
|
61 ;;; Chromatic pitch
|
m@81
|
62
|
m@86
|
63 (defmethod octave ((cp chromatic-pitch))
|
c@106
|
64 (1- (floor (%chromatic-pitch-number cp) 12)))
|
m@86
|
65
|
m@81
|
66 (defmethod middle-c ((cp chromatic-pitch))
|
m@81
|
67 (make-chromatic-pitch 60))
|
m@81
|
68
|
d@136
|
69 (defmethod chromatic-pitch ((pitch chromatic-pitch))
|
d@136
|
70 pitch)
|
m@24
|
71
|
d@136
|
72 (defmethod midi-pitch-number ((pitch chromatic-pitch))
|
d@136
|
73 (%chromatic-pitch-number pitch))
|
m@24
|
74
|
d@136
|
75 (defmethod midi-pitch-number ((pitch pitch))
|
d@136
|
76 (%chromatic-pitch-number (chromatic-pitch pitch)))
|
m@24
|
77
|
m@113
|
78 (defmethod print-object ((o chromatic-pitch) stream)
|
m@113
|
79 (print-unreadable-object (o stream :type t)
|
m@113
|
80 (write (midi-pitch-number o) :stream stream)))
|
m@113
|
81
|
m@113
|
82 (defmethod print-object ((o chromatic-pitch-interval) stream)
|
m@113
|
83 (print-unreadable-object (o stream :type t)
|
m@113
|
84 (write (span o) :stream stream)))
|
m@113
|
85
|
m@113
|
86
|
d@136
|
87 (defmethod span ((pitch-interval chromatic-pitch-interval))
|
d@136
|
88 (%chromatic-pitch-interval-span pitch-interval))
|
m@24
|
89
|
d@136
|
90 (defmethod duration ((period standard-period))
|
d@136
|
91 (%period-interval period))
|
m@24
|
92
|
d@136
|
93 (defmethod (setf duration) ((value real) (period standard-period))
|
d@136
|
94 (setf (%period-interval period) value))
|
d@33
|
95
|
d@136
|
96 (defmethod timepoint ((moment standard-moment))
|
d@136
|
97 (%moment-time moment))
|
m@24
|
98
|
d@136
|
99 (defmethod (setf timepoint) ((value real) (moment standard-moment))
|
d@136
|
100 (setf (%moment-time moment) value))
|
d@33
|
101
|
d@136
|
102 (defmethod cut-off ((anchored-period standard-anchored-period))
|
d@136
|
103 (make-instance 'standard-moment
|
d@136
|
104 :time (+ (%moment-time anchored-period)
|
d@136
|
105 (%period-interval anchored-period))))
|
d@73
|
106
|
d@136
|
107 (defmethod print-object ((o standard-moment) stream)
|
m@113
|
108 (print-unreadable-object (o stream :type t)
|
m@113
|
109 (write (timepoint o) :stream stream)))
|
m@113
|
110
|
d@136
|
111 (defmethod print-object ((o standard-period) stream)
|
m@113
|
112 (print-unreadable-object (o stream :type t)
|
m@113
|
113 (write (duration o) :stream stream)))
|
m@113
|
114
|
d@136
|
115 (defmethod print-object ((o standard-anchored-period) stream)
|
m@126
|
116 (print-unreadable-object (o stream :type t)
|
m@126
|
117 (format stream "~A ~A" (timepoint o) (duration o))))
|
m@113
|
118
|
d@136
|
119 (defmethod beat-units-per-bar ((time-signature standard-time-signature))
|
m@24
|
120 (%basic-time-signature-numerator time-signature))
|
m@24
|
121
|
d@136
|
122 (defmethod beat-units ((time-signature standard-time-signature))
|
m@24
|
123 (%basic-time-signature-denominator time-signature))
|
m@24
|
124
|
d@136
|
125 (defmethod time-signature-equal ((ts1 standard-time-signature)
|
d@136
|
126 (ts2 standard-time-signature))
|
m@67
|
127 (let ((n1 (time-signature-numerator ts1))
|
m@67
|
128 (n2 (time-signature-numerator ts2))
|
m@67
|
129 (d1 (time-signature-denominator ts1))
|
m@67
|
130 (d2 (time-signature-denominator ts2)))
|
m@67
|
131 (and n1 n2 (= n1 n2)
|
m@67
|
132 d1 d2 (= d1 d2))))
|
m@67
|
133
|
d@136
|
134 (defmethod print-object ((sts standard-time-signature) stream)
|
d@136
|
135 (print-unreadable-object (sts stream :type t)
|
d@136
|
136 (format stream "~A/~A" (beat-units-per-bar sts) (beat-units sts))))
|
m@113
|
137
|
d@136
|
138 (defmethod key-signature-sharps ((key-signature standard-key-signature))
|
m@24
|
139 (%basic-key-signature-sharp-count key-signature))
|
m@24
|
140
|
m@45
|
141 (defmethod key-signature-mode ((ks midi-key-signature))
|
m@45
|
142 (%midi-key-signature-mode ks))
|
m@45
|
143
|
m@113
|
144 (defmethod print-object ((mks midi-key-signature) stream)
|
m@113
|
145 (print-unreadable-object (mks stream :type t)
|
m@113
|
146 (format stream "~A ~A"
|
m@113
|
147 (%basic-key-signature-sharp-count mks)
|
m@113
|
148 (%midi-key-signature-mode mks))))
|
m@113
|
149
|
d@136
|
150 (defmethod key-signature-equal ((ks1 standard-key-signature)
|
d@136
|
151 (ks2 standard-key-signature))
|
m@67
|
152 (let ((s1 (key-signature-sharps ks1))
|
m@67
|
153 (s2 (key-signature-sharps ks2)))
|
m@67
|
154 (and s1 s2 (= s1 s2))))
|
m@67
|
155
|
m@67
|
156 (defmethod key-signature-equal ((ks1 midi-key-signature)
|
m@67
|
157 (ks2 midi-key-signature))
|
m@67
|
158 (let ((s1 (key-signature-sharps ks1))
|
m@67
|
159 (s2 (key-signature-sharps ks2))
|
m@67
|
160 (m1 (key-signature-mode ks1))
|
m@67
|
161 (m2 (key-signature-mode ks2)))
|
m@67
|
162 (and s1 s2 (= s1 s2)
|
m@67
|
163 m1 m2 (= m1 m2))))
|
m@67
|
164
|
d@136
|
165 (defmethod bpm ((tempo standard-tempo))
|
m@24
|
166 (%tempo-bpm tempo))
|
m@24
|
167
|
j@260
|
168 (defmethod bps ((tempo standard-tempo))
|
j@260
|
169 (/ (%tempo-bpm tempo) 60))
|
j@260
|
170
|
d@136
|
171 (defmethod print-object ((tempo standard-tempo) stream)
|
m@113
|
172 (print-unreadable-object (tempo stream :type t)
|
m@113
|
173 (write (bpm tempo) :stream stream)))
|
m@113
|
174
|
m@67
|
175 (defmethod tempo-equal ((t1 tempo) (t2 tempo))
|
m@67
|
176 (and (bpm t1) (bpm t2) (= t1 t2)))
|
m@67
|
177
|
m@67
|
178
|
m@24
|
179 ;; Time protocol
|
m@24
|
180
|
d@136
|
181 (defmethod time+ ((moment standard-moment) (period standard-period))
|
d@136
|
182 "Returns a <standard-moment>. Implemented as a straightforward
|
d@121
|
183 summation."
|
d@136
|
184 (make-standard-moment (+ (timepoint moment) (duration period))))
|
m@24
|
185
|
d@136
|
186 (defmethod time+ ((period standard-period) (moment standard-moment)) ;?
|
d@136
|
187 "Returns a <standard-moment>. Implemented as a straightforward
|
d@136
|
188 summation and defined by default as (time+ <moment> <period>)."
|
d@137
|
189 (time+ moment period))
|
m@24
|
190
|
d@136
|
191 (defmethod time+ ((period1 standard-period)
|
d@136
|
192 (period2 standard-period))
|
d@136
|
193 "Returns a <standard-period>. Implemented as a straightforward
|
d@121
|
194 summation."
|
d@136
|
195 (make-standard-period (+ (duration period1)
|
d@136
|
196 (duration period2))))
|
m@24
|
197
|
d@136
|
198 (defmethod time+ ((moment1 moment) (moment2 moment))
|
d@121
|
199 "Returns <condition:undefined-action>. The question makes no
|
d@121
|
200 sense."
|
d@136
|
201 (error 'undefined-action :operation 'time+
|
d@137
|
202 :datatype (list (class-of moment1) (class-of moment2))))
|
m@24
|
203
|
d@136
|
204 (defmethod time- ((moment1 standard-moment) (moment2 standard-moment))
|
d@136
|
205 "Returns <standard-anchored-period> with an onset at moment2 and
|
d@136
|
206 extending to moment1"
|
d@136
|
207 (make-standard-anchored-period (timepoint moment2)
|
d@136
|
208 (- (timepoint moment1)
|
d@136
|
209 (timepoint moment2))))
|
m@24
|
210
|
d@136
|
211 (defmethod time- ((moment standard-moment) (period standard-period))
|
d@136
|
212 "Returns <standard-moment>. Simple subtraction."
|
d@136
|
213 (make-standard-moment (- (timepoint moment)
|
d@136
|
214 (duration period))))
|
m@24
|
215
|
d@136
|
216 (defmethod time- ((period period) (moment moment)) ;?
|
d@121
|
217 "Returns <condition:undefined-action>. The question makes no
|
d@121
|
218 sense"
|
m@24
|
219 (error 'undefined-action
|
m@24
|
220 :operation 'time-
|
d@137
|
221 :datatype (list (class-of period) (class-of moment))))
|
m@24
|
222
|
d@136
|
223 (defmethod time- ((period1 standard-period) (period2 standard-period))
|
d@136
|
224 "Returns <standard-period> spanning the difference of the
|
d@121
|
225 periods"
|
d@136
|
226 (make-standard-period (- (duration period2)
|
d@136
|
227 (duration period1))))
|
m@24
|
228
|
m@24
|
229 ;; these ones are less certain. I've just put them in, but think I
|
m@24
|
230 ;; should remove them and force the user to specify what they mean
|
m@24
|
231 ;; when they give objects that are both moments *and* periods to these
|
m@24
|
232 ;; functions.
|
m@24
|
233
|
m@24
|
234 (defmethod time- ((object1 anchored-period) (object2 anchored-period)) ;?
|
m@24
|
235 (time- (moment object1) (moment object2)))
|
m@24
|
236
|
m@24
|
237 (defmethod time- (object1 (object2 anchored-period)) ;?
|
m@24
|
238 (time- object1 (moment object2)))
|
m@24
|
239
|
m@24
|
240 (defmethod time- ((object1 anchored-period) object2) ;?
|
m@24
|
241 (time- (moment object1) object2))
|
m@24
|
242
|
m@24
|
243 (defmethod time> ((object1 moment) (object2 moment))
|
m@24
|
244 (> (timepoint object1) (timepoint object2)))
|
m@24
|
245
|
d@73
|
246 (defmethod time< ((object1 moment) (object2 moment))
|
d@73
|
247 (< (timepoint object1) (timepoint object2)))
|
d@73
|
248
|
m@24
|
249 (defmethod time= ((object1 moment) (object2 moment))
|
m@24
|
250 (= (timepoint object1) (timepoint object2)))
|
m@24
|
251
|
d@136
|
252 (defmethod duration> ((period1 standard-period) (period2 standard-period))
|
d@136
|
253 (> (duration period1) (duration period2)))
|
m@24
|
254
|
d@136
|
255 (defmethod duration= ((period1 standard-period) (period2 standard-period))
|
d@136
|
256 (= (duration period1) (duration period2)))
|
m@24
|
257
|
d@136
|
258 (defmethod duration* ((period1 standard-period) (object2 number))
|
d@137
|
259 (make-standard-period (* (duration period1) object2)))
|
m@24
|
260
|
d@136
|
261 (defmethod duration* ((object1 number) (period standard-period))
|
d@136
|
262 (duration* period object1))
|
m@24
|
263
|
d@136
|
264 (defmethod duration/ ((period standard-period) (object2 number))
|
d@137
|
265 (make-standard-period (/ (duration period) object2)))
|
m@24
|
266
|
c@111
|
267 ;;;; Pitch protocol
|
m@24
|
268
|
c@111
|
269 ;;; Some catch-all methods for undefined operations and cases where we
|
c@111
|
270 ;;; don't have enough information:
|
c@111
|
271 (macrolet ((def (name class1 class2)
|
c@111
|
272 `(defmethod ,name ((object1 ,class1) (object2 ,class2))
|
c@111
|
273 (error 'undefined-action :operation ',name
|
c@111
|
274 :datatype (list (class-of object1) (class-of object2))))))
|
d@136
|
275 (def pitch+ pitch pitch)
|
d@136
|
276 (def pitch- pitch-interval pitch))
|
m@24
|
277
|
c@111
|
278 (macrolet ((def (name class1 class2)
|
c@111
|
279 `(defmethod ,name ((object1 ,class1) (object2 ,class2))
|
c@111
|
280 (error 'insufficient-information :operation ',name
|
c@111
|
281 :datatype (list (class-of object1) (class-of object2))))))
|
d@136
|
282 (def pitch+ pitch pitch-interval)
|
d@136
|
283 (def pitch+ pitch-interval pitch)
|
d@136
|
284 (def pitch+ pitch-interval pitch-interval)
|
d@136
|
285 (def pitch- pitch pitch)
|
d@136
|
286 (def pitch- pitch pitch-interval)
|
d@136
|
287 (def pitch- pitch-interval pitch-interval))
|
m@24
|
288
|
c@111
|
289 ;;; chromatic pitch intervals
|
m@24
|
290
|
c@111
|
291 (defmethod pitch+ ((object1 chromatic-pitch)
|
c@111
|
292 (object2 chromatic-pitch-interval))
|
c@111
|
293 (make-chromatic-pitch (+ (midi-pitch-number object1) (span object2))))
|
c@111
|
294
|
c@111
|
295 (defmethod pitch+ ((object1 chromatic-pitch-interval)
|
c@111
|
296 (object2 chromatic-pitch))
|
c@111
|
297 (make-chromatic-pitch (+ (span object1) (midi-pitch-number object2))))
|
c@111
|
298
|
c@111
|
299 (defmethod pitch+ ((object1 chromatic-pitch-interval)
|
c@111
|
300 (object2 chromatic-pitch-interval))
|
c@105
|
301 (make-chromatic-pitch-interval (+ (span object1) (span object2))))
|
m@24
|
302
|
c@111
|
303 (defmethod pitch- ((object1 chromatic-pitch)
|
c@111
|
304 (object2 chromatic-pitch))
|
c@111
|
305 (make-chromatic-pitch-interval
|
c@105
|
306 (- (midi-pitch-number object1) (midi-pitch-number object2))))
|
m@24
|
307
|
c@111
|
308 (defmethod pitch- ((object1 chromatic-pitch)
|
c@111
|
309 (object2 chromatic-pitch-interval))
|
c@105
|
310 (make-chromatic-pitch (- (midi-pitch-number object1) (span object2))))
|
m@24
|
311
|
c@111
|
312 (defmethod pitch- ((object1 chromatic-pitch-interval)
|
c@111
|
313 (object2 chromatic-pitch-interval))
|
c@105
|
314 (make-chromatic-pitch-interval (- (span object1) (span object2))))
|
m@24
|
315
|
c@111
|
316 (defmethod pitch> ((object1 chromatic-pitch)
|
c@111
|
317 (object2 chromatic-pitch))
|
c@111
|
318 (> (midi-pitch-number object1) (midi-pitch-number object2)))
|
m@24
|
319
|
c@111
|
320 (defmethod pitch= ((object1 chromatic-pitch)
|
c@111
|
321 (object2 chromatic-pitch))
|
c@111
|
322 (= (midi-pitch-number object1) (midi-pitch-number object2)))
|
m@24
|
323
|
c@111
|
324 (defmethod interval> ((object1 chromatic-pitch-interval)
|
c@111
|
325 (object2 chromatic-pitch-interval))
|
c@111
|
326 (> (span object1) (span object2)))
|
m@24
|
327
|
c@111
|
328 (defmethod interval= ((object1 chromatic-pitch-interval)
|
c@111
|
329 (object2 chromatic-pitch-interval))
|
c@111
|
330 (= (span object1) (span object2)))
|
m@24
|
331
|
c@111
|
332 ;;; diatonic pitch intervals
|
m@24
|
333
|
c@111
|
334 (defmethod pitch+ ((object1 diatonic-pitch) (object2 diatonic-pitch-interval))
|
c@111
|
335 (let* ((cp (%p-pc object1))
|
c@111
|
336 (mp (%p-pm object1))
|
c@111
|
337 (span (span object2))
|
c@111
|
338 (cps (first span))
|
c@111
|
339 (mps (second span)))
|
c@111
|
340 (make-mips-pitch (+ cp cps) (+ mp mps))))
|
c@111
|
341
|
c@111
|
342 (defmethod pitch+ ((object1 diatonic-pitch-interval) (object2 diatonic-pitch))
|
c@111
|
343 (let* ((cp (%p-pc object2))
|
c@111
|
344 (mp (%p-pm object2))
|
c@111
|
345 (span (span object1))
|
c@111
|
346 (cps (first span))
|
c@111
|
347 (mps (second span)))
|
c@111
|
348 (make-mips-pitch (+ cp cps) (+ mp mps))))
|
c@111
|
349
|
c@111
|
350 (defmethod pitch+ ((object1 diatonic-pitch-interval)
|
c@111
|
351 (object2 diatonic-pitch-interval))
|
c@111
|
352 (let* ((span1 (span object1))
|
c@111
|
353 (span2 (span object2)))
|
c@111
|
354 (make-mips-pitch-interval (+ (first span1) (first span2))
|
c@111
|
355 (+ (second span1) (second span2)))))
|
c@111
|
356
|
c@111
|
357 (defmethod pitch- ((object1 diatonic-pitch) (object2 diatonic-pitch))
|
c@111
|
358 (let ((cp1 (%p-pc object1))
|
c@111
|
359 (mp1 (%p-pm object1))
|
c@111
|
360 (cp2 (%p-pc object2))
|
c@111
|
361 (mp2 (%p-pm object2)))
|
c@111
|
362 (make-mips-pitch-interval (- cp1 cp2) (- mp1 mp2))))
|
c@111
|
363
|
c@111
|
364 (defmethod pitch- ((object1 diatonic-pitch) (object2 diatonic-pitch-interval))
|
c@111
|
365 (let* ((cp (%p-pc object1))
|
c@111
|
366 (mp (%p-pm object1))
|
c@111
|
367 (span (span object2))
|
c@111
|
368 (cps (first span))
|
c@111
|
369 (mps (second span)))
|
c@111
|
370 (make-mips-pitch (- cp cps) (- mp mps))))
|
c@111
|
371
|
c@111
|
372 (defmethod pitch- ((object1 diatonic-pitch-interval)
|
c@111
|
373 (object2 diatonic-pitch-interval))
|
c@111
|
374 (let ((span1 (span object1))
|
c@111
|
375 (span2 (span object2)))
|
c@111
|
376 (make-mips-pitch-interval (- (first span1) (first span2))
|
c@111
|
377 (- (second span1) (second span2)))))
|
c@111
|
378
|
c@111
|
379 (defmethod pitch> ((p1 diatonic-pitch) (p2 diatonic-pitch))
|
c@111
|
380 (error 'undefined-action :operation 'pitch>
|
c@111
|
381 :datatype (list (class-of p1) (class-of p2))))
|
c@111
|
382
|
c@111
|
383 (defmethod pitch= ((p1 diatonic-pitch) (p2 diatonic-pitch))
|
c@111
|
384 (let ((c1 (%p-pc p1)) (m1 (%p-pm p1))
|
c@111
|
385 (c2 (%p-pc p2)) (m2 (%p-pm p2)))
|
c@111
|
386 (and c1 c2 (= c1 c2)
|
c@111
|
387 m1 m2 (= m1 m2))))
|
m@24
|
388
|
m@24
|
389
|
m@24
|
390 ;; Allen
|
m@24
|
391
|
m@24
|
392 (defmethod meets ((object1 anchored-period)
|
m@24
|
393 (object2 anchored-period))
|
m@24
|
394 (or (time= (cut-off object1) object2)
|
m@24
|
395 (time= (cut-off object2) object1)))
|
m@24
|
396
|
m@24
|
397 (defmethod before ((object1 anchored-period)
|
m@24
|
398 (object2 anchored-period))
|
m@24
|
399 (time> object2 (cut-off object1)))
|
m@24
|
400
|
m@24
|
401 (defmethod overlaps ((object1 anchored-period)
|
m@24
|
402 (object2 anchored-period))
|
m@24
|
403 ;; FIXME: Is there a tidier method?
|
m@24
|
404 (or (and (time> object2 object1) ; object1 starts before object2
|
m@24
|
405 (time> (cut-off object1) object2) ; object1 ends after object2 starts
|
m@24
|
406 (time> (cut-off object2) (cut-off object1))) ; object1 ends before object2 does
|
m@24
|
407 (and (time> object1 object2) ; object1 starts after object2
|
m@24
|
408 (time> (cut-off object2) object1) ; object1 starts before object2 ends
|
m@24
|
409 (time> (cut-off object1) (cut-off object2))))) ; object1 ends after object2 does
|
m@24
|
410
|
m@24
|
411 (defmethod during ((object1 anchored-period)
|
m@24
|
412 (object2 anchored-period))
|
j@205
|
413 (or (and (time> object1 object2)
|
j@205
|
414 (time<= (cut-off object1) (cut-off object2)))
|
j@205
|
415 (and (time>= object1 object2)
|
j@205
|
416 (time< (cut-off object1) (cut-off object2)))))
|
m@24
|
417
|
m@24
|
418 (defmethod starts ((object1 anchored-period)
|
m@24
|
419 (object2 anchored-period))
|
m@24
|
420 (time= object1 object2))
|
m@24
|
421
|
m@24
|
422 (defmethod ends ((object1 anchored-period)
|
m@24
|
423 (object2 anchored-period))
|
m@24
|
424 (time= (cut-off object1) (cut-off object2)))
|
m@24
|
425
|
j@261
|
426 ;; Does Allen cover this? Anyway, they are useful for me -JF.
|
j@261
|
427 (defmethod onset-within ((object1 moment)
|
j@261
|
428 (object2 standard-anchored-period))
|
j@261
|
429 (and (time>= object1 object2)
|
j@261
|
430 (time<= object1 (cut-off object2))))
|
j@261
|
431
|
j@261
|
432 (defmethod onset-within-fuzzy ((object1 moment)
|
j@261
|
433 (object2 standard-anchored-period)
|
j@261
|
434 (period standard-period))
|
j@261
|
435 (and (time>= object1 (time- (onset object2) period))
|
j@261
|
436 (time<= object1 (time- (cut-off object2) period))))
|
j@261
|
437
|
m@24
|
438 ;; ...and
|
m@24
|
439
|
d@33
|
440 (defmethod period= ((object1 anchored-period)
|
c@105
|
441 (object2 anchored-period))
|
d@33
|
442 (and (time= object1 object2)
|
d@33
|
443 (duration= object1 object2)))
|
d@136
|
444 (defmethod period= ((object1 period)
|
d@136
|
445 (object2 period))
|
d@33
|
446 (duration= object1 object2))
|
d@33
|
447
|
d@136
|
448 (defmethod period-intersection ((object1 standard-anchored-period)
|
d@136
|
449 (object2 standard-anchored-period))
|
m@24
|
450 (cond
|
m@24
|
451 ((disjoint object1 object2)
|
m@24
|
452 ;; if they don't overlap, return nil, not a negative-valued
|
m@24
|
453 ;; period
|
m@24
|
454 nil)
|
m@24
|
455 ((let* ((start (if (time> (onset object2) (onset object1))
|
m@24
|
456 (onset object2)
|
m@24
|
457 (onset object1)))
|
m@24
|
458 (duration (duration (time- (if (time> (cut-off object2) (cut-off object1))
|
m@24
|
459 (cut-off object1)
|
m@24
|
460 (cut-off object2))
|
m@24
|
461 start))))
|
m@24
|
462 (make-anchored-period (timepoint start) duration)))))
|
m@24
|
463
|
d@136
|
464 ;; Time constructors
|
d@136
|
465 (defmethod make-moment ((time-value real))
|
d@136
|
466 "Returns STANDARD-MOMENT given a real"
|
d@136
|
467 (make-standard-moment time-value))
|
d@136
|
468 (defmethod make-period ((duration-value real))
|
d@136
|
469 "Returns STANDARD-PERIOD given a real"
|
d@136
|
470 (make-standard-period duration-value))
|
d@136
|
471 (defmethod make-anchored-period ((onset-value real) (duration-value real))
|
d@136
|
472 "Returns STANDARD-ANCHORED-PERIOD given a real"
|
m@143
|
473 (make-standard-anchored-period onset-value duration-value))
|
d@151
|
474
|
d@151
|
475 ;; Needed by some sequence functions, notably remove-if.
|
d@151
|
476 (defmethod sequence:make-sequence-like :around ((o standard-composition) length
|
d@151
|
477 &key (initial-element nil iep)
|
d@151
|
478 (initial-contents nil icp))
|
d@152
|
479 "Around method for make-sequence-like, only with all slots
|
d@151
|
480 preserved from the source sequence (except onset and duration,
|
d@151
|
481 which are calculated afresh)."
|
d@151
|
482 (declare (ignore length initial-element initial-contents iep icp))
|
d@178
|
483 (let ((new-sequence (call-next-method)) (slot-name))
|
d@151
|
484 ;; Get timing information
|
d@153
|
485 (setf new-sequence (%recompute-standard-composition-period new-sequence))
|
d@153
|
486 (dolist (slotd (sb-mop:class-slots (class-of new-sequence)) new-sequence)
|
d@178
|
487 (setf slot-name (sb-mop:slot-definition-name slotd))
|
d@178
|
488 (unless (or (equal slot-name '%data)
|
d@178
|
489 (equal slot-name 'time)
|
d@178
|
490 (equal slot-name 'interval)
|
d@178
|
491 (not (slot-boundp o slot-name)))
|
d@153
|
492 (setf (sb-mop:slot-value-using-class (class-of new-sequence)
|
d@153
|
493 new-sequence
|
d@153
|
494 slotd)
|
d@153
|
495 (sb-mop:slot-value-using-class (class-of new-sequence)
|
d@153
|
496 o ;; if this isn't the same, we're lost anyway
|
d@153
|
497 slotd))))))
|
d@153
|
498
|
d@153
|
499 (defun %recompute-standard-composition-period (composition)
|
d@153
|
500 "Find onset and duration times for newly-made composition object."
|
d@153
|
501 (let ((start) (finish))
|
d@153
|
502 (sequence:dosequence (element composition)
|
d@151
|
503 ;; Actually, this next bit is pretty stupid - I know this is
|
d@151
|
504 ;; ordered, so this bit could be replaced by
|
d@151
|
505 ;; (setf (timepoint new-sequence)
|
d@151
|
506 ;; (timepoint (elt new-sequence 0)))
|
d@151
|
507 ;; outside of the loop.
|
d@152
|
508 (when (and element
|
d@152
|
509 (or (null start)
|
d@152
|
510 (< (timepoint element) start)))
|
d@152
|
511 (setf start (timepoint element)))
|
d@152
|
512 (when (and element
|
d@152
|
513 (or (null finish)
|
d@152
|
514 (> (timepoint (cut-off element))
|
d@152
|
515 finish)))
|
d@152
|
516 (setf finish (timepoint (cut-off element)))))
|
d@152
|
517 (unless start
|
d@152
|
518 (setf start 0))
|
d@152
|
519 (unless finish
|
d@152
|
520 (setf finish 0))
|
d@153
|
521 (setf (timepoint composition) start
|
d@153
|
522 (duration composition) (- finish start))
|
d@153
|
523 composition))
|
d@153
|
524
|
d@153
|
525
|
d@153
|
526 (defmethod sequence:adjust-sequence :around ((o standard-composition) length
|
d@153
|
527 &key initial-element
|
d@153
|
528 (initial-contents nil icp))
|
d@153
|
529 (declare (ignore length o initial-element initial-contents icp))
|
d@175
|
530 (%recompute-standard-composition-period (call-next-method)))
|
d@175
|
531
|
j@193
|
532 (defmethod get-constituents ((identifier composition-identifier))
|
j@193
|
533 (list (get-composition identifier)))
|
d@175
|
534
|
d@175
|
535 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
d@175
|
536 ;;
|
d@175
|
537 ;; Experimental:
|
d@175
|
538 ;;
|
d@175
|
539
|
d@175
|
540 ;; Some not obviously correct implementations of the new metre
|
d@175
|
541 ;; functions. These are no worse than we're already using (they should
|
d@175
|
542 ;; be more or less equivalent)
|
d@175
|
543
|
d@175
|
544 (defmethod bar-period ((time-signature standard-time-signature)
|
d@175
|
545 object)
|
d@175
|
546 (make-standard-period (* (duration (crotchet object))
|
d@175
|
547 (time-signature-numerator time-signature)
|
d@175
|
548 (/ 4 (time-signature-denominator time-signature)))))
|
d@175
|
549
|
d@175
|
550 (defmethod current-bar ((moment standard-moment) (composition composition))
|
d@175
|
551 (let* ((time-sig (car (get-applicable-time-signatures
|
d@175
|
552 (make-standard-anchored-period (timepoint moment)
|
d@175
|
553 (duration (crotchet composition)))
|
d@175
|
554 composition)))
|
d@175
|
555 (bar-duration (bar-period time-sig composition)))
|
d@175
|
556 (do* ((start (onset time-sig) next-start)
|
d@175
|
557 (next-start (time+ start bar-duration) (time+ start bar-duration)))
|
d@175
|
558 ((time> next-start moment)
|
d@175
|
559 (make-standard-anchored-period (timepoint start)
|
d@175
|
560 (duration bar-duration))))))
|
d@175
|
561
|
j@233
|
562 (defmethod ioi-from-bar ((event event))
|
j@258
|
563 "Within-short-bar-p here is for catching anacruses. The correct IOI
|
j@258
|
564 of the event(s) from the barline can be calculated by finding the
|
j@258
|
565 corresponding position of timepoint 0 relative to the bar, and then
|
j@258
|
566 calculating the event IOI relative to that. FIXME: This will almost
|
j@258
|
567 certainly not be the desired behaviour if 'short bars' are found
|
j@258
|
568 within a piece. Also, what about 'long bars'? Also, more generally,
|
j@258
|
569 what should we do if get-app-time-sig gives us multiple
|
j@258
|
570 time-signatures? We should at least be checking."
|
j@258
|
571 (cond
|
j@258
|
572 ((within-short-bar-p event)
|
j@258
|
573 (+ (timepoint (onset event))
|
j@258
|
574 (- (duration (bar-period (car (get-applicable-time-signatures
|
j@258
|
575 event (composition event)))
|
j@258
|
576 event))
|
j@258
|
577 (duration (current-bar event (composition event))))))
|
j@258
|
578 (t
|
j@258
|
579 (- (timepoint (onset event))
|
j@258
|
580 (timepoint (current-bar event (composition event)))))))
|
j@233
|
581
|
j@233
|
582 (defmethod ioi-from-bar ((constituent constituent))
|
j@258
|
583 "FIXME: Check for short bars, or maybe just use the first event?"
|
j@233
|
584 (- (timepoint (onset constituent))
|
j@233
|
585 (timepoint (current-bar constituent constituent))))
|
j@233
|
586
|
j@233
|
587 (defmethod onset-in-bar ((o moment))
|
j@250
|
588 "FIXME: Won't actually work for standard-moments because they do not
|
j@250
|
589 have a composition slot! So either we allow for 'linked moments', or
|
j@250
|
590 change the method to have an optional composition parameter."
|
j@233
|
591 (1+ (ioi-from-bar o)))
|
j@233
|
592
|
j@250
|
593 (defmethod onset-in-bar-relative-to-tactus ((o moment))
|
j@250
|
594 (1+ (/ (ioi-from-bar o)
|
j@250
|
595 (tactus-duration
|
j@250
|
596 (car (get-applicable-time-signatures o (composition o)))))))
|
j@250
|
597
|
j@258
|
598 (defmethod within-short-bar-p ((event linked-event))
|
j@258
|
599 (let ((time-sig (get-applicable-time-signatures event
|
j@258
|
600 (composition event))))
|
j@258
|
601 (assert (= (length time-sig) 1))
|
j@258
|
602 (setf time-sig (car time-sig))
|
j@258
|
603 (let ((bar-duration (bar-period time-sig (composition event)))
|
j@258
|
604 (current-bar (current-bar event (composition event))))
|
j@258
|
605 (duration< current-bar bar-duration))))
|
j@258
|
606
|
d@175
|
607 (defmethod beat-period ((moment standard-moment)
|
d@175
|
608 (time-signature standard-time-signature)
|
d@175
|
609 (composition composition))
|
d@175
|
610 ;; Simple example - standard-time-signature has constant tactus
|
d@175
|
611 (let* ((containing-bar (current-bar moment composition))
|
d@175
|
612 (beat-duration (* (duration (crotchet composition))
|
d@175
|
613 (tactus-duration time-signature)))
|
d@175
|
614 (beat-period (make-standard-anchored-period (timepoint containing-bar)
|
d@175
|
615 beat-duration)))
|
d@175
|
616 (do ()
|
d@175
|
617 ((time> (cut-off beat-period) moment) beat-period)
|
d@175
|
618 (setf (timepoint beat-period) (timepoint (cut-off beat-period))))))
|
d@175
|
619
|
d@175
|
620 (defmethod current-beat ((moment standard-moment) (composition composition))
|
d@175
|
621 ;; Assume at most one time signature per bar (otherwise, this is hell)
|
d@175
|
622 (let* ((time-sig (car (get-applicable-time-signatures (current-bar moment composition) composition))))
|
d@175
|
623 (if time-sig
|
d@175
|
624 (beat-period moment time-sig composition)
|
d@175
|
625 ;; If no time-sig, there's no way of answering this
|
d@175
|
626 ;; directly. There may be sensible defaults, but it's the job
|
d@175
|
627 ;; of an implementation's author to solve that.
|
d@175
|
628 (error 'insufficient-information :operation 'beat-period :datatype (class-of composition)))))
|
d@175
|
629
|
j@208
|
630
|
j@245
|
631 ;;;=====================================================================
|
j@230
|
632 ;;; Copying events in time
|
j@245
|
633 ;;;=====================================================================
|
j@208
|
634
|
j@208
|
635 (defmethod move-to-first-bar ((composition composition))
|
j@226
|
636 (let ((offset (floor (timepoint (elt composition 0)))))
|
j@208
|
637 (loop
|
j@209
|
638 for event in (%list-slot-sequence-data composition)
|
j@208
|
639 do (setf event (copy-event event))
|
j@208
|
640 do (setf (timepoint event)
|
j@208
|
641 (- (timepoint event) offset))
|
j@208
|
642 collect event into shifted-events
|
j@259
|
643 finally (return
|
j@259
|
644 (sequence:make-sequence-like
|
j@259
|
645 composition
|
j@259
|
646 (length composition)
|
j@259
|
647 :initial-contents shifted-events)))))
|
j@230
|
648
|
j@230
|
649
|
j@245
|
650 ;;;=====================================================================
|
j@230
|
651 ;;; Searching for events
|
j@245
|
652 ;;;=====================================================================
|
j@230
|
653
|
j@230
|
654 (defmethod find-next-event ((source-event event) &key predicate test
|
j@230
|
655 break-test search-list)
|
j@230
|
656 "Ideally a sorted search list that begins with the first event after
|
j@230
|
657 the source-event should be provided, otherwise, the search will begin
|
j@230
|
658 from the beginning."
|
j@230
|
659 (unless search-list (setf search-list (composition source-event)))
|
j@230
|
660 (cond
|
j@230
|
661 ((and test predicate)
|
j@230
|
662 (error "Supplied both a test and a predicate."))
|
j@230
|
663 (test
|
j@230
|
664 (sequence:dosequence (e search-list nil)
|
j@230
|
665 (when (and (time> (onset e) (onset source-event))
|
j@230
|
666 (funcall test source-event e))
|
j@230
|
667 (return e))
|
j@230
|
668 (when break-test
|
j@230
|
669 (when (funcall break-test source-event e)
|
j@230
|
670 (return nil)))))
|
j@230
|
671 (predicate
|
j@230
|
672 (sequence:dosequence (e search-list nil)
|
j@230
|
673 (when (and (time> (onset e) (onset source-event))
|
j@230
|
674 (funcall predicate e))
|
j@230
|
675 (return e))
|
j@230
|
676 (when break-test
|
j@230
|
677 (when (funcall break-test source-event e)
|
j@230
|
678 (return nil)))))))
|
j@230
|
679
|
j@230
|
680
|
j@245
|
681 ;;;=====================================================================
|
j@230
|
682 ;;; Sorting Compositions
|
j@245
|
683 ;;;=====================================================================
|
j@230
|
684
|
j@230
|
685 (defmethod event< ((event1 event) (event2 event) attribute-list)
|
j@230
|
686 (dolist (attribute attribute-list nil) ;nil if equal
|
j@230
|
687 (if (< (funcall attribute event1) (funcall attribute event2))
|
j@230
|
688 (return t)
|
j@230
|
689 (if (> (funcall attribute event1) (funcall attribute event2))
|
j@230
|
690 (return nil)))))
|
j@230
|
691
|
j@230
|
692 (defun make-event< (attribute-list)
|
j@230
|
693 (lambda (event1 event2)
|
j@230
|
694 (funcall #'event< event1 event2 attribute-list)))
|
j@230
|
695
|
j@230
|
696 (defmethod sort-composition ((composition composition) dimension-spec)
|
j@230
|
697 (sequence:make-sequence-like composition
|
j@230
|
698 (length composition)
|
j@230
|
699 :initial-contents
|
j@230
|
700 (stable-sort
|
j@230
|
701 (copy-seq composition)
|
j@230
|
702 (make-event< dimension-spec))))
|