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 }