JellevM 0 Newbie Poster

Dear all,

I am developing some software that can deal with MIDI files and do some fun stuff with them, but I am running into some strange trouble when analysing the MidiEvents.

I want to list all the MidiEvents occurring in a particular Track, with their tickposition, status, and precise byte-message. The tick position works fine, but I am having troubles with the status bytes.

Many of the MidiEvents are not recognised by Java and are neither NoteOn nor NoteOff events, although I think they should be. Is it me having implemented something wrong, or are these MIDI-files with a really strange format?


The following is an excerpt of the code:

public class MidiEventStorage 
{	
	static Sequencer midiSequencer;
	
	public static void main(String[] args) 
	{
		Sequence mySequence = loadMidiSequence();
		RedBlackTree<MyMidiEvent> midiEventTree = midiSequenceToTree(mySequence);
		midiSequencer.start();		
		midiEventTree.printTree();
		
/*----*/pauseProg();/*--------------------------------------*/
		
		midiSequencer.stop();
		midiSequencer.close();
	}// End main
	
	private static Sequence loadMidiSequence() 
	{
		Sequence midiSequence;
		File myFile = new File(System.getProperty("user.dir"));			
		JFileChooser fileChooser = new JFileChooser(myFile);
		
		if (fileChooser.showOpenDialog(null) == JFileChooser.APPROVE_OPTION) 
		{
			if ( fileChooser.getSelectedFile().exists())
			{				
				try 
				{
					midiSequencer = MidiSystem.getSequencer();
					midiSequencer.open();
					
					midiSequence = MidiSystem.getSequence(fileChooser.getSelectedFile());
					midiSequencer.setSequence(midiSequence);
					return midiSequence;
				} 
				catch (MidiUnavailableException e) {} 
				catch (InvalidMidiDataException e) {}
				catch (IOException e) {}				
			}			
		}
		return null;
	}
	
	public static RedBlackTree<MyMidiEvent> midiSequenceToTree(Sequence midiSequence)
	{
		RedBlackTree<MyMidiEvent> midiEventTree = new RedBlackTree<MyMidiEvent>();
		
		Track [] midiTracks = midiSequence.getTracks();
		Track currentTrack;
		int size;
				
		for (int ii = 0; ii < midiTracks.length; ii++)
		{
			currentTrack = midiTracks[ii];
			size = currentTrack.size();
			
			for (int jj = 0; jj < size; jj++)
			{
				midiEventTree.insert( new MyMidiEvent(currentTrack.get(jj), ii) );
			}
		}
		
		return midiEventTree;
	}
	
	public static void pauseProg()
	{
		System.out.print("Press [ENTER] to continue...");  
        try  
        {  
          System.in.read();  
          System.in.skip(System.in.available());  
        }  
        catch(Exception e){e.printStackTrace();}   
        
	}
}


import javax.sound.midi.MidiEvent;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.ShortMessage;


public class MyMidiEvent extends MidiEvent implements Comparable<MyMidiEvent>
{		
	private int trackNumber;

	public MyMidiEvent(MidiEvent event, int track) 
	{
		super(event.getMessage(),event.getTick());
		trackNumber = track;
	}
	
	public MidiEvent toMidiEvent()
	{
		return new MidiEvent(getMessage(), getTick());
	}
	
	public String toString()
	{
		return "@ tick " + getTick() + ": " + getMessageType() + " (" + getByteMessage() + "), track " + trackNumber;
	}
	
	private static String format(byte b)
	{
		String binaryString = Integer.toBinaryString(b & 0xFF);
		while(binaryString.length() < 8) binaryString = "0" + binaryString;
		return binaryString;
	}
	
	public int getTrack() {return trackNumber;}
	
