Mercurial > hg > mep
diff MidiPlayer.java @ 0:4031cbb02f08
Initial import.
Ignore-this: 87317e384f22bde48db996355191fa5f
author | Marcus Pearce <m.pearce@gold.ac.uk> |
---|---|
date | Tue, 18 May 2010 11:37:10 +0100 |
parents | |
children | 93ed757b9871 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MidiPlayer.java Tue May 18 11:37:10 2010 +0100 @@ -0,0 +1,304 @@ +/*============================================================================= + * File: MidiPlayer.java + * Author: Marcus Pearce <m.pearce@gold.ac.uk> + * Created: <2007-02-14 12:13:56 marcusp> + * Time-stamp: <2008-10-20 16:52:05 marcusp> + *============================================================================= + */ + +/* + * Based on: + * http://www.jsresources.org/examples/SimpleMidiPlayer.html + * http://www.jsresources.org/examples/DumpSequence.html + */ + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; + +import javax.sound.midi.MidiSystem; +import javax.sound.midi.MidiDevice; +import javax.sound.midi.InvalidMidiDataException; +import javax.sound.midi.MidiUnavailableException; +import javax.sound.midi.MetaEventListener; +import javax.sound.midi.Sequence; +import javax.sound.midi.Sequencer; +import javax.sound.midi.Synthesizer; +import javax.sound.midi.MidiChannel; +import javax.sound.midi.Receiver; +import javax.sound.midi.Transmitter; +import javax.sound.midi.Track; +import javax.sound.midi.MidiEvent; +import javax.sound.midi.MidiMessage; +import javax.sound.midi.ShortMessage; +import javax.sound.midi.MetaMessage; +import javax.sound.midi.SysexMessage; +import javax.sound.midi.MetaMessage; + +public class MidiPlayer { + + /* variables */ + private Sequencer sequencer = null; + private Synthesizer synthesizer = null; + private Sequence sequence = null; + private ArrayList onsets, offsets, pitches = null; + private int midiDevice = 0; /* 4 for usb midi device */ + + /* accessors */ + public Sequencer getSequencer() { return sequencer; } + + /* Constructor */ + public MidiPlayer(String path) { + File midiFile = new File(path); + + // Get sequence + try { sequence = MidiSystem.getSequence(midiFile); } + catch (InvalidMidiDataException e) { + e.printStackTrace(); + System.exit(1); + } + catch (IOException e) { + e.printStackTrace(); + System.exit(1); + } + // Get sequencer + try { sequencer = MidiSystem.getSequencer(); } + catch (MidiUnavailableException e) { + e.printStackTrace(); + System.exit(1); + } + //sequencer.setTempoInBPM(bpm); + // Workaround bug in JDK +// sequencer.addMetaEventListener(new MetaEventListener() { +// public void meta(MetaMessage event) { +// if (event.getType() == 47) { +// sequencer.close(); +// if (synthesizer != null) { +// synthesizer.close(); +// } +// } +// } +// }); + // Open sequencer + try { sequencer.open(); } + catch (MidiUnavailableException e) { + e.printStackTrace(); + System.exit(1); + } + // Assign sequence to sequencer + try { sequencer.setSequence(sequence); } + catch (InvalidMidiDataException e) { + e.printStackTrace(); + System.exit(1); + } + // Set up MIDI output for sequence + try { + MidiDevice.Info msinfo[] = MidiSystem.getMidiDeviceInfo(); + for(int i = 0; i < msinfo.length; i++) { + MidiDevice.Info m = msinfo[i]; +// System.out.println("Name: " + m.getName() + +// "; Vendor: " + m.getVendor() + +// "; Version: " + m.getVersion()); + } + // synthesizer = MidiSystem.getSynthesizer(); + MidiDevice synth = MidiSystem.getMidiDevice(msinfo[midiDevice]); + + // Change the patch + // MidiChannel channels[] = synthesizer.getChannels(); + // for (int i = 0; i < channels.length; i++) + // channels[i].programChange(65); + + synth.open(); + Receiver synthReceiver = synth.getReceiver(); + Transmitter seqTransmitter = sequencer.getTransmitter(); + seqTransmitter.setReceiver(synthReceiver); + + } + catch (MidiUnavailableException e) { + e.printStackTrace(); + } + + // compute data from the MIDI file + onsets = computeOnsets(); + offsets = computeOffsets(); + pitches = computePitches(); + + //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 */ + private double microsecondsPerTick() { + double seqTickLength = (float)sequence.getTickLength(); + double seqMicrosecondLength = (float)sequence.getMicrosecondLength(); + double microsecondsPerTick = seqMicrosecondLength / seqTickLength; + //System.out.println("seqTickLength = " + seqTickLength); + //System.out.println("seqMicrosecondLength = " + seqMicrosecondLength); + //System.out.println("microsecondsPerTick = " + microsecondsPerTick); + return microsecondsPerTick; + } + + private long ticksToMicroseconds(long tick) { + double microsecondsPerTick = microsecondsPerTick(); + double seconds = (double)tick * microsecondsPerTick; + + return (long)(10 * Math.floor(seconds * 0.1)); + } + + /* compute a list of note onset times (microseconds) */ + private ArrayList computeOnsets() { + ArrayList ons = new ArrayList(); + Track[] tracks = sequence.getTracks(); + + for (int nTrack = 0; nTrack < tracks.length; nTrack++) { + Track track = tracks[nTrack]; + for (int nEvent = 0; nEvent < track.size(); nEvent++) { + MidiEvent event = track.get(nEvent); + MidiMessage message = event.getMessage(); + if (message instanceof ShortMessage && + ((ShortMessage)message).getCommand() == ShortMessage.NOTE_ON) { +// System.out.println("onset in ticks = " + event.getTick()+ +// "; onset in microseconds = " + +// ticksToMicroseconds(event.getTick())); + ons.add(ticksToMicroseconds(event.getTick())); + } + } + } + return ons; + } + + /* compute a list of note offset times (microseconds) */ + private ArrayList computeOffsets() { + ArrayList offs = new ArrayList(); + Track[] tracks = sequence.getTracks(); + + for (int nTrack = 0; nTrack < tracks.length; nTrack++) { + Track track = tracks[nTrack]; + for (int nEvent = 0; nEvent < track.size(); nEvent++) { + MidiEvent event = track.get(nEvent); + MidiMessage message = event.getMessage(); + if (message instanceof ShortMessage && + ((ShortMessage)message).getCommand() == ShortMessage.NOTE_OFF) { + //System.out.println("offset in ticks = " + event.getTick()+ + // "; microsecondsPerTick = " + microsecondsPerTick + + // "; offset in microseconds = " + + // (float)event.getTick() * microsecondsPerTick); + offs.add(ticksToMicroseconds(event.getTick())); + } + } + } + return offs; + } + + /* compute a list of note pitches */ + private ArrayList computePitches() { + ArrayList pit = new ArrayList(); + Track[] tracks = sequence.getTracks(); + + for (int nTrack = 0; nTrack < tracks.length; nTrack++) { + Track track = tracks[nTrack]; + for (int nEvent = 0; nEvent < track.size(); nEvent++) { + MidiEvent event = track.get(nEvent); + MidiMessage message = event.getMessage(); + if (message instanceof ShortMessage && + ((ShortMessage)message).getCommand() == ShortMessage.NOTE_ON) + pit.add(((ShortMessage)message).getData1()); + } + } + return pit; + } + + /* return a list of note onset times (milliseconds) */ + public ArrayList getOnsets() { return onsets; } + + /* return a list of note offset times (milliseconds) */ + public ArrayList getOffsets() { return offsets; } + + /* return a list of note pitches */ + public ArrayList getPitches() { return pitches; } + + /* return a list of note durations (microseconds) */ + public ArrayList getDurations() { + Object[] ons = onsets.toArray(); + Object[] offs = offsets.toArray(); + + ArrayList durations = new ArrayList(); + + for(int i = 0; i < ons.length; i++) { + durations.add(((Long)offs[i]).longValue() - + ((Long)ons[i]).longValue()); + } + return durations; + } + + /* return a list of inter-onset intervals (microseconds) */ + public ArrayList getInterOnsetIntervals() { + Object[] ons = onsets.toArray(); + + ArrayList iois = new ArrayList(); + long firstIOI = 0; + iois.add(firstIOI); // IOI of first note is zero + + for(int i = 1; i < ons.length; i++) { + iois.add(((Long)ons[i]).longValue() - + ((Long)ons[i-1]).longValue()); + } + return iois; + } + + /* return the length of a rest preceding a note */ + public ArrayList getDeltaST() { + Object[] durs = getDurations().toArray(); + Object[] iois = getInterOnsetIntervals().toArray(); + + ArrayList deltaST = new ArrayList(); + long firstDeltaST = 0; + deltaST.add(firstDeltaST); // deltaST of first note is zero + + for(int i = 1; i < durs.length; i++) { + deltaST.add(((Long)durs[i-1]).longValue() - + ((Long)iois[i]).longValue()); + } + return deltaST; + } + + /* return the tatum of the midi file */ + public long getTatum() { + Object[] durs = getDurations().toArray(); + 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) + min = Math.min(dur, rest); + + if (tatum < 0) + tatum = dur; + else if (min < tatum) + tatum = min; + } + return tatum; + } + + /* return length of sequence in microseconds */ + public long getLength() { + return sequence.getMicrosecondLength(); + } + + /* play the midi file */ + public void play() { + sequencer.start(); + } + + public void stop() { + sequencer.close(); + } +}