Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- MidiFile.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.IO;
- using UnityEngine;
- namespace MidiParser {
- class MidiFileData {
- //static public uint _TicksPerQuarter;
- private int _NumTracks;
- private int _Type;
- public int _TicksPerQuarter;
- public List<PMidiTrack> _Tracks;
- public PChannel []_Channels;
- public long _BPM = 0;
- class VariableTime {
- public uint _BytesUsed { get; set; }
- public uint _Time { get; set; }
- }
- static byte[] ReverseEndian(byte[] data, uint index, int size) {
- byte[] reversed = new byte[size];
- for (int i = 0; i < size; i++) {
- reversed[i] = data[index++];
- }
- Array.Reverse(reversed);
- return reversed;
- }
- public MidiFileData() {
- _NumTracks = 0;
- _Type = 0;
- _TicksPerQuarter = 0;
- _Channels = new PChannel[16];
- for (int i = 0; i < 16; i++) {
- _Channels[i] = new PChannel();
- }
- }
- public PMidiTrack Get_Track(int i) {
- if (_Tracks.Count() >= i) {
- return _Tracks.ElementAt(i);
- }
- return null;
- }
- public int Get_NumTracks() {
- return _Tracks.Count();
- }
- public void Parse(String fileName) {
- uint cnt = 0;
- byte[] fileBytes = File.ReadAllBytes(fileName);
- if ((fileBytes[0] != 0x4d) || (fileBytes[1] != 0x54) || (fileBytes[2] != 0x68) || (fileBytes[3] != 0x64)) {
- Console.WriteLine("Not a Midi File");
- return;
- }
- Debug.Log("Is a midi file: " + fileName);
- byte[] data = ReverseEndian(fileBytes, 4, 4);
- uint headerChunkSize = BitConverter.ToUInt32(data, 0);
- Console.WriteLine("Header Size: " + headerChunkSize.ToString());
- cnt = 8;
- data = ReverseEndian(fileBytes, cnt, 2);
- _Type = BitConverter.ToUInt16(data, 0);
- cnt += 2;
- data = ReverseEndian(fileBytes, cnt, 2);
- _NumTracks = BitConverter.ToUInt16(data, 0);
- cnt += 2;
- data = ReverseEndian(fileBytes, cnt, 2);
- _TicksPerQuarter = BitConverter.ToUInt16(data, 0);
- cnt += 2;
- _Tracks = new List<PMidiTrack>();
- Debug.Log("Type: " + _Type.ToString() + " Tracks: " + _NumTracks.ToString() + " Ticks Per Quarter: " + _TicksPerQuarter.ToString());
- uint offset = cnt;
- uint trackNum = 0;
- for (int j = 0; j < _NumTracks; j++) {
- Console.WriteLine("Parsing track: " + j.ToString());
- offset = Parse_Track(fileBytes, offset, trackNum);
- PMidiTrack mt = _Tracks.Last();
- Console.WriteLine("Midi Events: " + mt._Events.Count.ToString());
- mt.Calculate_Notes();
- Console.WriteLine("Notes in track: " + mt._Notes.Count().ToString());
- mt.Print_Notes();
- trackNum++;
- Console.WriteLine("Offset: " + offset.ToString());
- }
- //offset = Parse_Track(fileBytes, offset);
- Console.WriteLine("FileSize: " + offset.ToString());
- for (int i = 0; i < _NumTracks; i++) {
- foreach (MidiNote n in _Tracks[i]._Notes) {
- _Channels[n._Channel].Add_Note(n);
- }
- }
- for (int i = 0; i < 16; i++) {
- _Channels[i].Sort_Notes();
- }
- return;
- }
- // calculate the time offset of the next midi event
- private VariableTime Get_VariableTime(byte[] data, uint offset) {
- uint vtime = 0;
- uint bytesUsed = 1;
- byte byte1 = data[offset++];
- byte byte2;
- byte byte3;
- byte byte4;
- vtime = (uint)(byte1 & 0x7F);
- if ((byte1 & 0x80) != 0) {
- bytesUsed = 2;
- byte2 = data[offset++];
- vtime = vtime << 7;
- vtime += (uint)(byte2 & 0x7F);
- if ((byte2 & 0x80) != 0) {
- bytesUsed = 3;
- byte3 = data[offset++];
- vtime = vtime << 7;
- vtime += (uint)(byte3 & 0x7F);
- if ((byte3 & 0x80) != 0) {
- bytesUsed = 4;
- byte4 = data[offset++];
- vtime = vtime << 7;
- vtime += (uint)(byte3 & 0x7F);
- }
- }
- }
- Console.WriteLine(vtime.ToString());
- //VariableTime vt = new VariableTime { _Time = BitConverter.ToUInt32(temp, 0), _BytesUsed = bytesUsed };
- VariableTime vt = new VariableTime { _Time = vtime, _BytesUsed = bytesUsed };
- return vt;
- }
- private uint Parse_Track(byte[] fdata, uint offset, uint trackNum) {
- List<MidiEvent> events = new List<MidiEvent>();
- uint trackBytesParsed = 0;
- MidiControlEvent lastEvent = null;
- PMidiTrack mt = new PMidiTrack();
- if ((fdata[offset] != 0x4d) || (fdata[offset + 1] != 0x54) || (fdata[offset + 2] != 0x72) || (fdata[offset + 3] != 0x6b)) {
- Console.WriteLine("Not a Midi Track");
- return 0;
- }
- offset += 4;
- byte[] data = ReverseEndian(fdata, offset, 4);
- uint trackSize = BitConverter.ToUInt32(data, 0);
- Console.WriteLine("Track Size: " + trackSize.ToString());
- offset += 4;
- bool doneParsingTrack = false;
- while (!doneParsingTrack) {
- VariableTime vt = Get_VariableTime(fdata, offset);
- offset += vt._BytesUsed;
- trackBytesParsed += vt._BytesUsed;
- Console.WriteLine("Time: " + vt._Time.ToString());
- byte command = fdata[offset++];
- byte origCommand = command;
- uint channel;
- // RUNNING MODE
- if (command < 128) {
- Console.WriteLine("Running Mode");
- command = (byte)(lastEvent._Type);
- channel = lastEvent._Channel;
- offset--;
- }
- else {
- Console.WriteLine("Event");
- channel = (uint)(command & 0x0F);
- command &= 0xF0;
- }
- if (command == (byte)MidiEventType.NoteOn) {
- MidiControlEvent e = new MidiControlEvent();
- e._Type = MidiEventType.NoteOn;
- e._Time = vt._Time;
- byte eventData = fdata[offset++];
- e._Note = eventData;
- eventData = fdata[offset++];
- e._Velocity = eventData;
- e._Channel = channel;
- Console.WriteLine(e._Note.ToString() + " " + e._Velocity.ToString());
- mt._Events.Add(e);
- lastEvent = e;
- }
- else if (command == (byte)MidiEventType.NoteOff) {
- MidiControlEvent e = new MidiControlEvent();
- e._Type = MidiEventType.NoteOff;
- e._Time = vt._Time;
- byte eventData = fdata[offset++];
- e._Note = eventData;
- eventData = fdata[offset++];
- e._Velocity = eventData;
- e._Channel = channel;
- mt._Events.Add(e);
- lastEvent = e;
- }
- else if (command == (byte)MidiEventType.KeyAfter) {
- MidiControlEvent e = new MidiControlEvent();
- e._Type = MidiEventType.KeyAfter;
- e._Time = vt._Time;
- byte eventData = fdata[offset++];
- e._Note = eventData;
- eventData = fdata[offset++];
- e._Velocity = eventData;
- e._Channel = channel;
- mt._Events.Add(e);
- lastEvent = e;
- }
- else if (command == (byte)MidiEventType.ControlChange) {
- MidiControlEvent e = new MidiControlEvent();
- e._Type = MidiEventType.ControlChange;
- e._Time = vt._Time;
- byte eventData = fdata[offset++];
- e._Data1 = eventData;
- eventData = fdata[offset++];
- e._Data2 = eventData;
- e._Channel = channel;
- mt._Events.Add(e);
- lastEvent = e;
- }
- else if (command == (byte)MidiEventType.ProgramChange) {
- MidiControlEvent e = new MidiControlEvent();
- e._Type = MidiEventType.ProgramChange;
- e._Time = vt._Time;
- byte eventData = fdata[offset++];
- e._Data1 = eventData;
- e._Channel = channel;
- mt._Events.Add(e);
- lastEvent = e;
- }
- else if (command == (byte)MidiEventType.ChannelAfter) {
- MidiControlEvent e = new MidiControlEvent();
- e._Type = MidiEventType.ChannelAfter;
- e._Time = vt._Time;
- byte eventData = fdata[offset++];
- e._Data1 = eventData;
- e._Channel = channel;
- mt._Events.Add(e);
- lastEvent = e;
- }
- else if (command == (byte)MidiEventType.PitchChange) {
- MidiControlEvent e = new MidiControlEvent();
- e._Type = MidiEventType.PitchChange;
- e._Time = vt._Time;
- byte eventData = fdata[offset++];
- e._Data1 = eventData;
- eventData = fdata[offset++];
- e._Data2 = eventData;
- e._Channel = channel;
- mt._Events.Add(e);
- lastEvent = e;
- }
- else if (origCommand == (byte)MidiEventType.System) {
- byte curbyte = fdata[offset++];
- while (curbyte != 247) {
- curbyte = fdata[offset++];
- }
- offset++;
- }
- else if ((command | channel) == (byte)MidiEventType.Meta) {
- Console.WriteLine("Meta");
- byte metaCommand = fdata[offset++];
- if (metaCommand == (byte)MidiEventType.SetTrackSequence) {
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = MidiEventType.SetTrackSequence;
- me._Time = vt._Time;
- byte[] rdata = ReverseEndian(fdata, offset, 2);
- me._SequenceNum = BitConverter.ToUInt16(rdata, 0);
- offset += 2;
- mt._Events.Add(me);
- }
- else if ((metaCommand >= 1) && (metaCommand <= 7)) {
- int numDataBytes = fdata[offset++];
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = (MidiEventType)metaCommand;
- me._Time = vt._Time;
- for (int k = 0; k < numDataBytes; k++) {
- me._Text += (char)fdata[offset++];
- }
- mt._Events.Add(me);
- }
- // done parsing track so get out
- else if (metaCommand == (byte)MidiEventType.EndOfTrack) {
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = (MidiEventType)metaCommand;
- me._Time = vt._Time;
- offset++;
- mt._Events.Add(me);
- _Tracks.Add(mt);
- return offset;
- }
- else if (metaCommand == (byte)MidiEventType.SetTempo) {
- int numDataBytes = fdata[offset++];
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = MidiEventType.SetTempo;
- me._Time = vt._Time;
- me._Tempo = 0;
- me._Tempo = fdata[offset++];
- me._Tempo = me._Tempo << 8;
- me._Tempo += fdata[offset++];
- me._Tempo = me._Tempo << 8;
- me._Tempo += fdata[offset++];
- Debug.Log("Tempo: " + me._Tempo.ToString());
- _BPM = 60000000 / me._Tempo;
- Debug.Log("BB: " + _BPM.ToString());
- mt._Events.Add(me);
- }
- else if (metaCommand == (byte)MidiEventType.TimeSignature) {
- int numDataBytes = fdata[offset++];
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = MidiEventType.TimeSignature;
- me._Time = vt._Time;
- me._TimeSigNumerator = fdata[offset++];
- me._TimeSigDenomenator = fdata[offset++];
- me._MetronomeTicks = fdata[offset++];
- //_TicksPerQuarter = me._MetronomeTicks;
- me._32ToQuarters = fdata[offset++];
- mt._Events.Add(me);
- }
- else if (metaCommand == (byte)MidiEventType.KeySignature) {
- int numDataBytes = fdata[offset++];
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = MidiEventType.KeySignature;
- me._Time = vt._Time;
- me._Sharps = fdata[offset++];
- me._Major = fdata[offset++];
- mt._Events.Add(me);
- }
- else if (metaCommand == (byte)MidiEventType.SequencerSpecific) {
- uint numDataBytes = fdata[offset++];
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = MidiEventType.SequencerSpecific;
- me._Time = vt._Time;
- // we will just skip sequencer specific data
- offset += numDataBytes;
- mt._Events.Add(me);
- }
- else if (metaCommand == (byte)MidiEventType.TimingClock) {
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = MidiEventType.TimingClock;
- me._Time = vt._Time;
- mt._Events.Add(me);
- }
- else if (metaCommand == (byte)MidiEventType.StartSequence) {
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = MidiEventType.StartSequence;
- me._Time = vt._Time;
- mt._Events.Add(me);
- }
- else if (metaCommand == (byte)MidiEventType.ContinueSequence) {
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = MidiEventType.ContinueSequence;
- me._Time = vt._Time;
- mt._Events.Add(me);
- }
- else if (metaCommand == (byte)MidiEventType.StopSequence) {
- MetaMidiEvent me = new MetaMidiEvent();
- me._Type = MidiEventType.StopSequence;
- me._Time = vt._Time;
- mt._Events.Add(me);
- }
- else {
- Console.WriteLine("Invalid Meta Event Found: " + metaCommand.ToString());
- int numDataBytes = fdata[offset++];
- offset += (uint)numDataBytes; ;
- }
- }
- }
- return offset + trackSize;
- }
- }
- }
- MidiEvent.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace MidiParser {
- public enum MidiEventType {
- // control events
- NoteOff = 0x80,
- NoteOn = 0x90,
- KeyAfter = 0xA0,
- ControlChange = 0xB0,
- ProgramChange = 0xC0,
- ChannelAfter = 0xD0,
- PitchChange = 0xE0,
- Meta = 0xFF,
- System = 0xF0,
- // midi events
- SetTrackSequence = 0x00,
- TextEvent = 0x01,
- Copyright = 0x02,
- TrackName = 0x03,
- InstrumentName = 0x04,
- Lyric = 0x05,
- Marker = 0x06,
- Cue = 0x07,
- EndOfTrack = 0x2f,
- SetTempo = 0x51,
- TimeSignature = 0x58,
- KeySignature = 0x59,
- SequencerSpecific = 0x7F,
- TimingClock = 0xF8,
- StartSequence = 0xFA,
- ContinueSequence = 0xFB,
- StopSequence = 0xFC,
- Unknown = 0xDD
- };
- MidiTrack.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using MidiParser;
- namespace MidiParser {
- public class MidiNote {
- public uint _Time;
- public uint _Pitch;
- public uint _Velocity;
- public uint _Length;
- public uint _Channel;
- }
- public class PMidiTrack {
- public List<MidiEvent> _Events;
- public List<MidiNote> _Notes;
- public PMidiTrack() {
- _Events = new List<MidiEvent>();
- // _MetaEvents = new List<MetaMidiEvent>();
- _Notes = new List<MidiNote>();
- }
- public void Calculate_Notes() {
- int numNotes = _Events.Count();
- List<MidiNote> toEnd = new List<MidiNote>();
- uint curTime = 0;
- MidiNote curNote = new MidiNote();
- bool midNote = false;
- for (int i = 0; i < numNotes; i++) {
- MidiEvent e = _Events.ElementAt(i);
- curTime += e._Time;
- if (e.Is_NoteEvent()) {
- MidiControlEvent ce = (MidiControlEvent)e;
- // note on without a note off (is this possible?)
- if (ce._Type == MidiEventType.NoteOn) {
- // end a note with implicit NoteOff
- if (ce._Velocity == 0) {
- if (toEnd.Count == 0) {
- Console.WriteLine("Ending note that is not running?");
- }
- bool foundNote = false;
- foreach (MidiNote mn in toEnd) {
- if (mn._Pitch == ce._Note) {
- mn._Length = (uint)(curTime - mn._Time);
- _Notes.Add(mn);
- curNote = new MidiNote();
- toEnd.Remove(mn);
- foundNote = true;
- break;
- }
- }
- if (!foundNote) {
- Console.WriteLine("Could not find matching note to end");
- }
- }
- // note on with non-zero velocity
- else {
- //curStart = curTime;
- curNote._Time = curTime;
- curNote._Pitch = ce._Note;
- curNote._Velocity = ce._Velocity;
- curNote._Channel = ce._Channel;
- toEnd.Add(curNote);
- curNote = new MidiNote();
- // midNote = true;
- }
- }
- // ending note
- else if (ce._Type == MidiEventType.NoteOff) {
- if (!midNote) {
- Console.WriteLine("Turning a note off that isn't on!");
- }
- bool foundNote = false;
- foreach (MidiNote mn in toEnd) {
- if (mn._Pitch == ce._Note) {
- mn._Length = (uint)(curTime - mn._Time);
- _Notes.Add(mn);
- curNote = new MidiNote();
- toEnd.Remove(mn);
- foundNote = true;
- break;
- }
- }
- if (!foundNote) {
- Console.WriteLine("Could not find note to match NoteOff");
- }
- }
- }
- }
- }
- public void Print_Notes() {
- foreach (MidiNote n in _Notes) {
- Console.WriteLine(n._Time.ToString() + " " + n._Pitch.ToString() + " " + n._Length.ToString() + " V: " + n._Velocity.ToString());
- }
- }
- public uint Get_HighestNote() {
- uint high = 0;
- foreach (MidiNote n in _Notes) {
- if (n._Pitch > high) high = n._Pitch;
- }
- return high;
- }
- public uint Get_LowestNote() {
- uint low = 255;
- foreach (MidiNote n in _Notes) {
- if (n._Pitch < low) low = n._Pitch;
- }
- return low;
- }
- }
- }
- PChannel.cs
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace MidiParser {
- class PChannel {
- public List<MidiNote> _Notes;
- public PChannel() {
- _Notes = new List<MidiNote>();
- }
- public void Add_Note(MidiNote n) {
- _Notes.Add(n);
- }
- public void Sort_Notes() {
- _Notes = _Notes.OrderBy(o => o._Time).ToList();
- }
- public uint Get_HighestNote() {
- uint high = 0;
- foreach (MidiNote n in _Notes) {
- if (n._Pitch > high) high = n._Pitch;
- }
- return high;
- }
- public uint Get_LowestNote() {
- uint low = 255;
- foreach (MidiNote n in _Notes) {
- if (n._Pitch < low) low = n._Pitch;
- }
- return low;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement