annotate MidiPlayer.java @ 1:93ed757b9871

Add midi device as a command line parameter. Ignore-this: a6c745a4b262ed92195abba61880f758
author Marcus Pearce <m.pearce@gold.ac.uk>
date Tue, 08 Jun 2010 17:30:07 +0100
parents 4031cbb02f08
children 5080b65e6963
rev   line source
m@0 1 /*=============================================================================
m@0 2 * File: MidiPlayer.java
m@0 3 * Author: Marcus Pearce <m.pearce@gold.ac.uk>
m@0 4 * Created: <2007-02-14 12:13:56 marcusp>
m@1 5 * Time-stamp: <2010-06-08 16:12:05 marcusp>
m@0 6 *=============================================================================
m@0 7 */
m@0 8
m@0 9 /*
m@0 10 * Based on:
m@0 11 * http://www.jsresources.org/examples/SimpleMidiPlayer.html
m@0 12 * http://www.jsresources.org/examples/DumpSequence.html
m@0 13 */
m@0 14
m@0 15 import java.io.File;
m@0 16 import java.io.IOException;
m@0 17 import java.util.ArrayList;
m@0 18
m@0 19 import javax.sound.midi.MidiSystem;
m@0 20 import javax.sound.midi.MidiDevice;
m@0 21 import javax.sound.midi.InvalidMidiDataException;
m@0 22 import javax.sound.midi.MidiUnavailableException;
m@0 23 import javax.sound.midi.MetaEventListener;
m@0 24 import javax.sound.midi.Sequence;
m@0 25 import javax.sound.midi.Sequencer;
m@0 26 import javax.sound.midi.Synthesizer;
m@0 27 import javax.sound.midi.MidiChannel;
m@0 28 import javax.sound.midi.Receiver;
m@0 29 import javax.sound.midi.Transmitter;
m@0 30 import javax.sound.midi.Track;
m@0 31 import javax.sound.midi.MidiEvent;
m@0 32 import javax.sound.midi.MidiMessage;
m@0 33 import javax.sound.midi.ShortMessage;
m@0 34 import javax.sound.midi.MetaMessage;
m@0 35 import javax.sound.midi.SysexMessage;
m@0 36 import javax.sound.midi.MetaMessage;
m@0 37
m@0 38 public class MidiPlayer {
m@0 39
m@0 40 /* variables */
m@0 41 private Sequencer sequencer = null;
m@0 42 private Synthesizer synthesizer = null;
m@0 43 private Sequence sequence = null;
m@0 44 private ArrayList onsets, offsets, pitches = null;
m@0 45 private int midiDevice = 0; /* 4 for usb midi device */
m@0 46
m@0 47 /* accessors */
m@0 48 public Sequencer getSequencer() { return sequencer; }
m@0 49
m@0 50 /* Constructor */
m@1 51 public MidiPlayer(String path, int deviceNumber) {
m@0 52 File midiFile = new File(path);
m@1 53 midiDevice = deviceNumber;
m@0 54
m@0 55 // Get sequence
m@0 56 try { sequence = MidiSystem.getSequence(midiFile); }
m@0 57 catch (InvalidMidiDataException e) {
m@0 58 e.printStackTrace();
m@0 59 System.exit(1);
m@0 60 }
m@0 61 catch (IOException e) {
m@0 62 e.printStackTrace();
m@0 63 System.exit(1);
m@0 64 }
m@0 65 // Get sequencer
m@0 66 try { sequencer = MidiSystem.getSequencer(); }
m@0 67 catch (MidiUnavailableException e) {
m@0 68 e.printStackTrace();
m@0 69 System.exit(1);
m@0 70 }
m@0 71 //sequencer.setTempoInBPM(bpm);
m@0 72 // Workaround bug in JDK
m@0 73 // sequencer.addMetaEventListener(new MetaEventListener() {
m@0 74 // public void meta(MetaMessage event) {
m@0 75 // if (event.getType() == 47) {
m@0 76 // sequencer.close();
m@0 77 // if (synthesizer != null) {
m@0 78 // synthesizer.close();
m@0 79 // }
m@0 80 // }
m@0 81 // }
m@0 82 // });
m@0 83 // Open sequencer
m@0 84 try { sequencer.open(); }
m@0 85 catch (MidiUnavailableException e) {
m@0 86 e.printStackTrace();
m@0 87 System.exit(1);
m@0 88 }
m@0 89 // Assign sequence to sequencer
m@0 90 try { sequencer.setSequence(sequence); }
m@0 91 catch (InvalidMidiDataException e) {
m@0 92 e.printStackTrace();
m@0 93 System.exit(1);
m@0 94 }
m@0 95 // Set up MIDI output for sequence
m@0 96 try {
m@0 97 MidiDevice.Info msinfo[] = MidiSystem.getMidiDeviceInfo();
m@0 98 for(int i = 0; i < msinfo.length; i++) {
m@0 99 MidiDevice.Info m = msinfo[i];
m@0 100 // System.out.println("Name: " + m.getName() +
m@0 101 // "; Vendor: " + m.getVendor() +
m@0 102 // "; Version: " + m.getVersion());
m@0 103 }
m@0 104 // synthesizer = MidiSystem.getSynthesizer();
m@0 105 MidiDevice synth = MidiSystem.getMidiDevice(msinfo[midiDevice]);
m@0 106
m@0 107 // Change the patch
m@0 108 // MidiChannel channels[] = synthesizer.getChannels();
m@0 109 // for (int i = 0; i < channels.length; i++)
m@0 110 // channels[i].programChange(65);
m@0 111
m@0 112 synth.open();
m@0 113 Receiver synthReceiver = synth.getReceiver();
m@0 114 Transmitter seqTransmitter = sequencer.getTransmitter();
m@0 115 seqTransmitter.setReceiver(synthReceiver);
m@0 116
m@0 117 }
m@0 118 catch (MidiUnavailableException e) {
m@0 119 e.printStackTrace();
m@0 120 }
m@0 121
m@0 122 // compute data from the MIDI file
m@0 123 onsets = computeOnsets();
m@0 124 offsets = computeOffsets();
m@0 125 pitches = computePitches();
m@0 126
m@0 127 //String divisionType;
m@0 128 //if (sequence.getDivisionType() == sequence.PPQ)
m@0 129 // divisionType = "ppq";
m@0 130 //else
m@0 131 // divisionType = "smpte";
m@0 132 //System.out.println("division type = " + divisionType +
m@0 133 // "; resolution = " + sequence.getResolution());
m@0 134 }
m@0 135
m@0 136 /* number of microseconds per MIDI tick */
m@0 137 private double microsecondsPerTick() {
m@0 138 double seqTickLength = (float)sequence.getTickLength();
m@0 139 double seqMicrosecondLength = (float)sequence.getMicrosecondLength();
m@0 140 double microsecondsPerTick = seqMicrosecondLength / seqTickLength;
m@0 141 //System.out.println("seqTickLength = " + seqTickLength);
m@0 142 //System.out.println("seqMicrosecondLength = " + seqMicrosecondLength);
m@0 143 //System.out.println("microsecondsPerTick = " + microsecondsPerTick);
m@0 144 return microsecondsPerTick;
m@0 145 }
m@0 146
m@0 147 private long ticksToMicroseconds(long tick) {
m@0 148 double microsecondsPerTick = microsecondsPerTick();
m@0 149 double seconds = (double)tick * microsecondsPerTick;
m@0 150
m@0 151 return (long)(10 * Math.floor(seconds * 0.1));
m@0 152 }
m@0 153
m@0 154 /* compute a list of note onset times (microseconds) */
m@0 155 private ArrayList computeOnsets() {
m@0 156 ArrayList ons = new ArrayList();
m@0 157 Track[] tracks = sequence.getTracks();
m@0 158
m@0 159 for (int nTrack = 0; nTrack < tracks.length; nTrack++) {
m@0 160 Track track = tracks[nTrack];
m@0 161 for (int nEvent = 0; nEvent < track.size(); nEvent++) {
m@0 162 MidiEvent event = track.get(nEvent);
m@0 163 MidiMessage message = event.getMessage();
m@0 164 if (message instanceof ShortMessage &&
m@0 165 ((ShortMessage)message).getCommand() == ShortMessage.NOTE_ON) {
m@0 166 // System.out.println("onset in ticks = " + event.getTick()+
m@0 167 // "; onset in microseconds = " +
m@0 168 // ticksToMicroseconds(event.getTick()));
m@0 169 ons.add(ticksToMicroseconds(event.getTick()));
m@0 170 }
m@0 171 }
m@0 172 }
m@0 173 return ons;
m@0 174 }
m@0 175
m@0 176 /* compute a list of note offset times (microseconds) */
m@0 177 private ArrayList computeOffsets() {
m@0 178 ArrayList offs = new ArrayList();
m@0 179 Track[] tracks = sequence.getTracks();
m@0 180
m@0 181 for (int nTrack = 0; nTrack < tracks.length; nTrack++) {
m@0 182 Track track = tracks[nTrack];
m@0 183 for (int nEvent = 0; nEvent < track.size(); nEvent++) {
m@0 184 MidiEvent event = track.get(nEvent);
m@0 185 MidiMessage message = event.getMessage();
m@0 186 if (message instanceof ShortMessage &&
m@0 187 ((ShortMessage)message).getCommand() == ShortMessage.NOTE_OFF) {
m@0 188 //System.out.println("offset in ticks = " + event.getTick()+
m@0 189 // "; microsecondsPerTick = " + microsecondsPerTick +
m@0 190 // "; offset in microseconds = " +
m@0 191 // (float)event.getTick() * microsecondsPerTick);
m@0 192 offs.add(ticksToMicroseconds(event.getTick()));
m@0 193 }
m@0 194 }
m@0 195 }
m@0 196 return offs;
m@0 197 }
m@0 198
m@0 199 /* compute a list of note pitches */
m@0 200 private ArrayList computePitches() {
m@0 201 ArrayList pit = new ArrayList();
m@0 202 Track[] tracks = sequence.getTracks();
m@0 203
m@0 204 for (int nTrack = 0; nTrack < tracks.length; nTrack++) {
m@0 205 Track track = tracks[nTrack];
m@0 206 for (int nEvent = 0; nEvent < track.size(); nEvent++) {
m@0 207 MidiEvent event = track.get(nEvent);
m@0 208 MidiMessage message = event.getMessage();
m@0 209 if (message instanceof ShortMessage &&
m@0 210 ((ShortMessage)message).getCommand() == ShortMessage.NOTE_ON)
m@0 211 pit.add(((ShortMessage)message).getData1());
m@0 212 }
m@0 213 }
m@0 214 return pit;
m@0 215 }
m@0 216
m@0 217 /* return a list of note onset times (milliseconds) */
m@0 218 public ArrayList getOnsets() { return onsets; }
m@0 219
m@0 220 /* return a list of note offset times (milliseconds) */
m@0 221 public ArrayList getOffsets() { return offsets; }
m@0 222
m@0 223 /* return a list of note pitches */
m@0 224 public ArrayList getPitches() { return pitches; }
m@0 225
m@0 226 /* return a list of note durations (microseconds) */
m@0 227 public ArrayList getDurations() {
m@0 228 Object[] ons = onsets.toArray();
m@0 229 Object[] offs = offsets.toArray();
m@0 230
m@0 231 ArrayList durations = new ArrayList();
m@0 232
m@0 233 for(int i = 0; i < ons.length; i++) {
m@0 234 durations.add(((Long)offs[i]).longValue() -
m@0 235 ((Long)ons[i]).longValue());
m@0 236 }
m@0 237 return durations;
m@0 238 }
m@0 239
m@0 240 /* return a list of inter-onset intervals (microseconds) */
m@0 241 public ArrayList getInterOnsetIntervals() {
m@0 242 Object[] ons = onsets.toArray();
m@0 243
m@0 244 ArrayList iois = new ArrayList();
m@0 245 long firstIOI = 0;
m@0 246 iois.add(firstIOI); // IOI of first note is zero
m@0 247
m@0 248 for(int i = 1; i < ons.length; i++) {
m@0 249 iois.add(((Long)ons[i]).longValue() -
m@0 250 ((Long)ons[i-1]).longValue());
m@0 251 }
m@0 252 return iois;
m@0 253 }
m@0 254
m@0 255 /* return the length of a rest preceding a note */
m@0 256 public ArrayList getDeltaST() {
m@0 257 Object[] durs = getDurations().toArray();
m@0 258 Object[] iois = getInterOnsetIntervals().toArray();
m@0 259
m@0 260 ArrayList deltaST = new ArrayList();
m@0 261 long firstDeltaST = 0;
m@0 262 deltaST.add(firstDeltaST); // deltaST of first note is zero
m@0 263
m@0 264 for(int i = 1; i < durs.length; i++) {
m@0 265 deltaST.add(((Long)durs[i-1]).longValue() -
m@0 266 ((Long)iois[i]).longValue());
m@0 267 }
m@0 268 return deltaST;
m@0 269 }
m@0 270
m@0 271 /* return the tatum of the midi file */
m@0 272 public long getTatum() {
m@0 273 Object[] durs = getDurations().toArray();
m@0 274 Object[] rests = getDeltaST().toArray();
m@0 275 long tatum = -1;
m@0 276
m@0 277 for(int i = 0; i < durs.length; i++) {
m@0 278 long dur = ((Long)durs[i]).longValue();
m@0 279 long rest = ((Long)rests[i]).longValue();
m@0 280 long min = dur;
m@0 281 if (rest != 0)
m@0 282 min = Math.min(dur, rest);
m@0 283
m@0 284 if (tatum < 0)
m@0 285 tatum = dur;
m@0 286 else if (min < tatum)
m@0 287 tatum = min;
m@0 288 }
m@0 289 return tatum;
m@0 290 }
m@0 291
m@0 292 /* return length of sequence in microseconds */
m@0 293 public long getLength() {
m@0 294 return sequence.getMicrosecondLength();
m@0 295 }
m@0 296
m@0 297 /* play the midi file */
m@0 298 public void play() {
m@0 299 sequencer.start();
m@0 300 }
m@0 301
m@0 302 public void stop() {
m@0 303 sequencer.close();
m@0 304 }
m@0 305 }