Mercurial > hg > mep
comparison MidiPlayer.java @ 24:7102e646b223
Sort notes into onset order. Fix incorrect tatums caused by negative rest values.
author | Jeremy Gow <jeremy.gow@gmail.com> |
---|---|
date | Mon, 12 Nov 2012 22:32:44 +0000 |
parents | 9fc8683b8fed |
children | 796b3e3e053f |
comparison
equal
deleted
inserted
replaced
23:9fc8683b8fed | 24:7102e646b223 |
---|---|
33 import javax.sound.midi.ShortMessage; | 33 import javax.sound.midi.ShortMessage; |
34 import javax.sound.midi.MetaMessage; | 34 import javax.sound.midi.MetaMessage; |
35 import javax.sound.midi.SysexMessage; | 35 import javax.sound.midi.SysexMessage; |
36 import javax.sound.midi.MetaMessage; | 36 import javax.sound.midi.MetaMessage; |
37 import java.util.Arrays; | 37 import java.util.Arrays; |
38 import java.util.Iterator; | |
39 | |
40 | |
38 public class MidiPlayer { | 41 public class MidiPlayer { |
39 | 42 |
40 /* variables */ | 43 /* variables */ |
41 private Sequencer sequencer = null; | 44 private Sequencer sequencer = null; |
42 private Synthesizer synthesizer = null; | 45 private Synthesizer synthesizer = null; |
43 private Sequence sequence = null; | 46 private Sequence sequence = null; |
44 private ArrayList onsets, offsets, pitches = null; | 47 private ArrayList<Long> onsets, offsets = null; |
48 private ArrayList<Integer> pitches, velocities = null; | |
45 private int midiDevice = 0; /* 4 for usb midi device */ | 49 private int midiDevice = 0; /* 4 for usb midi device */ |
50 private boolean debug; | |
46 | 51 |
47 /* accessors */ | 52 /* accessors */ |
48 public Sequencer getSequencer() { return sequencer; } | 53 public Sequencer getSequencer() { return sequencer; } |
49 | 54 |
50 /* Constructor */ | 55 /* Constructors */ |
51 public MidiPlayer(String path, int deviceNumber) { | 56 public MidiPlayer(String path, int deviceNumber, boolean d) { |
52 File midiFile = new File(path); | 57 File midiFile = new File(path); |
53 midiDevice = deviceNumber; | 58 midiDevice = deviceNumber; |
59 debug = d; | |
54 | 60 |
55 // Get sequence | 61 // Get sequence |
56 try { sequence = MidiSystem.getSequence(midiFile); } | 62 try { sequence = MidiSystem.getSequence(midiFile); } |
57 catch (InvalidMidiDataException e) { | 63 catch (InvalidMidiDataException e) { |
58 e.printStackTrace(); | 64 e.printStackTrace(); |
118 catch (MidiUnavailableException e) { | 124 catch (MidiUnavailableException e) { |
119 e.printStackTrace(); | 125 e.printStackTrace(); |
120 } | 126 } |
121 | 127 |
122 // compute data from the MIDI file | 128 // compute data from the MIDI file |
129 | |
130 /* | |
123 onsets = computeOnsets(); | 131 onsets = computeOnsets(); |
124 offsets = computeOffsets(); | 132 offsets = computeOffsets(); |
125 pitches = computePitches(); | 133 pitches = computePitches(); |
126 | 134 */ |
127 //String divisionType; | 135 registerEvents(); |
128 //if (sequence.getDivisionType() == sequence.PPQ) | 136 |
129 // divisionType = "ppq"; | 137 if (debug) { |
130 //else | 138 String divisionType; |
131 // divisionType = "smpte"; | 139 if (sequence.getDivisionType() == sequence.PPQ) |
132 //System.out.println("division type = " + divisionType + | 140 divisionType = "ppq"; |
133 // "; resolution = " + sequence.getResolution()); | 141 else |
142 divisionType = "smpte"; | |
143 System.out.println("division type = " + divisionType + | |
144 "; resolution = " + sequence.getResolution()); | |
145 } | |
134 } | 146 } |
135 | 147 |
136 /* number of microseconds per MIDI tick */ | 148 /* number of microseconds per MIDI tick */ |
137 private double microsecondsPerTick() { | 149 private double microsecondsPerTick() { |
138 double seqTickLength = (float)sequence.getTickLength(); | 150 double seqTickLength = (float)sequence.getTickLength(); |
222 } | 234 } |
223 } | 235 } |
224 return pit; | 236 return pit; |
225 } | 237 } |
226 | 238 |
239 /* | |
240 * Simultaneously construct lists of event onsets, offsets and pitches | |
241 */ | |
242 private void registerEvents() { | |
243 onsets = new ArrayList<Long>(); | |
244 offsets = new ArrayList<Long>(); | |
245 pitches = new ArrayList<Integer>(); | |
246 velocities = new ArrayList<Integer>(); | |
247 | |
248 Track[] tracks = sequence.getTracks(); | |
249 | |
250 // Iterate over MIDI tracks | |
251 for (int i = 0; i < tracks.length; i++) { | |
252 Track track = tracks[i]; | |
253 // Iterate over track events | |
254 for (int j = 0; j < track.size(); j++) { | |
255 registerEvent(i, j); | |
256 } | |
257 } | |
258 | |
259 if (debug) { | |
260 System.out.println("\nRegistered events..."); | |
261 Iterator<Long> oi = onsets.iterator(); | |
262 Iterator<Long> fi = offsets.iterator(); | |
263 Iterator<Integer> pi = pitches.iterator(); | |
264 Iterator<Integer> vi = velocities.iterator(); | |
265 | |
266 int pos = 1; | |
267 while (oi.hasNext() && fi.hasNext() && pi.hasNext() && vi.hasNext()) { | |
268 System.out.println("Event " + pos + ": onset " + oi.next() + ", offset " + fi.next() | |
269 + ", pitch " + pi.next() + ", velocity " + vi.next()); | |
270 pos++; | |
271 } | |
272 | |
273 if (oi.hasNext() || fi.hasNext() || pi.hasNext() || vi.hasNext()) { | |
274 System.out.println("Warning: event lists not equal length."); | |
275 } | |
276 } | |
277 } | |
278 | |
279 /* | |
280 * Add the given event to the onset, offset and pitch lists. | |
281 */ | |
282 private void registerEvent(int trackIndex, int eventIndex) { | |
283 | |
284 Track track = sequence.getTracks()[trackIndex]; | |
285 MidiEvent event = track.get(eventIndex); | |
286 MidiMessage message = event.getMessage(); | |
287 | |
288 // Register NOTE_ON events | |
289 if (message instanceof ShortMessage) { | |
290 ShortMessage shortMsg = (ShortMessage) message; | |
291 int velocity = shortMsg.getData2(); | |
292 | |
293 if (shortMsg.getCommand() == ShortMessage.NOTE_ON && velocity != 0) { | |
294 | |
295 long onset = ticksToMicroseconds(event.getTick()); | |
296 int pitch = shortMsg.getData1(); | |
297 long offset = getOffset(trackIndex, eventIndex, onset, pitch); | |
298 | |
299 registerEvent(onset, offset, pitch, velocity); | |
300 } | |
301 } | |
302 } | |
303 | |
304 /* | |
305 * Add the event details to the onset, offset and pitch lists, | |
306 * use onset to determine the list position. | |
307 */ | |
308 private void registerEvent(long onset, long offset, int pitch, int velocity) { | |
309 | |
310 int index = 0; | |
311 boolean inserted = false; | |
312 | |
313 while (index < onsets.size()) { | |
314 | |
315 if (onsets.get(index) > onset) | |
316 break; | |
317 else | |
318 index++; | |
319 } | |
320 | |
321 onsets.add(index, onset); | |
322 offsets.add(index, offset); | |
323 pitches.add(index, pitch); | |
324 velocities.add(index, pitch); | |
325 } | |
326 | |
327 | |
328 | |
329 /* | |
330 * Find the matching NOTE_OFF event *after* the given NOTE_ON event | |
331 * (the <onsetIndex>th event in track <trackNum>) | |
332 */ | |
333 private long getOffset(int trackNum, int onsetIndex, long onsetTime, int pitch) { | |
334 | |
335 long offset = -1; | |
336 Track track = sequence.getTracks()[trackNum]; | |
337 | |
338 // Iterate through remaining events looking for NOTE_OFF | |
339 for (int k = onsetIndex + 1; k < track.size(); k++) { | |
340 MidiEvent event = track.get(k); | |
341 MidiMessage message = event.getMessage(); | |
342 | |
343 if (message instanceof ShortMessage) { | |
344 ShortMessage shortMsg = (ShortMessage) message; | |
345 int command = shortMsg.getCommand(); | |
346 | |
347 // Check NOTE_OFF message | |
348 if (command == ShortMessage.NOTE_OFF | |
349 || (command == ShortMessage.NOTE_ON && shortMsg.getData2() == 0)) { | |
350 int pitch2 = shortMsg.getData1(); | |
351 // If pitches are identical then offset found | |
352 if (pitch == pitch2) { | |
353 offset = ticksToMicroseconds(event.getTick()); | |
354 break; | |
355 } | |
356 } | |
357 } | |
358 } | |
359 | |
360 if (offset < 0) { | |
361 System.out.println("No NOTE_OFF found for track " + trackNum | |
362 + ", onset " + onsetTime + ", pitch " + pitch); | |
363 } | |
364 | |
365 return offset; | |
366 } | |
367 | |
368 | |
369 | |
227 /* return a list of note onset times (milliseconds) */ | 370 /* return a list of note onset times (milliseconds) */ |
228 public ArrayList getOnsets() { return onsets; } | 371 public ArrayList getOnsets() { return onsets; } |
229 | 372 |
230 /* return a list of note offset times (milliseconds) */ | 373 /* return a list of note offset times (milliseconds) */ |
231 public ArrayList getOffsets() { return offsets; } | 374 public ArrayList getOffsets() { return offsets; } |
276 ((Long)iois[i]).longValue()); | 419 ((Long)iois[i]).longValue()); |
277 } | 420 } |
278 return deltaST; | 421 return deltaST; |
279 } | 422 } |
280 | 423 |
281 /* return the tatum of the midi file */ | 424 /* |
425 * Return the tatum of the midi file, i.e. the shortest | |
426 * note duration or positive rest. | |
427 */ | |
282 public long getTatum() { | 428 public long getTatum() { |
283 | 429 |
284 | 430 |
285 Object[] durs = getDurations().toArray(); | 431 Object[] durs = getDurations().toArray(); |
286 Object[] rests = getDeltaST().toArray(); | 432 Object[] rests = getDeltaST().toArray(); |
287 long tatum = -1; | 433 long tatum = -1; |
434 | |
288 | 435 |
289 for(int i = 0; i < durs.length; i++) { | 436 for(int i = 0; i < durs.length; i++) { |
290 long dur = ((Long)durs[i]).longValue(); | 437 long dur = ((Long)durs[i]).longValue(); |
291 long rest = ((Long)rests[i]).longValue(); | 438 long rest = ((Long)rests[i]).longValue(); |
292 long min = dur; | 439 long min = dur; |
293 if (rest != 0) | 440 if (rest > 0) |
294 min = Math.min(dur, rest); | 441 min = Math.min(dur, rest); |
295 System.out.println("Min: " + min); | 442 if (debug) |
443 System.out.println("Dur: " + dur + " Rest: " + rest); | |
296 if (tatum < 0) | 444 if (tatum < 0) |
297 tatum = dur; | 445 tatum = dur; |
298 else if (min < tatum) | 446 else if (min < tatum) |
299 tatum = min; | 447 tatum = min; |
300 } | 448 } |