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