	public String getMessageType() 
	{
		int messageStatus = (int) (this.getMessage().getStatus() & 0xFF);
		
		// Note: a NoteOff message has either a NOTE_OFF message status
		// or a NoteOn message status with argument (volume) 0. This 
		// method does not yet cover for the latter case!
		if (messageStatus == ShortMessage.ACTIVE_SENSING)
			return messageStatus + "-" + "ActiveSensing";
		if (messageStatus == ShortMessage.CHANNEL_PRESSURE)
			return messageStatus + "-" + "ChannelPressure";
		if (messageStatus == ShortMessage.CONTINUE)
			return messageStatus + "-" + "Continue";
		if (messageStatus == ShortMessage.CONTROL_CHANGE)
			return messageStatus + "-" + "ControlChange";
		if (messageStatus == ShortMessage.END_OF_EXCLUSIVE)
			return messageStatus + "-" + "EndOfExclusive";
		if (messageStatus == ShortMessage.MIDI_TIME_CODE)
			return messageStatus + "-" + "MidiTimeCode";
		if (messageStatus == ShortMessage.NOTE_OFF)
			return messageStatus + "-" + "NoteOff";		
		if (messageStatus == ShortMessage.NOTE_ON)
			return messageStatus + "-" + "NoteOn";		
		if (messageStatus == ShortMessage.PITCH_BEND)
			return messageStatus + "-" + "PitchBend";
		if (messageStatus == ShortMessage.POLY_PRESSURE)
			return messageStatus + "-" + "PolyphonicPressure";
		if (messageStatus == ShortMessage.PROGRAM_CHANGE)
			return messageStatus + "-" + "ProgramChange";
		if (messageStatus == ShortMessage.SONG_POSITION_POINTER)
			return messageStatus + "-" + "SongPositionPointer";
		if (messageStatus == ShortMessage.SONG_SELECT)
			return messageStatus + "-" + "SongSelect";
		if (messageStatus == ShortMessage.START)
			return messageStatus + "-" + "Start";
		if (messageStatus == ShortMessage.STOP)
			return messageStatus + "-" + "Stop";
		if (messageStatus == ShortMessage.SYSTEM_RESET)
			return messageStatus + "-" + "SystemReset";
		if (messageStatus == ShortMessage.TIMING_CLOCK)
			return messageStatus + "-" + "TimingClock";
		if (messageStatus == ShortMessage.TUNE_REQUEST)
			return messageStatus + "-" + "TuneRequest";
		
		return messageStatus + "-" + "Unknown";
	}
	
	public String getByteMessage()
	{
		byte [] byteArray = this.getMessage().getMessage();
		String bitRepresentation = "";
		
		for (int ii = 0; ii < byteArray.length; ii++)
		{
			bitRepresentation = bitRepresentation + " " + format(byteArray[ii]) ;
		}	
		return bitRepresentation;
	}	
	
	//******************************************
	// Interface Methods (from Comparable interface)
	//******************************************
	final int BEFORE = -1;
	final int EQUAL = 0;
	final int AFTER = 1;
	
	public int compareTo(MyMidiEvent event) 
	{
		// compare tick position
		if (this.getTick() > event.getTick())
			return AFTER;
		else if (this.getTick() < event.getTick())
			return BEFORE;
		
		// compare track number
		else if (this.trackNumber > event.trackNumber)
			return AFTER;
		else if (this.trackNumber < event.trackNumber)
			return BEFORE;
		
		else 
			return EQUAL;
	}
	
}

Please assume that I have a class RedBlackTree that works fine, and which contains a method printTree() that executes myMidiEvent.toString() at all the nodes it contains.

The System output that I tend to get generally looks like this:

@ tick 576: 153-Unknown ( 10011001 00101010 01011010), track 3
@ tick 636: 153-Unknown ( 10011001 00101010 00000000), track 3
@ tick 664: 149-Unknown ( 10010101 00100110 00000000), track 1
@ tick 768: 144-NoteOn ( 10010000 01000011 01000000), track 0
@ tick 768: 153-Unknown ( 10011001 00111000 01000000), track 3
@ tick 812: 128-NoteOff ( 10000000 01000011 00000000), track 0
@ tick 816: 144-NoteOn ( 10010000 01000010 01001010), track 0
@ tick 828: 153-Unknown ( 10011001 00111000 00000000), track 3
@ tick 860: 128-NoteOff ( 10000000 01000010 00000000), track 0
@ tick 864: 144-NoteOn ( 10010000 01000001 01011110), track 0
@ tick 864: 149-Unknown ( 10010101 00101010 01000000), track 1
@ tick 908: 128-NoteOff ( 10000000 01000001 00000000), track 0
@ tick 912: 144-NoteOn ( 10010000 01000000 01110010), track 0
@ tick 956: 128-NoteOff ( 10000000 01000000 00000000), track 0
@ tick 956: 149-Unknown ( 10010101 00101010 00000000), track 1
@ tick 960: 153-Unknown ( 10011001 00111000 00010000), track 3

So some events are recognised, but many are not. Can someone tell me why not? What is going on in the brains of Java?

Best,
Jelle