annotate implementations/midi/midifile-import.lisp @ 212:619194befdd4

add identifier and timebase slots to midifile composition class Ignore-this: 4e72a0860344399452bea196c3739bbf darcs-hash:20090524150506-16a00-bce845ccf20a9289a4015fb58287f25ebfa178ad.gz committer: Jamie Forth <j.forth@gold.ac.uk>
author j.forth <j.forth@gold.ac.uk>
date Thu, 24 Feb 2011 11:23:17 +0000
parents 1f3873585a5d
children de60993404c1
rev   line source
d@35 1 (cl:in-package #:amuse-midi)
d@35 2
d@169 3 (defclass midifile-identifier (composition-identifier midi-object)
d@35 4 ((pathname :initarg :path
d@35 5 :reader midifile-identifier-pathname
d@134 6 :initform 'nil))
d@134 7 (:documentation "Identifier for MIDI files, containing pathname
d@134 8 information"))
d@35 9
d@35 10 (defun midifile-id (pathname)
d@134 11 "Creates an identifier for MIDI files, based on a pathname"
d@35 12 (make-instance 'midifile-identifier :path pathname))
d@35 13
d@35 14 (defmethod get-composition ((identifier midifile-identifier))
d@35 15 (%initialise-midifile-composition (midi:read-midi-file
j@212 16 (midifile-identifier-pathname identifier))
j@212 17 identifier))
d@35 18
j@212 19 (defun %initialise-midifile-composition (midifile identifier)
d@35 20 ;; Takes a midifile object (from the "MIDI" package)
d@35 21 ;; and returns an amuse midi object
d@35 22 ;; FIXME: gets it wrong if patch changes in mid-note
d@35 23 ;; FIXME: assumes controllers are global in scope and location
d@35 24 (let ((tracks (midi:midifile-tracks midifile))
d@35 25 (division (midi:midifile-division midifile))
d@115 26 (notes) (time-sigs) (key-sigs) (tempi) (misses 0)
d@115 27 (track-no -1) (last-time 0))
d@35 28 (dolist (track tracks)
d@35 29 (incf track-no)
d@35 30 (setf track (sort (copy-seq track)
d@35 31 #'(lambda (x y)
d@35 32 (or (< (midi:message-time x)
d@35 33 (midi:message-time y))
d@35 34 (and (= (midi:message-time x)
d@35 35 (midi:message-time y))
d@35 36 (typep x 'midi:note-off-message))))))
d@36 37 (let ((ons (make-array '(16 128) :initial-element nil))
d@35 38 (offs)
d@36 39 (patches (make-array 16 :initial-element 0)))
d@35 40 (dolist (event track)
d@35 41 (when (> (midi:message-time event) last-time)
d@35 42 (setf last-time (midi:message-time event)))
d@35 43 (cond
d@35 44 ((or (typep event 'midi:note-off-message)
d@35 45 (and (typep event 'midi:note-on-message)
d@35 46 (= (midi:message-velocity event) 0)))
d@35 47 (let ((pitch (midi:message-key event))
d@36 48 (channel (midi:message-channel event))
d@35 49 (t-off (midi:message-time event)))
d@35 50 (if (aref ons channel pitch)
m@145 51 (progn
m@145 52 (push (make-event-from-on-off-pair (aref ons channel pitch)
m@145 53 t-off
m@145 54 division
m@145 55 track-no
m@145 56 (aref patches channel))
m@145 57 notes)
m@145 58 (setf (aref ons channel pitch) nil))
d@35 59 ;; if there's no matching on, wait until the beat
d@35 60 ;; is done.
d@35 61 (push event offs))))
d@35 62 ((typep event 'midi:note-on-message)
d@35 63 (let ((pitch (midi:message-key event))
d@36 64 (channel (midi:message-channel event))
d@35 65 (t-off (midi:message-time event)))
d@35 66 (when (aref ons channel pitch)
d@35 67 ;; there's a note already sounding. End it.
d@35 68 (push (make-event-from-on-off-pair (aref ons channel pitch)
d@35 69 t-off
d@35 70 division
d@35 71 track-no
d@35 72 (aref patches channel))
d@35 73 notes))
d@35 74 (setf (aref ons channel pitch) event)))
d@35 75 ((typep event 'midi:time-signature-message)
d@35 76 ;; FIXME: Should I make a midi version of this object,
d@35 77 ;; with track/channel?
d@35 78 (when time-sigs
d@35 79 (setf (duration (car time-sigs))
d@35 80 (- (/ (midi:message-time event)
d@35 81 division)
d@35 82 (timepoint (car time-sigs)))))
d@136 83 (push (make-instance 'standard-time-signature-period
d@35 84 :time (/ (midi:message-time event)
d@35 85 division)
d@35 86 :numerator (midi:message-numerator event)
d@36 87 :denominator (expt 2 (midi:message-denominator event)))
d@35 88 time-sigs))
d@115 89 ((typep event 'midi:key-signature-message)
d@115 90 ;; FIXME: Should I make a midi version of this object,
d@115 91 ;; with track/channel? [probably, yes]
d@115 92 (when key-sigs
d@115 93 (setf (duration (car time-sigs))
d@115 94 (- (/ (midi:message-time event)
d@115 95 division)
d@115 96 (timepoint (car time-sigs)))))
d@136 97 (push (make-instance 'midi-key-signature-period
d@115 98 :time (/ (midi:message-time event)
d@115 99 division)
d@119 100 :sharp-count (midi:message-sf event)
d@119 101 :mode (midi:message-mi event))
d@115 102 key-sigs))
d@35 103 ((typep event 'midi:tempo-message)
d@35 104 (when tempi
d@35 105 (setf (duration (car tempi))
d@35 106 (- (/ (midi:message-time event)
d@35 107 division)
d@35 108 (timepoint (car tempi)))))
d@136 109 (push (make-instance 'standard-tempo-period
d@35 110 :time (/ (midi:message-time event)
d@35 111 division)
d@35 112 :bpm (microsecond-per-crotchet-to-bpm (midi:message-tempo event)))
d@35 113 tempi))
d@35 114 ((typep event 'midi:program-change-message)
d@36 115 (setf (aref patches (midi:message-channel event))
d@35 116 (midi:message-program event)))
d@35 117 (t (incf misses))))))
d@35 118 (when tempi
d@35 119 (setf (duration (car tempi)) (- (/ last-time division) (timepoint (car tempi)))))
d@35 120 (when time-sigs
d@35 121 (setf (duration (car time-sigs)) (- (/ last-time division) (timepoint (car time-sigs)))))
d@115 122 (when key-sigs
d@115 123 (setf (duration (car key-sigs)) (- (/ last-time division) (timepoint (car key-sigs)))))
d@35 124 ;; make a midi object from notes, etc.
d@35 125 (let ((composition (make-instance 'midi-composition
d@35 126 :time 0
d@35 127 :interval (/ last-time division)
d@36 128 :time-signatures (if time-sigs
d@36 129 (sort time-sigs #'time<)
d@136 130 (list (make-standard-time-signature-period
d@136 131 4 4 0 (/ last-time division))))
d@115 132 :tempi (sort tempi #'time<)
j@212 133 :key-signatures (sort key-sigs #'time<)
j@212 134 :identifier identifier
j@212 135 :midi-timebase division)))
d@35 136 (sequence:adjust-sequence composition
d@35 137 (length notes)
d@35 138 :initial-contents (sort notes #'time<)))))
d@35 139
d@35 140 (defun make-event-from-on-off-pair (note-on cut-off divisions track patch)
d@35 141 (cond
d@35 142 ((or (= (midi:message-channel note-on) 9)
d@35 143 (> patch 111))
d@35 144 ;; percussive
j@206 145 (make-midi-percussive-event (midi:message-key note-on)
j@206 146 (midi:message-velocity note-on)
j@206 147 patch
j@206 148 (1+ (midi:message-channel note-on))
j@206 149 track
j@206 150 (/ (midi:message-time note-on) divisions)
j@206 151 (/ (- cut-off (midi:message-time note-on))
j@206 152 divisions)))
d@35 153 (t
d@35 154 ;; pitched
j@206 155 (make-midi-pitched-event (midi:message-key note-on)
j@206 156 (midi:message-velocity note-on)
j@206 157 patch
j@206 158 (1+ (midi:message-channel note-on))
j@206 159 track
j@206 160 (/ (midi:message-time note-on) divisions)
j@206 161 (/ (- cut-off (midi:message-time note-on))
j@206 162 divisions)))))