annotate implementations/midi/methods.lisp @ 154:edf2322ea33f

Added copy key and time sigs and tempo. Need to do general version. Only midi-type currently darcs-hash:20071119173147-40ec0-779ffe4dc4bbc356cc6decf6d0f50cb42f943b83.gz
author d.lewis <d.lewis@gold.ac.uk>
date Mon, 19 Nov 2007 17:31:47 +0000
parents 74fc4c6cbf6c
children fc6848dda767
rev   line source
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