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