d@34
|
1 (cl:in-package #:amuse-midi)
|
d@34
|
2
|
d@134
|
3 (defgeneric midi-channel (midi-message)
|
d@134
|
4 (:documentation "MIDI channel. Also used for midi output"))
|
d@34
|
5 (defmethod midi-channel ((midi-message midi-message))
|
d@34
|
6 (%midi-message-channel midi-message))
|
d@34
|
7
|
d@134
|
8 (defgeneric midi-track (midi-message)
|
d@134
|
9 (:documentation "MIDI track. Also used for midi output"))
|
d@34
|
10 (defmethod midi-track ((midi-message midi-message))
|
d@34
|
11 (%midi-message-track midi-message))
|
d@34
|
12
|
d@134
|
13 (defgeneric midi-velocity (event)
|
d@134
|
14 (:documentation "MIDI velocity. Also used for midi output"))
|
d@34
|
15 (defmethod midi-velocity ((event midi-pitched-event))
|
d@34
|
16 (%midi-pitched-event-velocity event))
|
d@34
|
17 (defmethod midi-velocity ((event midi-percussive-event))
|
d@34
|
18 (%midi-percussive-event-velocity event))
|
d@34
|
19
|
d@134
|
20 (defgeneric midi-patch (event)
|
d@134
|
21 (:documentation "MIDI patch (instrumental sound). Also used for
|
d@134
|
22 midi output"))
|
d@34
|
23 (defmethod midi-patch ((event midi-pitched-event))
|
d@34
|
24 (%midi-pitched-event-patch event))
|
d@34
|
25
|
d@134
|
26 (defgeneric midi-drum-sound (event)
|
d@134
|
27 (:documentation "MIDI pitch for unpitched events (usually, drum
|
d@134
|
28 sound for drum kits on channel 10, but also for semi-pitched
|
d@134
|
29 SFX, etc). Also used for midi output"))
|
d@34
|
30 (defmethod midi-drum-sound ((event midi-percussive-event))
|
d@34
|
31 (%midi-percussive-event-sound event))
|
d@34
|
32
|
d@34
|
33 (defmethod time-signatures ((composition midi-composition))
|
d@34
|
34 (%midi-time-signatures composition))
|
d@34
|
35 (defmethod (setf time-signatures) (sequence (composition midi-composition))
|
d@34
|
36 (setf (%midi-time-signatures composition) sequence))
|
d@34
|
37 (defmethod tempi ((composition midi-composition))
|
d@34
|
38 (%midi-tempi composition))
|
d@34
|
39 (defmethod (setf tempi) (sequence (composition midi-composition))
|
d@34
|
40 (setf (%midi-tempi composition) sequence))
|
d@115
|
41 (defmethod key-signatures ((composition midi-composition))
|
d@115
|
42 (%midi-key-signatures composition))
|
d@115
|
43 (defmethod (setf key-signatures) (sequence (composition midi-composition))
|
d@115
|
44 (setf (%midi-key-signatures composition) sequence))
|
d@34
|
45
|
d@34
|
46 (defgeneric copy-event (event))
|
d@34
|
47 ;; FIXME: This ought to call-next-method and operate on the result,
|
d@34
|
48 ;; rather than calling internals from the other package
|
d@34
|
49 (defmethod copy-event ((event midi-pitched-event))
|
d@34
|
50 (with-slots (channel track (number amuse::number) (time amuse::time) (interval amuse::interval) velocity patch)
|
d@34
|
51 event
|
d@34
|
52 (make-instance 'midi-pitched-event
|
d@34
|
53 :channel channel
|
d@34
|
54 :track track
|
d@34
|
55 :number number
|
d@34
|
56 :time time
|
d@34
|
57 :interval interval
|
d@34
|
58 :velocity velocity
|
d@34
|
59 :patch patch)))
|
d@34
|
60 (defmethod copy-event ((event midi-percussive-event))
|
d@34
|
61 (with-slots (channel track (time amuse::time) (interval amuse::interval) velocity patch sound)
|
d@34
|
62 event
|
d@34
|
63 (make-instance 'midi-percussive-event
|
d@34
|
64 :channel channel
|
d@34
|
65 :track track
|
d@34
|
66 :time time
|
d@34
|
67 :interval interval
|
d@34
|
68 :velocity velocity
|
d@34
|
69 :patch patch
|
d@34
|
70 :sound sound)))
|
d@154
|
71 (defgeneric copy-time-signature (time-signature))
|
d@154
|
72 (defmethod copy-time-signature ((time-signature standard-time-signature))
|
d@154
|
73 (make-instance (class-of time-signature)
|
d@154
|
74 :numerator (time-signature-numerator time-signature)
|
d@154
|
75 :denominator (time-signature-denominator time-signature)))
|
d@154
|
76 (defmethod copy-time-signature ((time-signature-period standard-time-signature-period))
|
d@154
|
77 (let ((sig (call-next-method)))
|
d@154
|
78 (setf (timepoint sig) (timepoint time-signature-period)
|
d@154
|
79 (duration sig) (duration time-signature-period))
|
d@154
|
80 sig))
|
d@154
|
81 (defgeneric copy-tempo (tempo))
|
d@154
|
82 (defmethod copy-tempo ((tempo standard-tempo))
|
d@154
|
83 (make-instance (class-of tempo)
|
d@154
|
84 :bpm (bpm tempo)))
|
d@154
|
85 (defmethod copy-tempo ((tempo-period standard-tempo-period))
|
d@154
|
86 (let ((tp (call-next-method)))
|
d@154
|
87 (setf (timepoint tp) (timepoint tempo-period)
|
d@154
|
88 (duration tp) (duration tempo-period))
|
d@154
|
89 tp))
|
d@154
|
90 (defgeneric copy-key-signature (key-signature))
|
d@154
|
91 (defmethod copy-key-signature ((key-signature standard-key-signature))
|
d@154
|
92 (make-instance (class-of key-signature)
|
d@154
|
93 :sharp-count (key-signature-sharps key-signature)
|
d@154
|
94 :mode (key-signature-mode key-signature)))
|
d@154
|
95 (defmethod copy-key-signature ((key-signature-period standard-key-signature-period))
|
d@154
|
96 (let ((sig (call-next-method)))
|
d@154
|
97 (setf (timepoint sig) (timepoint key-signature-period)
|
d@154
|
98 (duration sig) (duration key-signature-period))
|
d@154
|
99 sig))
|
d@154
|
100
|
d@34
|
101
|
d@34
|
102
|
d@34
|
103 ;; Allow derived sequences from remove-if, etc. to preserve other slot
|
d@34
|
104 ;; info (timesigs, etc)
|
d@154
|
105 #+nil
|
d@34
|
106 (defmethod sequence:make-sequence-like :around ((o midi-composition) length
|
d@34
|
107 &key (initial-element nil iep)
|
d@34
|
108 (initial-contents nil icp))
|
d@34
|
109 (declare (ignore length initial-element initial-contents))
|
d@34
|
110 (let ((result (call-next-method)))
|
d@34
|
111 (cond
|
d@34
|
112 ((or iep icp)
|
d@34
|
113 (setf (timepoint result) (timepoint (elt result 0))
|
d@34
|
114 (duration result) (- (timepoint
|
d@34
|
115 (loop for e being the elements of result
|
d@34
|
116 maximize (cut-off e)))
|
d@34
|
117 (timepoint (elt result 0)))))
|
d@34
|
118 (t (setf (timepoint result) 0
|
d@34
|
119 (duration result) 0)))
|
d@34
|
120 (with-slots (time-signatures tempi misc-controllers)
|
d@34
|
121 o
|
d@34
|
122 (setf (%midi-time-signatures result) time-signatures
|
d@34
|
123 (%midi-tempi result) tempi
|
d@34
|
124 (%midi-misc-controllers result) misc-controllers))
|
d@34
|
125 result))
|
d@153
|
126
|
d@34
|
127
|
d@34
|
128 ;; useful little function
|
d@34
|
129
|
d@34
|
130 (defun microsecond-per-crotchet-to-bpm (mu-per-c)
|
d@34
|
131 (/ 60000000 mu-per-c))
|
d@34
|
132
|
d@34
|
133 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
d@34
|
134 ;;
|
d@34
|
135 ;; MIDI playback methods
|
d@34
|
136
|
d@34
|
137 (defmethod get-patch-for-midi ((event midi-pitched-event))
|
d@34
|
138 ;; FIXME
|
d@34
|
139 (midi-patch event))
|
d@34
|
140
|
d@34
|
141 (defmethod get-channel-for-midi ((event midi-message))
|
d@34
|
142 ;; FIXME 1- ??? I'm only doing this because of the Geerdes
|
d@34
|
143 ;; database. Looks like a recipe for disaster. Think should probably
|
d@34
|
144 ;; enforce 0-15.
|
d@34
|
145 (1- (midi-channel event)))
|
d@34
|
146
|
d@34
|
147 (defmethod get-velocity-for-midi ((event midi-message))
|
d@34
|
148 ;; FIXME: under-exclusive specialisation. Does this matter?
|
d@34
|
149 (midi-velocity event))
|
d@34
|
150
|
d@34
|
151 (defmethod get-pitch-for-midi ((event midi-percussive-event))
|
d@34
|
152 (midi-drum-sound event))
|
d@34
|
153
|
d@34
|
154 (defmethod get-pitch-for-midi ((event midi-pitched-event))
|
d@41
|
155 (midi-pitch-number event))
|
d@41
|
156
|
d@41
|
157 ;; Have avoided percussion vs pitched, as this is more obviously
|
d@41
|
158 ;; meaningless.
|
d@41
|
159 (defmethod significantly-louderp ((event1 midi-pitched-event) (event2 midi-pitched-event))
|
d@41
|
160 (>= (/ (midi-velocity event1)
|
d@41
|
161 (midi-velocity event2))
|
d@41
|
162 4/3))
|
d@41
|
163 (defmethod significantly-louderp ((event1 midi-percussive-event) (event2 midi-percussive-event))
|
d@41
|
164 (>= (/ (midi-velocity event1)
|
d@41
|
165 (midi-velocity event2))
|
d@41
|
166 4/3))
|
d@41
|
167 (defmethod substantially-louderp ((event1 midi-pitched-event) (event2 midi-pitched-event))
|
d@41
|
168 (>= (/ (midi-velocity event1)
|
d@41
|
169 (midi-velocity event2))
|
d@41
|
170 2))
|
d@41
|
171 (defmethod substantially-louderp ((event1 midi-percussive-event) (event2 midi-percussive-event))
|
d@41
|
172 (>= (/ (midi-velocity event1)
|
d@41
|
173 (midi-velocity event2))
|
d@41
|
174 2))
|
d@114
|
175
|
d@130
|
176 (defmethod crotchet ((object midi-object))
|
d@139
|
177 (make-standard-period 1))
|
m@146
|
178
|
m@146
|
179 (defmethod monody ((identifier midifile-identifier))
|
m@146
|
180 (monody (get-composition identifier)))
|
m@146
|
181
|
m@146
|
182 ;; TODO: improve this naive first-cut at MONODY for midi files which
|
m@146
|
183 ;; simply selects a track which is both monodic (if any monodic tracks
|
m@146
|
184 ;; exist) and contains the highest pitch of any monodic track.
|
m@146
|
185 (defmethod monody ((c midi-composition))
|
m@146
|
186 (flet ((not-overlapping (track)
|
m@146
|
187 (let ((result t)
|
m@146
|
188 (track (sort (copy-list track) #'amuse:time<)))
|
m@146
|
189 (dotimes (i (1- (length track)) result)
|
m@146
|
190 (let ((e1 (elt track i))
|
m@146
|
191 (e2 (elt track (1+ i))))
|
m@146
|
192 (unless (or (before e1 e2) (meets e1 e2))
|
m@146
|
193 (setf result nil)))))))
|
m@146
|
194 (let ((tracks (make-hash-table))
|
m@146
|
195 (result nil)
|
m@146
|
196 (max-pitch 0))
|
m@146
|
197 (sequence:dosequence (message c)
|
m@146
|
198 (let* ((tracknum (amuse-midi:midi-track message))
|
m@146
|
199 (track (gethash tracknum tracks)))
|
m@146
|
200 (setf (gethash tracknum tracks) (cons message track))))
|
m@146
|
201 (maphash #'(lambda (k v)
|
m@146
|
202 (declare (ignore k))
|
m@146
|
203 (let ((max (apply #'max (mapcar #'midi-pitch-number v))))
|
m@146
|
204 (when (and (not-overlapping v) (> max max-pitch))
|
m@146
|
205 (setf result (sort v #'amuse:time<)
|
m@146
|
206 max-pitch max))))
|
m@146
|
207 tracks)
|
m@146
|
208 (when result
|
m@146
|
209 (let ((monody (make-instance 'midi-monody
|
m@146
|
210 :time (amuse:timepoint c)
|
m@146
|
211 :interval (amuse:duration c)
|
m@146
|
212 :time-signatures (time-signatures c)
|
m@146
|
213 :key-signatures (key-signatures c)
|
m@146
|
214 :tempi (tempi c)
|
m@146
|
215 :controllers (%midi-misc-controllers c))))
|
m@146
|
216 (sequence:adjust-sequence monody (length result)
|
m@146
|
217 :initial-contents result)
|
m@146
|
218 monody)))))
|
m@146
|
219
|