![JAR search and dependency download from the Maven repository](/logo.png)
jm.midi.SMF Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jmusic Show documentation
Show all versions of jmusic Show documentation
JMusic - Java Music Library
The newest version!
/*
Copyright (C) 2000 Andrew Sorensen & Andrew Brown
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 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
// Updates for key and time signatures by Sean Hayes
package jm.midi;
import jm.JMC;
import jm.midi.event.EndTrack;
import jm.midi.event.Event;
import jm.midi.event.VoiceEvt;
import java.io.*;
import java.util.Enumeration;
import java.util.Vector;
/**
* @author Andrew Sorensen and Sean Hayes
* @version 1.0, Sun Feb 25 18:43
*/
public final class SMF implements JMC {
//--------------------------------------
//attributes
//--------------------------------------
/**
* The standard MIDI file type to be read or written
*/
private short fileType;
/**
* The current number of tracks stored by this Class
*/
private short numOfTracks;
/**
* The number of bytes being read or written
*/
private int numOfBytes;
/**
* Pulses per quarter note value
*/
private short ppqn;
/**
* list of Tracks contained within this SMF
*/
private Vector trackList;
/* Print statements */
private boolean VERBOSE = false;
//------------------------------------------
//SMF Default Constructor
//------------------------------------------
/**
* Create a new empty SMF with default filetype and ppqn settings
* SMF objects are useful for retrieving statistical information
* about a MIDI file which has just been read or written.
*/
public SMF() {
this((short) 1, (short) 480);
}
/**
* Create a new empty SMF. Allows user to set filetype and ppqn settings
* SMF Objects are useful for retrieving statistical information
* about a MIDI file which has just been read or written.
*/
public SMF(short fileType, short ppqn) {
this.fileType = fileType;
this.ppqn = ppqn;
this.numOfBytes = 0;
this.numOfTracks = 0;
this.trackList = new Vector();
}
public void setVerbose(boolean val) {
this.VERBOSE = val;
}
public Vector getTrackList() {
return this.trackList;
}
public short getPPQN() {
return this.ppqn;
}
public void clearTracks() {
if (!this.trackList.isEmpty()) {
//remove any previous tracks
this.trackList.removeAllElements();
}
}
//------------------------------------------
//Read SMF
//------------------------------------------
/**
* Read from a standard MIDI file
*
* @throws IOException - any IO problems
* @params InputStream - the datasource to read from
* @params Score score - the score to place jMusic data translation into
*/
public void read(InputStream is)
throws IOException {
//Given the small size of MIDI files read all
//data into a ByteArrayStream for further processing
byte[] fileData = new byte[is.available()];
is.read(fileData);
ByteArrayInputStream bais =
new ByteArrayInputStream(fileData);
DataInputStream dis = new DataInputStream(bais);
//clear any SMF data
if (!this.trackList.isEmpty()) {
this.trackList.removeAllElements(); //remove any previous tracks
}
//check header for MIDIfile validity
if (dis.readInt() != 0x4D546864) {//Check for MIDI track validity
throw new IOException("This is NOT a MIDI file !!!");
} else {//If MIDI file passes skip length bytes
dis.readInt(); //skip over Length info
}
//get SMF Class data
try {
fileType = dis.readShort();
if (VERBOSE) System.out.println("MIDI file type = " + fileType);
this.numOfTracks = dis.readShort();
if (VERBOSE) System.out.println("Number of tracks = " + numOfTracks);
this.ppqn = dis.readShort();
if (VERBOSE) System.out.println("ppqn = " + ppqn);
} catch (IOException e) {
System.out.println(e);
e.printStackTrace();
}
// skip the tempo track fro type 1 files
/*
if(fileType == 1) {
skipATrack(dis);
numOfTracks--;
}
*/
//Read all track chunks
for (int i = 0; i < numOfTracks; i++) {
readTrackChunk(dis);
}
is.close();
dis.close();
}
//------------------------------------
//Write a SMF
//-------------------------------------
/**
* Write to a standard MIDI file
*
* @param OutputStream the datasource to write to
* @param Score score the Score to get data from
* @throws IOException did the write go ok
*/
public void write(OutputStream os)
throws IOException {
//IO Stream stuff
DataOutputStream dos = new DataOutputStream(os);
//find number of tracks
this.numOfTracks = (short) trackList.size();
//write header chunk
try {
dos.writeInt(0x4D546864); //MThd
dos.writeInt(6); //Length
dos.writeShort(1); //Midi File Type
dos.writeShort(numOfTracks); //Number of tracks
dos.writeShort(ppqn); //Pulses Per Quarter Note
} catch (Exception e) {
e.printStackTrace();
}
//write all tracks
Enumeration aEnum = trackList.elements();
while (aEnum.hasMoreElements()) {
Track smfTrack = (Track) aEnum.nextElement();
writeTrackChunk(dos, smfTrack);
}
os.flush();
os.close();
dos.flush();
dos.close();
}
/**
* Print all MIDI tracks and MIDI events
*/
public void print() {
Enumeration aEnum = trackList.elements();
while (aEnum.hasMoreElements()) {
Track track = (Track) aEnum.nextElement();
track.print();
}
}
/**
* Reads a MIDI track without doing anything to the data
*/
private void skipATrack(RandomAccessFile dos) throws IOException {
if (VERBOSE) System.out.println("Skipping the tempo track . . .");
dos.readInt();
dos.skipBytes(dos.readInt());
}
//----------------------------------------
//SMF Track Reads and Writes
//----------------------------------------
/**
* Reads a MIDI track chunk
*
* @param DataInputStream dis - the input stream to read from
* @throws IOException
*/
private void readTrackChunk(DataInputStream dis)
throws IOException {
//local variables for Track class
Track track = new Track();
//Insert new Track into a list of tracks
this.trackList.addElement(track);
int deltaTime = 0;
if (VERBOSE) System.out.println("Reading Track ..........");
//Read track header
if (dis.readInt() != 0x4D54726B) {//If MTrk read is wrong
throw new IOException
("Track started in wrong place!!!! ABORTING");
} else {//If MTrk read ok get bytesRemaining
dis.readInt();
}
//loop variables
int status, oldStatus = 0, eventLength = 0;
//Start gathering event data
Event event = null;
while (true) {
try {
//get variable length timestamp
deltaTime = MidiUtil.readVarLength(dis);
//mark stream so we can return if we need running status
dis.mark(2);
status = dis.readUnsignedByte();
//decide on running status
if (status < 0x80) { //set running status
status = oldStatus;
//return stream to before status read
dis.reset();
}
//create default event of correct type
if (status >= 0xFF) { //Meta Event
int type = dis.readUnsignedByte();
eventLength = MidiUtil.readVarLength(dis);
event = MidiUtil.createMetaEvent(type);
} else if (status >= 0xF0) { //System Exclusive --- NOT SUPPORTED
System.out.println("SysEX---");
eventLength = MidiUtil.readVarLength(dis);
} else if (status >= 0x80) { //MIDI voice event
short selection = (short) (status / 0x10);
short midiChannel = (short) (status - (selection * 0x10));
VoiceEvt evt = (VoiceEvt) MidiUtil.createVoiceEvent(selection);
evt.setMidiChannel(midiChannel);
event = evt;
if (event == null) {
throw new IOException("MIDI file read error: invalid voice event type!");
}
}
oldStatus = status;
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
if (event != null) {
//read data into the new event and
//add the new event to the Track object
event.setTime(deltaTime);
event.read(dis);
//if (VERBOSE) event.print();
track.addEvent(event);
//event.print();
if (event instanceof EndTrack)
break;
} else {
//skip the stream ahead to next valid event
dis.skipBytes(eventLength);
}
}
}
/**
* Write the Track Chunk
*
* @param DataOutputStream dos
* @param Track track - track to write
* @throws IOException
*/
private void writeTrackChunk(DataOutputStream odos, Track track)
throws IOException {
if (VERBOSE) System.out.println("Writing MIDI Track");
//Write to temporary stream to buffer disk writes and
//calculate the number of bytes written to the stream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
int header = 0x4D54726B;
Enumeration aEnum = track.getEvtList().elements();
aEnum = track.getEvtList().elements();
//At this stage Except that all events are NoteOn events
while (aEnum.hasMoreElements()) {
Event evt = (Event) aEnum.nextElement();
evt.write(dos);
if (DEBUG) evt.print();
}
//Write to the real stream
odos.writeInt(header);
odos.writeInt(baos.size());
odos.write(baos.toByteArray(), 0, baos.size());
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy