diff 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
line wrap: on
line diff
--- a/MidiPlayer.java	Wed Nov 07 18:22:01 2012 +0000
+++ b/MidiPlayer.java	Mon Nov 12 22:32:44 2012 +0000
@@ -35,22 +35,28 @@
 import javax.sound.midi.SysexMessage;
 import javax.sound.midi.MetaMessage;
 import java.util.Arrays;
+import java.util.Iterator;
+
+
 public class MidiPlayer { 
 
     /* variables */ 
     private Sequencer   sequencer = null;
     private Synthesizer synthesizer = null;
     private Sequence sequence = null;
-    private ArrayList onsets, offsets, pitches = null; 
+    private ArrayList<Long> onsets, offsets = null;
+    private ArrayList<Integer> pitches, velocities = null; 
     private int midiDevice = 0; /* 4 for usb midi device */ 
+    private boolean debug;
 
     /* accessors */ 
-    public Sequencer getSequencer() { return sequencer; }
+    public Sequencer getSequencer() { return sequencer; }  
 
-    /* Constructor */ 
-    public MidiPlayer(String path, int deviceNumber) { 
+    /* Constructors */ 
+    public MidiPlayer(String path, int deviceNumber, boolean d) { 
         File midiFile = new File(path);
         midiDevice = deviceNumber;
+	debug = d;
 
         // Get sequence 
         try { sequence = MidiSystem.getSequence(midiFile); }
@@ -120,17 +126,23 @@
         }
 
         // compute data from the MIDI file 
+
+	/*
         onsets = computeOnsets(); 
         offsets = computeOffsets(); 
         pitches = computePitches(); 
+	*/
+	registerEvents();
 
-        //String divisionType; 
-        //if (sequence.getDivisionType() == sequence.PPQ)
-        //    divisionType = "ppq"; 
-        //else 
-        //    divisionType = "smpte"; 
-        //System.out.println("division type = " + divisionType + 
-        //                   "; resolution = " + sequence.getResolution()); 
+	if (debug) {
+	    String divisionType; 
+	    if (sequence.getDivisionType() == sequence.PPQ)
+		divisionType = "ppq"; 
+	    else 
+		divisionType = "smpte"; 
+	    System.out.println("division type = " + divisionType + 
+			       "; resolution = " + sequence.getResolution()); 
+	}
     }
 
     /* number of microseconds per MIDI tick */ 
@@ -224,6 +236,137 @@
         return pit; 
     }
 
