/*
* ReadTrack.java
* Copyright 2001 Michael Castleman, mlc67@columbia.edu
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
import java.io.*;
/**
* Reads a chunk from a {@link InputStream} and aids in parsing it if it's
* a track.
*/
final class ReadTrack {
/** Little data structure representing a MIDI event */
public static class Event {
/** Time since previous event */
public int deltaTime;
/** Status byte (indicates type of event) */
public byte status;
/** actual data for the event. */
public byte[] data;
/** creates a new Event
*/
public Event(int deltaTime, byte status, byte[] data) {
this.deltaTime = deltaTime;
this.status = status;
this.data = data;
}
}
protected String chunkType;
protected byte [] data;
private int inPos;
private byte lastStatus;
/**
* Reads a chunk from the stream and makes it ready for parsing.
* @throws IOException if there's a problem reading the data.
*/
public ReadTrack(InputStream in) throws IOException {
byte[] b = new byte[4];
in.read(b);
chunkType = new String(b);
in.read(b);
int chunkLen = (((b[0]&0xFF) << 24) | ((b[1]&0xFF) << 16) | ((b[2]&0xFF) << 8) | ((b[3]&0xFF))); System.err.println(chunkType + " " + chunkLen + " (" + b[0] + "," + b[1] + "," + b[2] + "," + b[3] + ")"); data = new byte[chunkLen]; in.read(data); inPos = 0; } /** * Constructs a reader for the specified chunk type and data */ public ReadTrack(String chunkType, byte [] data) { this.data = (byte[])(data.clone()); this.chunkType = chunkType; inPos = 0; } public String toString() { return "ReadTrack " + chunkType + ", " + data.length + " bytes"; } /** * Reads a byte from the buffer */ public byte readByte() { return data[inPos++]; } /** * Reads from the buffer * @param buf A buffer to store data into * @param start Where in the buffer to start storing data * @param len How many bytes to store */ public void read(byte [] buf, int start, int len) { System.arraycopy(data, inPos, buf, start, len); inPos += len; } /** * Reads from the buffer * @param buf The buffer to fill */ public void read(byte [] buf) { read(buf, 0, buf.length); } /** * Reads a variable length value * @see TrackChunk#addVarLen(int) */ public int readVarLen() { int value; byte c; if (((value = readByte()) & 0x80) != 0) { value &= 0x7F; do { value = (value << 7) + ((c = readByte()) & 0x7F); } while ((c & (byte)(0x80)) != 0); } return value; } /** * Pushes back the previously read byte */ public void unReadByte() { inPos--; } /** * Reads a MIDI event * @return an {@link Event} or null
if the track is done
*/
public Event readEvent() {
if (this.inPos>= this.data.length)
return null;
int deltaTime = readVarLen();
byte status = readByte();
if ((status & (byte)(0x80)) == 0) {
System.out.println(status + " " + (status&(byte)(0x80)) + " " + lastStatus);
System.out.println((status & (byte)(0x80)) == 0);
unReadByte();
status = lastStatus;
} else {
lastStatus = status;
}
byte [] data = null;
if (status == (byte)(0xFF)) { // midi file special stuff
byte type = readByte();
int len = readVarLen();
data = new byte[1 + len];
data[0] = type;
read(data, 1, len);
} else {
int len;
switch (status & (byte)(0xF0)) { // get 'status nibble'
case (byte)(0x80): // note off
case (byte)(0x90): // note on
case (byte)(0xA0): // after touch
case (byte)(0xB0): // controller
case (byte)(0xE0): // pitch wheel
len = 2;
break;
case (byte)(0xC0): // program change
case (byte)(0xD0): // channel pressure
len = 1;
break;
case (byte)(0xF0):
switch (status & (byte)(0x0F)) {
// case 0xFF is special and handled above
case (byte)(0x00): // System exclusive
case (byte)(0x07): // system exclusive continuation
len = readVarLen();
break;
case (byte)(0x01): // MTC quarter frame
case (byte)(0x03): // Song select
len = 1;
break;
case (byte)(0x02): // Song position pointer
len = 2;
break;
case (byte)(0x06): // Tune request
case (byte)(0x08): // MIDI clock
case (byte)(0x09): // Tick
case (byte)(0x0A): // start
case (byte)(0x0B): // continue
case (byte)(0x0C): // stop
case (byte)(0x0E): // active sense
len = 0;
break;
default:
throw new RuntimeException("Invalid MIDI byte or broken parser?");
}
break;
default:
throw new RuntimeException("I'm confused");
}
data = new byte[len];
read(data);
}
return new Event(deltaTime, status, data);
}
}