comparison implementations/midi/midifile-import.lisp @ 35:1d757c33e00e

Changes for midi file import darcs-hash:20070502153016-f76cc-89b748c36180ccaca77d2a70a65a6e7f77df8d43.gz
author David Lewis <d.lewis@gold.ac.uk>
date Wed, 02 May 2007 16:30:16 +0100
parents
children ad321ce17e3e
comparison
equal deleted inserted replaced
34:81b4228e26f5 35:1d757c33e00e
1 (cl:in-package #:amuse-midi)
2
3 (defclass midifile-identifier (identifier)
4 ((pathname :initarg :path
5 :reader midifile-identifier-pathname
6 :initform 'nil)))
7
8 (defun midifile-id (pathname)
9 (make-instance 'midifile-identifier :path pathname))
10
11 (defmethod get-composition ((identifier midifile-identifier))
12 (%initialise-midifile-composition (midi:read-midi-file
13 (midifile-identifier-pathname identifier))))
14
15 (defun %initialise-midifile-composition (midifile)
16 ;; Takes a midifile object (from the "MIDI" package)
17 ;; and returns an amuse midi object
18 ;; FIXME: gets it wrong if patch changes in mid-note
19 ;; FIXME: assumes controllers are global in scope and location
20 (let ((tracks (midi:midifile-tracks midifile))
21 (division (midi:midifile-division midifile))
22 (notes) (time-sigs) (tempi) (misses 0) (track-no -1) (last-time 0))
23 (dolist (track tracks)
24 (incf track-no)
25 (setf track (sort (copy-seq track)
26 #'(lambda (x y)
27 (or (< (midi:message-time x)
28 (midi:message-time y))
29 (and (= (midi:message-time x)
30 (midi:message-time y))
31 (typep x 'midi:note-off-message))))))
32 (let ((ons (make-array '(17 128) :initial-element nil))
33 (offs)
34 (patches (make-array 17 :initial-element 0)))
35 (dolist (event track)
36 (when (> (midi:message-time event) last-time)
37 (setf last-time (midi:message-time event)))
38 (cond
39 ((or (typep event 'midi:note-off-message)
40 (and (typep event 'midi:note-on-message)
41 (= (midi:message-velocity event) 0)))
42 (let ((pitch (midi:message-key event))
43 (channel (1+ (midi:message-channel event)))
44 (t-off (midi:message-time event)))
45 (if (aref ons channel pitch)
46 (push (make-event-from-on-off-pair (aref ons channel pitch)
47 t-off
48 division
49 track-no
50 (aref patches channel))
51 notes)
52 ;; if there's no matching on, wait until the beat
53 ;; is done.
54 (push event offs))))
55 ((typep event 'midi:note-on-message)
56 (let ((pitch (midi:message-key event))
57 (channel (1+ (midi:message-channel event)))
58 (t-off (midi:message-time event)))
59 (when (aref ons channel pitch)
60 ;; there's a note already sounding. End it.
61 (push (make-event-from-on-off-pair (aref ons channel pitch)
62 t-off
63 division
64 track-no
65 (aref patches channel))
66 notes))
67 (setf (aref ons channel pitch) event)))
68 ((typep event 'midi:time-signature-message)
69 ;; FIXME: Should I make a midi version of this object,
70 ;; with track/channel?
71 (when time-sigs
72 (setf (duration (car time-sigs))
73 (- (/ (midi:message-time event)
74 division)
75 (timepoint (car time-sigs)))))
76 (push (make-instance 'basic-time-signature
77 :time (/ (midi:message-time event)
78 division)
79 :numerator (midi:message-numerator event)
80 :denominator (midi:message-denominator event))
81 time-sigs))
82 ((typep event 'midi:tempo-message)
83 (when tempi
84 (setf (duration (car tempi))
85 (- (/ (midi:message-time event)
86 division)
87 (timepoint (car tempi)))))
88 (push (make-instance 'tempo
89 :time (/ (midi:message-time event)
90 division)
91 :bpm (microsecond-per-crotchet-to-bpm (midi:message-tempo event)))
92 tempi))
93 ((typep event 'midi:program-change-message)
94 (setf (aref patches (1+ (midi:message-channel event)))
95 (midi:message-program event)))
96 (t (incf misses))))))
97 (when tempi
98 (setf (duration (car tempi)) (- (/ last-time division) (timepoint (car tempi)))))
99 (when time-sigs
100 (setf (duration (car time-sigs)) (- (/ last-time division) (timepoint (car time-sigs)))))
101 ;; make a midi object from notes, etc.
102 (let ((composition (make-instance 'midi-composition
103 :time 0
104 :interval (/ last-time division)
105 :time-signatures (sort time-sigs #'time<)
106 :tempi (sort tempi #'time<))))
107 (sequence:adjust-sequence composition
108 (length notes)
109 :initial-contents (sort notes #'time<)))))
110
111 (defparameter *short* nil)
112
113 (defun make-event-from-on-off-pair (note-on cut-off divisions track patch)
114 (when (< (/ (- cut-off (midi:message-time note-on)) divisions) 1/8)
115 (push (cons note-on cut-off) *short*))
116 (cond
117 ((or (= (midi:message-channel note-on) 9)
118 (> patch 111))
119 ;; percussive
120 (make-instance 'midi-percussive-event
121 :channel (1+ (midi:message-channel note-on))
122 :track track
123 :time (/ (midi:message-time note-on) divisions)
124 :interval (/ (- cut-off (midi:message-time note-on))
125 divisions)
126 :velocity (midi:message-velocity note-on)
127 :patch patch
128 :sound (midi:message-key note-on)))
129 (t
130 ;; pitched
131 (make-instance 'midi-pitched-event
132 :channel (1+ (midi:message-channel note-on))
133 :track track
134 :time (/ (midi:message-time note-on) divisions)
135 :interval (/ (- cut-off (midi:message-time note-on))
136 divisions)
137 :velocity (midi:message-velocity note-on)
138 :patch patch
139 :number (midi:message-key note-on)))))