Mercurial > hg > amuse
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))))) |