private void loadStream(Stream stream)
{
byte[] tmp = new byte[4];
stream.Read(tmp, 0, 4);
if (UTF8Encoding.UTF8.GetString(tmp, 0, tmp.Length) != "MThd")
throw new Exception("Not a valid midi file!");
mheader = new MidiHeader();
//Read header length
stream.Read(tmp, 0, 4);
Array.Reverse(tmp); //Reverse the bytes
int headerLength = BitConverter.ToInt32(tmp, 0);
//Read midi format
tmp = new byte[2];
stream.Read(tmp, 0, 2);
Array.Reverse(tmp); //Reverse the bytes
mheader.setMidiFormat(BitConverter.ToInt16(tmp, 0));
//Read Track Count
stream.Read(tmp, 0, 2);
Array.Reverse(tmp); //Reverse the bytes
int trackCount = BitConverter.ToInt16(tmp, 0);
tracks = new MidiTrack[trackCount];
//Read Delta Time
stream.Read(tmp, 0, 2);
Array.Reverse(tmp); //Reverse the bytes
int delta = BitConverter.ToInt16(tmp, 0);
mheader.DeltaTiming = (delta & 0x7FFF);
//Time Format
mheader.TimeFormat = ((delta & 0x8000) > 0) ? MidiHelper.MidiTimeFormat.FamesPerSecond : MidiHelper.MidiTimeFormat.TicksPerBeat;
//Begin Reading Each Track
for (int x = 0; x < trackCount; x++)
{
List<byte> Programs = new List<byte>();
List<byte> DrumPrograms = new List<byte>();
List<MidiEvent> midiEvList = new List<MidiEvent>();
List<MidiTrack.Section> TrackSections = new List<MidiTrack.Section>();
tracks[x] = new MidiTrack();
Programs.Add(0); //assume the track uses program at 0 in case no program changes are used
DrumPrograms.Add(0);
tmp = new byte[4]; //reset the size again
stream.Read(tmp, 0, 4);
if (UTF8Encoding.UTF8.GetString(tmp, 0, tmp.Length) != "MTrk")
throw new Exception("Invalid track!");
stream.Read(tmp, 0, 4);
Array.Reverse(tmp); //Reverse the bytes
int TrackLength = BitConverter.ToInt32(tmp, 0);
//Read The Rest of The Track
tmp = new byte[TrackLength];
stream.Read(tmp, 0, TrackLength);
int index = 0;
byte prevByte = 0;
int prevChan = 0;
bool anyNotes = false;
MidiTrack.Section section = new MidiTrack.Section();
section.StartSample = 0;
section.EventIndex = 0;
while (index < tmp.Length)
{
UInt16 numofbytes = 0;
UInt32 ScrmbledDta = BitConverter.ToUInt32(tmp, index);
MidiEvent MEv = new MidiEvent();
MEv.deltaTime = GetTime(ScrmbledDta, ref numofbytes);
index += 4 - (4 - numofbytes);
byte statusByte = tmp[index];
int CHANNEL = GetChannel(statusByte);
if (statusByte < 0x80)
{
statusByte = prevByte;
CHANNEL = prevChan;
index--;
}
if (statusByte != 0xFF)
statusByte &= 0xF0;
prevByte = statusByte;
prevChan = CHANNEL;
switch (statusByte)
{
case 0x80:
{
MEv.midiChannelEvent = MidiHelper.MidiChannelEvent.Note_Off;
++index;
MEv.channel = (byte)CHANNEL;
MEv.Parameters[0] = MEv.channel;
MEv.parameter1 = tmp[index++];
MEv.parameter2 = tmp[index++];
MEv.Parameters[1] = MEv.parameter1;
MEv.Parameters[2] = MEv.parameter2;
}
break;
case 0x90:
{
MEv.midiChannelEvent = MidiHelper.MidiChannelEvent.Note_On;
++index;
MEv.channel = (byte)CHANNEL;
MEv.Parameters[0] = MEv.channel;
MEv.parameter1 = tmp[index++];
MEv.parameter2 = tmp[index++];
MEv.Parameters[1] = MEv.parameter1;
MEv.Parameters[2] = MEv.parameter2;
if (MEv.parameter2 == 0x00) //Setting velocity to 0 is actually just turning the note off.
MEv.midiChannelEvent = MidiHelper.MidiChannelEvent.Note_Off;
tracks[x].NotesPlayed++;
}
break;
case 0xA0:
{
MEv.midiChannelEvent = MidiHelper.MidiChannelEvent.Note_Aftertouch;
MEv.channel = (byte)CHANNEL;
MEv.Parameters[0] = MEv.channel;
++index;
MEv.parameter1 = tmp[++index];//note number
MEv.parameter2 = tmp[++index];//Amount
}
break;
case 0xB0:
{
MEv.midiChannelEvent = MidiHelper.MidiChannelEvent.Controller;
MEv.channel = (byte)CHANNEL;
MEv.Parameters[0] = MEv.channel;
++index;
MEv.parameter1 = tmp[index++]; //type
MEv.parameter2 = tmp[index++]; //value
MEv.Parameters[1] = MEv.parameter1;
MEv.Parameters[2] = MEv.parameter2;
}
break;
case 0xC0:
{
MEv.midiChannelEvent = MidiHelper.MidiChannelEvent.Program_Change;
MEv.channel = (byte)CHANNEL;
MEv.Parameters[0] = MEv.channel;
++index;
MEv.parameter1 = tmp[index++];
MEv.Parameters[1] = MEv.parameter1;
//record which programs are used by the track
if (MEv.channel != 9)
{
if (Programs.Contains(MEv.parameter1) == false)
Programs.Add(MEv.parameter1);
}
else
{
if (DrumPrograms.Contains(MEv.parameter1) == false)
DrumPrograms.Add(MEv.parameter1);
}
}
break;
case 0xD0:
{
MEv.midiChannelEvent = MidiHelper.MidiChannelEvent.Channel_Aftertouch;
MEv.channel = (byte)CHANNEL;
MEv.Parameters[0] = MEv.channel;
++index;
//Amount
MEv.parameter1 = tmp[++index];
}
break;
case 0xE0:
{
MEv.midiChannelEvent = MidiHelper.MidiChannelEvent.Pitch_Bend;
MEv.channel = (byte)CHANNEL;
MEv.Parameters[0] = MEv.channel;
++index;
MEv.parameter1 = tmp[++index];
MEv.parameter2 = tmp[++index];
ushort s = (ushort)MEv.parameter1;
s <<= 7;
s |= (ushort)MEv.parameter2;
MEv.Parameters[1] = ((double)s - 8192.0) / 8192.0;
}
break;
case 0xFF:
statusByte = tmp[++index];
switch (statusByte)
{
case 0x00:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Sequence_Number; ++index;
break;
case 0x01:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Text_Event; ++index;
//Get the length of the string
MEv.parameter1 = tmp[index++];
MEv.Parameters[0] = MEv.parameter1;
//Set the string in the parameter list
MEv.Parameters[1] = UTF8Encoding.UTF8.GetString(tmp, index, ((int)tmp[index - 1])); index += (int)tmp[index - 1];
break;
case 0x02:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Copyright_Notice; ++index;
//Get the length of the string
MEv.parameter1 = tmp[index++];
MEv.Parameters[0] = MEv.parameter1;
//Set the string in the parameter list
MEv.Parameters[1] = UTF8Encoding.UTF8.GetString(tmp, index, ((int)tmp[index - 1])); index += (int)tmp[index - 1];
break;
case 0x03:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Sequence_Or_Track_Name; ++index;
//Get the length of the string
MEv.parameter1 = tmp[index++];
MEv.Parameters[0] = MEv.parameter1;
//Set the string in the parameter list
MEv.Parameters[1] = UTF8Encoding.UTF8.GetString(tmp, index, ((int)tmp[index - 1])); index += (int)tmp[index - 1];
break;
case 0x04:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Instrument_Name; ++index;
//Set the instrument name
MEv.Parameters[0] = UTF8Encoding.UTF8.GetString(tmp, index + 1, (int)tmp[index]);
index += (int)tmp[index] + 1;
break;
case 0x05:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Lyric_Text; ++index;
//Set the lyric string
MEv.Parameters[0] = UTF8Encoding.UTF8.GetString(tmp, index + 1, (int)tmp[index]);
index += (int)tmp[index] + 1;
break;
case 0x06:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Marker_Text; ++index;
//Set the marker
MEv.Parameters[0] = UTF8Encoding.UTF8.GetString(tmp, index + 1, (int)tmp[index]);
index += (int)tmp[index] + 1;
break;
case 0x07:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Cue_Point; ++index;
//Set the cue point
MEv.Parameters[0] = UTF8Encoding.UTF8.GetString(tmp, index + 1, (int)tmp[index]);
index += (int)tmp[index] + 1;
break;
case 0x20:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Midi_Channel_Prefix_Assignment; index++;
//Get the length of the data
MEv.parameter1 = tmp[index++];
MEv.Parameters[0] = MEv.parameter1;
//Set the string in the parameter list
MEv.Parameters[1] = tmp[index++];
break;
case 0x2F:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.End_of_Track;
index += 2;
break;
case 0x51:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Tempo; ++index;
//Get the length of the data
MEv.Parameters[4] = tmp[index++];
//Put the data into an array
byte[] mS = new byte[4]; for (int i = 0; i < 3; i++) mS[i + 1] = tmp[i + index]; index += 3;
//Put it into a readable format
byte[] mS2 = new byte[4]; for (int i = 0; i < 4; i++) mS2[3 - i] = mS[i];
//Get the value from the array
UInt32 Val = BitConverter.ToUInt32(mS2, 0);
//Set the value
MEv.Parameters[0] = Val;
break;
case 0x54:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Smpte_Offset; ++index;
int v = tmp[index++];
if (v >= 4)
for (int i = 0; i < 4; i++) MEv.Parameters[i] = tmp[index++];
else
for (int i = 0; i < v; i++) MEv.Parameters[i] = tmp[index++];
for (int i = 4; i < v; i++) index++;
break;
case 0x58:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Time_Signature; ++index;
int v1 = tmp[index++];
if (v1 >= 4)
for (int i = 0; i < 4; i++) MEv.Parameters[i] = tmp[index++];
else
for (int i = 0; i < v1; i++) MEv.Parameters[i] = tmp[index++];
for (int i = 4; i < v1; i++) index++;
break;
case 0x59:
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Key_Signature; ++index;
int v2 = tmp[index++];
if (v2 >= 4)
for (int i = 0; i < 4; i++) MEv.Parameters[i] = tmp[index++];
else
for (int i = 0; i < v2; i++) MEv.Parameters[i] = tmp[index++];
for (int i = 4; i < v2; i++) index++;
break;
case 0x7F:
//Sequencer specific events
MEv.midiMetaEvent = MidiHelper.MidiMetaEvent.Sequencer_Specific_Event; ++index; //increment the indexer
//Get the length of the data
MEv.Parameters[4] = tmp[index++];
//Get the byte length
byte[] len = new byte[(byte)MEv.Parameters[4]];
//get the byte info
for (int i = 0; i < len.Length; i++) len[i] = tmp[index++];
MEv.Parameters[0] = len;
break;
}
break;
//System exclusive
case 0xF0:
// iMuse sysEx?
if ((tmp[index] != 0xF7) && (tmp[index+1] != 0xF7) && (tmp[index+2] == 0x7d) && (tmp[index+3] == 0x03)) {
MEv.midiSysExEvent = MidiHelper.MidiSysExEvent.iMUSE;
index += 4;
// imuse sysex is text
StringBuilder stringBuilder = new StringBuilder();
while ((tmp[index] != 0xF7) && (tmp[index] != 0x0)) {
stringBuilder.Append(Convert.ToChar(tmp[index]));
index++;
}
MEv.Parameters = new object[1];
MEv.Parameters[0] = stringBuilder.ToString();
}
while (tmp[index] != 0xF7)
index++;
index++;
break;
}
midiEvList.Add(MEv);
tracks[x].TotalTime = tracks[x].TotalTime + MEv.deltaTime;
if (MEv.midiSysExEvent == MidiHelper.MidiSysExEvent.iMUSE) {
if ((MEv.Parameters[0] as string) == "start new") {
if ((section != null) && anyNotes) {
section.EndSample = tracks[x].TotalTime;
TrackSections.Add(section);
}
section = new MidiTrack.Section();
section.EventIndex = midiEvList.Count;
section.StartSample = tracks[x].TotalTime;
anyNotes = false;
}
}
if (MEv.midiChannelEvent == MidiHelper.MidiChannelEvent.Note_On) {
anyNotes = true;
}
}
if (anyNotes && (section != null)) {
section.EndSample = tracks[x].TotalTime;
TrackSections.Add(section);
}
tracks[x].Programs = Programs.ToArray();
tracks[x].DrumPrograms = DrumPrograms.ToArray();
tracks[x].MidiEvents = midiEvList.ToArray();
tracks[x].Sections = TrackSections.ToArray();
}
}