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