+    /*
+     * Simultaneously construct lists of event onsets, offsets and pitches
+     */
+    private void registerEvents() {
+	onsets = new ArrayList<Long>();
+	offsets = new ArrayList<Long>();
+        pitches = new ArrayList<Integer>(); 
+        velocities = new ArrayList<Integer>(); 
+
+	Track[] tracks = sequence.getTracks();
+
+	// Iterate over MIDI tracks
+	for (int i = 0; i < tracks.length; i++) {
+	    Track track = tracks[i];
+	    // Iterate over track events
+	    for (int j = 0; j < track.size(); j++) {
+		registerEvent(i, j);
+	    }
+	}
+
+	if (debug) {
+	    System.out.println("\nRegistered events...");
+	    Iterator<Long> oi = onsets.iterator();
+	    Iterator<Long> fi = offsets.iterator();
+	    Iterator<Integer> pi = pitches.iterator();
+	    Iterator<Integer> vi = velocities.iterator();
+ 
+	    int pos = 1;
+	    while (oi.hasNext() && fi.hasNext() && pi.hasNext() && vi.hasNext()) {
+		System.out.println("Event " + pos + ": onset " + oi.next() + ", offset " + fi.next()
+				   + ", pitch " + pi.next() + ", velocity " + vi.next());
+		pos++;
+	    }
+
+	    if (oi.hasNext() || fi.hasNext() || pi.hasNext() || vi.hasNext()) {
+		System.out.println("Warning: event lists not equal length.");
+	    }
+	}
+    }
+
+    /*
+     * Add the given event to the onset, offset and pitch lists.
+     */
+    private void registerEvent(int trackIndex, int eventIndex) {
+
+	Track track = sequence.getTracks()[trackIndex];
+	MidiEvent event = track.get(eventIndex);
+	MidiMessage message = event.getMessage();
+
+	// Register NOTE_ON events
+	if (message instanceof ShortMessage) {
+	    ShortMessage shortMsg = (ShortMessage) message;
+	    int velocity = shortMsg.getData2();
+	    
+	    if (shortMsg.getCommand() == ShortMessage.NOTE_ON && velocity != 0) {
+		
+		long onset = ticksToMicroseconds(event.getTick());
+		int pitch = shortMsg.getData1();
+		long offset = getOffset(trackIndex, eventIndex, onset, pitch);
+		
+		registerEvent(onset, offset, pitch, velocity);
+	    }
+	}
+    }
+
+    /*
+     * Add the event details to the onset, offset and pitch lists,
+     * use onset to determine the list position.
+     */
+    private void registerEvent(long onset, long offset, int pitch, int velocity) {
+	
+	int index = 0;
+	boolean inserted = false;
+
+	while (index < onsets.size()) {
+
+	    if (onsets.get(index) > onset) 
+		break;
+	    else 
+		index++;
+	}
+
+	onsets.add(index, onset);
+	offsets.add(index, offset);
+	pitches.add(index, pitch);
+	velocities.add(index, pitch);
+    }
+
+
+
+    /*
+     * Find the matching NOTE_OFF event *after* the given NOTE_ON event
+     * (the <onsetIndex>th event in track <trackNum>)
+     */
+    private long getOffset(int trackNum, int onsetIndex, long onsetTime, int pitch) {
+	
+	long offset = -1;	
+	Track track = sequence.getTracks()[trackNum];
+
+	// Iterate through remaining events looking for NOTE_OFF
+	for (int k = onsetIndex + 1; k < track.size(); k++) {
+	    MidiEvent event = track.get(k);
+	    MidiMessage message = event.getMessage();
+	    
+	    if (message instanceof ShortMessage) {
+		ShortMessage shortMsg = (ShortMessage) message;
+		int command = shortMsg.getCommand();
+
+		// Check NOTE_OFF message
+		if (command == ShortMessage.NOTE_OFF
+		    || (command == ShortMessage.NOTE_ON && shortMsg.getData2() == 0)) {
+		    int pitch2 = shortMsg.getData1();
+		    // If pitches are identical then offset found
+		    if (pitch == pitch2) {
+			offset = ticksToMicroseconds(event.getTick());
+			break;
+		    }   
+		}
+	    }
+	}
+
+	if (offset < 0) {
+	    System.out.println("No NOTE_OFF found for track " + trackNum
+			       + ", onset " + onsetTime + ", pitch " + pitch);
+	}
+
+	return offset;
+    }
+
+
+
     /* return a list of note onset times (milliseconds) */ 
     public ArrayList getOnsets() { return onsets; } 
 
@@ -278,7 +421,10 @@
         return deltaST; 
     } 
     
-    /* return the tatum of the midi file */ 
+    /* 
+     * Return the tatum of the midi file, i.e. the shortest
+     * note duration or positive rest.
+     */ 
     public long getTatum() {
     
 
@@ -286,13 +432,15 @@
         Object[] rests = getDeltaST().toArray(); 
         long tatum = -1; 
 
+
         for(int i = 0; i < durs.length; i++) { 
             long dur = ((Long)durs[i]).longValue(); 
             long rest = ((Long)rests[i]).longValue(); 
             long min = dur; 
-            if (rest != 0)
+            if (rest > 0)
                 min = Math.min(dur, rest);
-            System.out.println("Min: " + min);
+            if (debug)
+		System.out.println("Dur: " + dur + "    Rest: " + rest);
             if (tatum < 0)
                 tatum = dur; 
             else if (min < tatum)