1:
37:
38: package ;
39:
40: import ;
41: import ;
42: import ;
43: import ;
44: import ;
45: import ;
46:
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55: import ;
56:
57:
70: public class MidiFileReader extends javax.sound.midi.spi.MidiFileReader
71: {
72:
75: public MidiFileFormat getMidiFileFormat(InputStream in)
76: throws InvalidMidiDataException, IOException
77: {
78: DataInputStream din;
79: if (in instanceof DataInputStream)
80: din = (DataInputStream) in;
81: else
82: din = new DataInputStream(in);
83:
84: int type, ntracks, division, resolution, bytes;
85: float divisionType;
86:
87: if (din.readInt() != 0x4d546864)
88: throw new InvalidMidiDataException("Invalid MIDI chunk header.");
89:
90: bytes = din.readInt();
91: if (bytes < 6)
92: throw new
93: InvalidMidiDataException("Invalid MIDI chunk header length: " + bytes);
94:
95: type = din.readShort();
96: if (type < 0 || type > 2)
97: throw new
98: InvalidMidiDataException("Invalid MIDI file type value: " + type);
99:
100: ntracks = din.readShort();
101: if (ntracks <= 0)
102: throw new
103: InvalidMidiDataException("Invalid number of MIDI tracks: " + ntracks);
104:
105: division = din.readShort();
106: if ((division & 0x8000) != 0)
107: {
108: division = -((division >>> 8) & 0xFF);
109: switch (division)
110: {
111: case 24:
112: divisionType = Sequence.SMPTE_24;
113: break;
114:
115: case 25:
116: divisionType = Sequence.SMPTE_25;
117: break;
118:
119: case 29:
120: divisionType = Sequence.SMPTE_30DROP;
121: break;
122:
123: case 30:
124: divisionType = Sequence.SMPTE_30;
125: break;
126:
127: default:
128: throw new
129: InvalidMidiDataException("Invalid MIDI frame division type: "
130: + division);
131: }
132: resolution = division & 0xff;
133: }
134: else
135: {
136: divisionType = Sequence.PPQ;
137: resolution = division & 0x7fff;
138: }
139:
140:
141: din.skip(bytes - 6);
142:
143: return new ExtendedMidiFileFormat(type, divisionType, resolution,
144: MidiFileFormat.UNKNOWN_LENGTH,
145: MidiFileFormat.UNKNOWN_LENGTH, ntracks);
146: }
147:
148:
151: public MidiFileFormat getMidiFileFormat(URL url)
152: throws InvalidMidiDataException, IOException
153: {
154: InputStream is = url.openStream();
155: try
156: {
157: return getMidiFileFormat(is);
158: }
159: finally
160: {
161: is.close();
162: }
163: }
164:
165:
168: public MidiFileFormat getMidiFileFormat(File file)
169: throws InvalidMidiDataException, IOException
170: {
171: InputStream is = new FileInputStream(file);
172: try
173: {
174: return getMidiFileFormat(is);
175: }
176: finally
177: {
178: is.close();
179: }
180: }
181:
182:
185: public Sequence getSequence(InputStream is) throws InvalidMidiDataException,
186: IOException
187: {
188: MidiDataInputStream din = new MidiDataInputStream(is);
189: ExtendedMidiFileFormat mff = (ExtendedMidiFileFormat) getMidiFileFormat(din);
190:
191: Sequence seq = new Sequence(mff.getDivisionType(), mff.getResolution());
192:
193: int ntracks = mff.getNumberTracks();
194:
195: while (ntracks-- > 0)
196: {
197: Track track = seq.createTrack();
198: int Mtrk = din.readInt();
199: if (Mtrk != 0x4d54726b)
200: throw new InvalidMidiDataException("Invalid MIDI track header.");
201: din.readInt();
202:
203: int runningStatus = -1;
204: int click = 0;
205:
206:
207: boolean done = false;
208:
209:
210: while (! done)
211: {
212: MidiMessage mm;
213: int dtime = din.readVariableLengthInt();
214: click += dtime;
215:
216: int sbyte = din.readUnsignedByte();
217:
218: if (sbyte < 0xf0)
219: {
220: ShortMessage sm;
221: switch (sbyte & 0xf0)
222: {
223: case ShortMessage.NOTE_OFF:
224: case ShortMessage.NOTE_ON:
225: case ShortMessage.POLY_PRESSURE:
226: case ShortMessage.CONTROL_CHANGE:
227: case ShortMessage.PITCH_BEND:
228: case ShortMessage.SONG_POSITION_POINTER:
229: sm = new ShortMessage();
230: sm.setMessage(sbyte, din.readByte(), din.readByte());
231: runningStatus = sbyte;
232: break;
233:
234: case ShortMessage.PROGRAM_CHANGE:
235: case ShortMessage.CHANNEL_PRESSURE:
236: case ShortMessage.SONG_SELECT:
237: case 0xF5:
238: sm = new ShortMessage();
239: sm.setMessage(sbyte, din.readByte(), 0);
240: runningStatus = sbyte;
241: break;
242:
243: case ShortMessage.TUNE_REQUEST:
244: case ShortMessage.END_OF_EXCLUSIVE:
245: case ShortMessage.TIMING_CLOCK:
246: case ShortMessage.START:
247: case ShortMessage.CONTINUE:
248: case ShortMessage.STOP:
249: case ShortMessage.ACTIVE_SENSING:
250: case ShortMessage.SYSTEM_RESET:
251: sm = new ShortMessage();
252: sm.setMessage(sbyte, 0, 0);
253: runningStatus = sbyte;
254: break;
255:
256: default:
257: if (runningStatus != - 1)
258: {
259: switch (runningStatus & 0xf0)
260: {
261: case ShortMessage.NOTE_OFF:
262: case ShortMessage.NOTE_ON:
263: case ShortMessage.POLY_PRESSURE:
264: case ShortMessage.CONTROL_CHANGE:
265: case ShortMessage.PITCH_BEND:
266: case ShortMessage.SONG_POSITION_POINTER:
267: sm = new ShortMessage();
268: sm.setMessage(runningStatus, sbyte, din.readByte());
269: break;
270:
271: case ShortMessage.PROGRAM_CHANGE:
272: case ShortMessage.CHANNEL_PRESSURE:
273: case ShortMessage.SONG_SELECT:
274: case 0xF5:
275:
276: sm = new ShortMessage();
277: sm.setMessage(runningStatus, sbyte, 0);
278: continue;
279:
280: case ShortMessage.TUNE_REQUEST:
281: case ShortMessage.END_OF_EXCLUSIVE:
282: case ShortMessage.TIMING_CLOCK:
283: case ShortMessage.START:
284: case ShortMessage.CONTINUE:
285: case ShortMessage.STOP:
286: case ShortMessage.ACTIVE_SENSING:
287: case ShortMessage.SYSTEM_RESET:
288: sm = new ShortMessage();
289: sm.setMessage(runningStatus, 0, 0);
290: continue;
291:
292: default:
293: throw new
294: InvalidMidiDataException("Invalid Short MIDI Event: "
295: + sbyte);
296: }
297: }
298: else
299: throw new
300: InvalidMidiDataException("Invalid Short MIDI Event: "
301: + sbyte);
302: }
303: mm = sm;
304: }
305: else if (sbyte == 0xf0 || sbyte == 0xf7)
306: {
307:
308: int slen = din.readVariableLengthInt();
309: byte sysex[] = new byte[slen];
310: din.readFully(sysex);
311: SysexMessage sm = new SysexMessage();
312: sm.setMessage(sbyte, sysex, slen);
313: mm = sm;
314: runningStatus = - 1;
315: }
316: else if (sbyte == 0xff)
317: {
318:
319: byte mtype = din.readByte();
320: int mlen = din.readVariableLengthInt();
321: byte meta[] = new byte[mlen];
322: din.readFully(meta);
323: MetaMessage metam = new MetaMessage();
324: metam.setMessage(mtype, meta, mlen);
325: mm = metam;
326:
327: if (mtype == 0x2f)
328: done = true;
329:
330: runningStatus = - 1;
331: }
332: else
333: {
334: throw new InvalidMidiDataException("Invalid status byte: "
335: + sbyte);
336: }
337:
338: track.add(new MidiEvent(mm, click));
339: }
340: }
341:
342: return seq;
343: }
344:
345:
348: public Sequence getSequence(URL url) throws InvalidMidiDataException,
349: IOException
350: {
351: InputStream is = url.openStream();
352: try
353: {
354: return getSequence(is);
355: }
356: finally
357: {
358: is.close();
359: }
360: }
361:
362:
365: public Sequence getSequence(File file) throws InvalidMidiDataException,
366: IOException
367: {
368: InputStream is = new FileInputStream(file);
369: try
370: {
371: return getSequence(is);
372: }
373: finally
374: {
375: is.close();
376: }
377: }
378: }