|
using System;
using System.IO;
using System.Windows.Forms;
using System.Collections;
namespace SteganoMidi
{
/// <summary>Analyse a MIDI file</summary>
public class MidiFileReader {
public void ReadFile(String srcFileName, out String report, out int countProgramChange, out long fileSize){
FileStream srcFile = new FileStream(srcFileName, FileMode.Open);
BinaryReader srcReader = new BinaryReader(srcFile);
report = String.Empty;
fileSize = 0;
countProgramChange = 0;
int countNoteOff = 0;
int countNoteOn = 0;
int countAfterTouch = 0;
int countChannelPressure = 0;
int countControlChange = 0;
int countPitchWheel = 0;
int countSysEx = 0;
int countNonMidi = 0;
try{
MidiFileHeader header = new MidiFileHeader();
//Read type
header.HeaderType = srcReader.ReadChars(4);
header.DataLength = new byte[4];
header.DataLength = srcReader.ReadBytes(4);
if((new String(header.HeaderType) != "MThd")
||(header.DataLength[3] != 6)){
MessageBox.Show("This is not a standard MIDI file!");
srcReader.Close();
}
//These values are Int16, stored in reverse byte order
header.FileType = (Int16)(srcReader.ReadByte()*16 + srcReader.ReadByte());
header.CountTracks = (Int16)(srcReader.ReadByte()*16 + srcReader.ReadByte());
header.Division = (Int16)(srcReader.ReadByte()*16 + srcReader.ReadByte());
//-------------------------------- Read Tracks
MidiMessage midiMessage = new MidiMessage();
midiMessage.MessageData = new byte[2];
for(int track=0; track<header.CountTracks; track++){
if(srcReader.BaseStream.Position == srcReader.BaseStream.Length){
//no more tracks found
break;
}
//Read track header
MidiTrackHeader th = new MidiTrackHeader();
th.HeaderType = srcReader.ReadChars(4);
if(new String(th.HeaderType) != "MTrk"){
//not a standard track - search the next track
while(srcReader.BaseStream.Position+8 < srcReader.BaseStream.Length){
th.HeaderType = srcReader.ReadChars(4);
if(new String(th.HeaderType) == "MTrk"){
break;
}
}
}
//Read the length field and convert it to Int32
//srcReader.ReadInt32() returns a wrong value,
//because of the reverse byte order
byte[] trackLength = srcReader.ReadBytes(4);
th.DataLength = trackLength[3]
+ trackLength[2]*256
+ trackLength[1]*4096
+ trackLength[0]*65536;
long startOfTrack = srcReader.BaseStream.Position;
bool isEndOfTrack = false;
//while((srcReader.BaseStream.Position - startOfTrack) < th.DataLength){
while( ! isEndOfTrack){
//Read the messages
/* 1st field: Time - variable length
* 2nd fiels: Message type and channel - 1 byte
* The lower four bits contain channel (0-15),
* the higher four bits contain the message type (8-F)
* 3rd and 4th field: Message parameters - 1 byte each */
//Read time
ReadVariableLengthValue(srcReader, out midiMessage.Time);
//Read type and channel
midiMessage.MessageType = srcReader.ReadByte();
if(midiMessage.MessageType == 0xFF){ //non-MIDI event
byte name = srcReader.ReadByte();
int length = (int)ReadVariableLengthValue(srcReader, out midiMessage.MessageData);
srcReader.ReadBytes(length);
countNonMidi++;
if((name == 0x2F)&&(length == 0)){ // End Of Track
break; //continue with next track
}
}else{
//remove channel information by resetting the 4 lower bits
byte cleanMessageType = midiMessage.MessageType;
byte[] bitValues = {1, 2, 4, 8};
foreach(byte bitValue in bitValues){
if((cleanMessageType & bitValue) > 0){
cleanMessageType = (byte)(cleanMessageType ^ bitValue);
}
}
switch(cleanMessageType){
case 0x80:{ //Note Off - Note and Velocity following
midiMessage.MessageData = srcReader.ReadBytes(2);
countNoteOff++;
break;
}
case 0x90:{ //Note On - Note and Velocity following
midiMessage.MessageData = srcReader.ReadBytes(2);
countNoteOn++;
break;
}
case 0xA0:{ //After Touch - Note and Pressure following
srcReader.ReadBytes(2);
countAfterTouch++;
break;
}
case 0xB0:{ //Control Change - Control and Value following
srcReader.ReadBytes(2);
countControlChange++;
break;
}
case 0xC0:{ //Program Change - Program following
midiMessage.MessageData = new byte[1]{ srcReader.ReadByte() };
countProgramChange++;
break;
}
case 0xD0:{ //Channel Pressure - Value following
srcReader.ReadByte();
countChannelPressure++;
break;
}
case 0xE0:{ //Pitch Wheel - 14-bit value following
srcReader.ReadBytes(2);
countPitchWheel++;
break;
}
case 0xF0: { //SysEx - no static length, read until end tag 0xF7 is found
byte b=0;
while(b != 0xF7){ b = srcReader.ReadByte(); }
countSysEx++;
break;
}
}
} //else - MIDI message
} //while() over messages
}//for() over tracks
//Fill return variables
fileSize = srcFile.Length;
report += "---- MIDI Messages ----\r\n";
report += "Program Change: "+countProgramChange+"\r\n";
report += "Note Off: "+countNoteOff+"\r\n";
report += "Note On: "+countNoteOn+"\r\n";
report += "After Touch: "+countAfterTouch+"\r\n";
report += "Channel Pressure: "+countChannelPressure+"\r\n";
report += "Control Change: "+countControlChange+"\r\n";
report += "PitchWheel: "+countPitchWheel+"\r\n";
report += "System Exclusive Messages: "+countSysEx+"\r\n";
report += "Non-Midi Messages: "+countNonMidi+"\r\n";
}catch(Exception ex){
MessageBox.Show("This is not a standard MIDI file!\r\n\r\n" + ex.ToString());
}finally{
srcReader.Close();
}
}
private static long ReadVariableLengthValue(BinaryReader srcReader, out byte[] rawDataBuffer) {
long returnValue;
byte b;
ArrayList allBytes = new ArrayList();
//read the first byte
returnValue = srcReader.ReadByte();
allBytes.Add((byte)returnValue);
if ( (returnValue & 0x80) > 0 ) { //bit 7 is set: there are more bytes to read
returnValue &= 0x7F; //remove bit 7 - it is only a not-the-last-one flag
do {
b = srcReader.ReadByte(); //read next byte
allBytes.Add(b);
//remove flag and append byte
returnValue = (returnValue << 7) + (b & 0x7F);
} while( (b & 0x80) > 0 ); //until bit-7 is not set
}
rawDataBuffer = (byte[])allBytes.ToArray(typeof(byte));
return returnValue;
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.