d@74
|
1 (in-package "AMUSE-SEGMENTATION")
|
d@74
|
2
|
d@74
|
3 ;;;;;;;;;;;;;;;;;
|
d@74
|
4 ;;
|
d@74
|
5 ;; Demo example: `simple segmenter'
|
d@74
|
6 ;;
|
d@74
|
7 ;; Simple segmenter segments a piece at long gaps between notes (where
|
d@74
|
8 ;; `gaps' are the amount of time between successive note-onsets).
|
d@74
|
9 ;;
|
d@74
|
10 ;; In this implementation, the modal gap-length is calculated (the
|
d@74
|
11 ;; highest is taken in cases of a tie) and a threshold is taken of 3.9
|
d@74
|
12 ;; times that length. An absolute gap of 1.5 seconds is also used if
|
d@74
|
13 ;; beats-to-seconds is available.
|
d@74
|
14
|
d@75
|
15 (defclass simple-segmenter (before-segmenter) ())
|
d@75
|
16
|
d@75
|
17 (defmethod boundary-strength ((segmenter simple-segmenter) (event moment-designator) (composition composition))
|
d@75
|
18 (declare (ignore segmenter))
|
d@75
|
19 (multiple-value-bind (i-o-i mode i-o-i-rt)
|
d@75
|
20 (inter-onset-intervals-for-simple-segmenter-with-cache composition)
|
d@75
|
21 (if (or (eq event (elt composition 0))
|
d@75
|
22 (> (cdr (assoc event i-o-i)) (* mode 3.9))
|
d@75
|
23 (and i-o-i-rt (> (cdr (assoc event i-o-i-rt))
|
d@75
|
24 1.5)))
|
d@75
|
25 1
|
d@75
|
26 0)))
|
d@74
|
27
|
d@74
|
28 (defgeneric simple-segmentsp (event composition))
|
d@74
|
29 (defmethod simple-segmentsp ((event moment-designator) (composition composition))
|
d@74
|
30 (multiple-value-bind (i-o-i mode i-o-i-rt)
|
d@74
|
31 (inter-onset-intervals-for-simple-segmenter-with-cache composition)
|
d@74
|
32 (if (or (eq event (elt composition 0))
|
d@74
|
33 (> (cdr (assoc event i-o-i)) (* mode 3.9))
|
d@74
|
34 (and i-o-i-rt (> (cdr (assoc event i-o-i-rt))
|
d@74
|
35 1.5)))
|
d@74
|
36 1
|
d@74
|
37 0)))
|
d@74
|
38
|
d@74
|
39 ;; helper-functions
|
d@74
|
40
|
d@74
|
41 (defparameter *i-o-i-cache* (make-hash-table :weakness :KEY))
|
d@74
|
42 (defun inter-onset-intervals-for-simple-segmenter-with-cache (composition &key (rounding-divisor 1/4))
|
d@74
|
43 (unless (gethash composition *i-o-i-cache*)
|
d@74
|
44 (setf (gethash composition *i-o-i-cache*)
|
d@74
|
45 (multiple-value-list (inter-onset-intervals-for-simple-segmenter composition
|
d@74
|
46 :rounding-divisor rounding-divisor))))
|
d@74
|
47 (values-list (gethash composition *i-o-i-cache*)))
|
d@74
|
48
|
d@74
|
49 (defgeneric inter-onset-intervals-for-simple-segmenter (composition &key rounding-divisor)
|
d@74
|
50 (:documentation "Returns values: inter-onset-intervals, i-o-i
|
d@74
|
51 mode and real-time-i-o-is. Both sets of i-o-is are alists
|
d@74
|
52 of (<event> . i-o-i)"))
|
d@74
|
53 (defmethod inter-onset-intervals-for-simple-segmenter ((composition composition)
|
d@74
|
54 &key (rounding-divisor 1/4))
|
d@74
|
55 ;; FIXME: (vaguely) assumes monody
|
d@74
|
56 (let ((i-o-i-list) (i-o-i-secs-list) (real-time t) (prev) (i-o-i-period)
|
d@74
|
57 (hits (make-array (1+ (/ 32 rounding-divisor))))
|
d@74
|
58 (modal-value) (modal-count 0))
|
d@74
|
59 (sequence:dosequence (event composition)
|
d@74
|
60 (when prev
|
d@74
|
61 (setf i-o-i-period (inter-onset-interval prev event))
|
d@74
|
62 (when real-time
|
d@74
|
63 (setf i-o-i-secs-list
|
d@74
|
64 (acons event (handler-case (beats-to-seconds i-o-i-period composition)
|
d@74
|
65 ((or undefined-action insufficient-information) ()
|
d@74
|
66 (setf real-time nil)))
|
d@74
|
67 i-o-i-secs-list)))
|
d@74
|
68 (setf i-o-i-list (acons event (duration i-o-i-period) i-o-i-list))
|
d@74
|
69 (when (and (< (round (duration i-o-i-period) rounding-divisor) 32)
|
d@74
|
70 (> (round (duration i-o-i-period) rounding-divisor) 0))
|
d@74
|
71 ;; larger than 32 crotchets doesn't really count as a useful
|
d@74
|
72 ;; i-o-i for our purposes (or am I just doing this because
|
d@74
|
73 ;; I'm using an array.
|
d@74
|
74 (incf (aref hits (round (duration i-o-i-period) rounding-divisor)))))
|
d@74
|
75 (setf prev event))
|
d@74
|
76 (loop for i downfrom (1- (length hits)) to 0
|
d@74
|
77 ;; we want the highest mode if there are several
|
d@74
|
78 when (> (aref hits i) modal-count)
|
d@74
|
79 do (setf modal-value i
|
d@74
|
80 modal-count (aref hits i)))
|
d@74
|
81 (values i-o-i-list (* modal-value rounding-divisor) (and real-time i-o-i-secs-list))))
|
d@74
|
82 |