Advertisement
Guest User

Unity Midi Parser

a guest
Jun 1st, 2017
2,287
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 17.37 KB | None | 0 0
  1. MidiFile.cs
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using System.IO;
  8. using UnityEngine;
  9.  
  10. namespace MidiParser {
  11.     class MidiFileData {
  12.  
  13.         //static public uint _TicksPerQuarter;
  14.        
  15.         private int _NumTracks;
  16.         private int _Type;
  17.         public int _TicksPerQuarter;
  18.         public List<PMidiTrack> _Tracks;
  19.         public PChannel []_Channels;
  20.         public long _BPM = 0;
  21.        
  22.         class VariableTime {
  23.             public uint _BytesUsed { get; set; }
  24.             public uint _Time { get; set; }
  25.         }
  26.        
  27.        
  28.         static byte[] ReverseEndian(byte[] data, uint index, int size) {
  29.             byte[] reversed = new byte[size];
  30.             for (int i = 0; i < size; i++) {
  31.                 reversed[i] = data[index++];
  32.             }
  33.             Array.Reverse(reversed);
  34.             return reversed;
  35.         }
  36.        
  37.         public MidiFileData() {
  38.             _NumTracks = 0;
  39.             _Type = 0;
  40.             _TicksPerQuarter = 0;
  41.             _Channels = new PChannel[16];
  42.             for (int i = 0; i < 16; i++) {
  43.                 _Channels[i] = new PChannel();
  44.             }
  45.         }
  46.        
  47.         public PMidiTrack Get_Track(int i) {
  48.             if (_Tracks.Count() >= i) {
  49.                 return _Tracks.ElementAt(i);
  50.             }
  51.             return null;
  52.         }
  53.        
  54.        
  55.         public int Get_NumTracks() {
  56.             return _Tracks.Count();
  57.         }
  58.        
  59.        
  60.        
  61.         public void Parse(String fileName) {
  62.             uint cnt = 0;
  63.             byte[] fileBytes = File.ReadAllBytes(fileName);
  64.            
  65.            
  66.             if ((fileBytes[0] != 0x4d) || (fileBytes[1] != 0x54) || (fileBytes[2] != 0x68) || (fileBytes[3] != 0x64)) {
  67.                 Console.WriteLine("Not a Midi File");
  68.                 return;
  69.             }
  70.            
  71.             Debug.Log("Is a midi file: " + fileName);
  72.             byte[] data = ReverseEndian(fileBytes, 4, 4);
  73.             uint headerChunkSize = BitConverter.ToUInt32(data, 0);
  74.             Console.WriteLine("Header Size: " + headerChunkSize.ToString());
  75.             cnt = 8;
  76.            
  77.             data = ReverseEndian(fileBytes, cnt, 2);
  78.             _Type = BitConverter.ToUInt16(data, 0);
  79.             cnt += 2;
  80.            
  81.             data = ReverseEndian(fileBytes, cnt, 2);
  82.             _NumTracks = BitConverter.ToUInt16(data, 0);
  83.             cnt += 2;
  84.             data = ReverseEndian(fileBytes, cnt, 2);
  85.             _TicksPerQuarter = BitConverter.ToUInt16(data, 0);
  86.             cnt += 2;
  87.            
  88.            
  89.             _Tracks = new List<PMidiTrack>();
  90.             Debug.Log("Type: " + _Type.ToString() + "    Tracks: " + _NumTracks.ToString() + "   Ticks Per Quarter: " + _TicksPerQuarter.ToString());
  91.             uint offset = cnt;
  92.             uint trackNum = 0;
  93.             for (int j = 0; j < _NumTracks; j++) {
  94.                 Console.WriteLine("Parsing track: " + j.ToString());
  95.                 offset = Parse_Track(fileBytes, offset, trackNum);
  96.                 PMidiTrack mt = _Tracks.Last();
  97.                 Console.WriteLine("Midi Events: " + mt._Events.Count.ToString());
  98.                 mt.Calculate_Notes();
  99.                 Console.WriteLine("Notes in track: " + mt._Notes.Count().ToString());
  100.                 mt.Print_Notes();
  101.                 trackNum++;
  102.                 Console.WriteLine("Offset: " + offset.ToString());
  103.             }
  104.             //offset = Parse_Track(fileBytes, offset);
  105.             Console.WriteLine("FileSize: " + offset.ToString());
  106.            
  107.            
  108.             for (int i = 0; i < _NumTracks; i++) {
  109.                 foreach (MidiNote n in _Tracks[i]._Notes) {
  110.                     _Channels[n._Channel].Add_Note(n);
  111.                 }
  112.             }
  113.             for (int i = 0; i < 16; i++) {
  114.                 _Channels[i].Sort_Notes();
  115.             }
  116.             return;
  117.         }
  118.        
  119.        
  120.        
  121.         // calculate the time offset of the next midi event
  122.         private VariableTime Get_VariableTime(byte[] data, uint offset) {
  123.             uint vtime = 0;
  124.             uint bytesUsed = 1;
  125.            
  126.             byte byte1 = data[offset++];
  127.             byte byte2;
  128.             byte byte3;
  129.             byte byte4;
  130.            
  131.            
  132.             vtime = (uint)(byte1 & 0x7F);
  133.             if ((byte1 & 0x80) != 0) {
  134.                 bytesUsed = 2;
  135.                 byte2 = data[offset++];
  136.                 vtime = vtime << 7;
  137.                 vtime += (uint)(byte2 & 0x7F);
  138.                
  139.                 if ((byte2 & 0x80) != 0) {
  140.                     bytesUsed = 3;
  141.                     byte3 = data[offset++];
  142.                     vtime = vtime << 7;
  143.                     vtime += (uint)(byte3 & 0x7F);
  144.                    
  145.                     if ((byte3 & 0x80) != 0) {
  146.                         bytesUsed = 4;
  147.                         byte4 = data[offset++];
  148.                         vtime = vtime << 7;
  149.                         vtime += (uint)(byte3 & 0x7F);
  150.                     }
  151.                 }
  152.             }
  153.            
  154.             Console.WriteLine(vtime.ToString());
  155.             //VariableTime vt = new VariableTime { _Time = BitConverter.ToUInt32(temp, 0), _BytesUsed = bytesUsed };
  156.             VariableTime vt = new VariableTime { _Time = vtime, _BytesUsed = bytesUsed };
  157.             return vt;
  158.         }
  159.        
  160.        
  161.        
  162.         private uint Parse_Track(byte[] fdata, uint offset, uint trackNum) {
  163.             List<MidiEvent> events = new List<MidiEvent>();
  164.            
  165.             uint trackBytesParsed = 0;
  166.             MidiControlEvent lastEvent = null;
  167.             PMidiTrack mt = new PMidiTrack();
  168.            
  169.             if ((fdata[offset] != 0x4d) || (fdata[offset + 1] != 0x54) || (fdata[offset + 2] != 0x72) || (fdata[offset + 3] != 0x6b)) {
  170.                 Console.WriteLine("Not a Midi Track");
  171.                 return 0;
  172.             }
  173.             offset += 4;
  174.            
  175.             byte[] data = ReverseEndian(fdata, offset, 4);
  176.             uint trackSize = BitConverter.ToUInt32(data, 0);
  177.             Console.WriteLine("Track Size: " + trackSize.ToString());
  178.             offset += 4;
  179.            
  180.             bool doneParsingTrack = false;
  181.             while (!doneParsingTrack) {
  182.                
  183.                 VariableTime vt = Get_VariableTime(fdata, offset);
  184.                 offset += vt._BytesUsed;
  185.                 trackBytesParsed += vt._BytesUsed;
  186.                 Console.WriteLine("Time: " + vt._Time.ToString());
  187.                
  188.                
  189.                 byte command = fdata[offset++];
  190.                 byte origCommand = command;
  191.                 uint channel;
  192.                
  193.                 // RUNNING MODE
  194.                 if (command < 128) {
  195.                     Console.WriteLine("Running Mode");
  196.                     command = (byte)(lastEvent._Type);
  197.                     channel = lastEvent._Channel;
  198.                     offset--;
  199.                 }
  200.                 else {
  201.                     Console.WriteLine("Event");
  202.                     channel = (uint)(command & 0x0F);
  203.                     command &= 0xF0;
  204.                 }
  205.                
  206.                 if (command == (byte)MidiEventType.NoteOn) {
  207.                     MidiControlEvent e = new MidiControlEvent();
  208.                     e._Type = MidiEventType.NoteOn;
  209.                     e._Time = vt._Time;
  210.                     byte eventData = fdata[offset++];
  211.                     e._Note = eventData;
  212.                     eventData = fdata[offset++];
  213.                     e._Velocity = eventData;
  214.                     e._Channel = channel;
  215.                     Console.WriteLine(e._Note.ToString() + "    " + e._Velocity.ToString());
  216.                     mt._Events.Add(e);
  217.                     lastEvent = e;
  218.                    
  219.                 }
  220.                 else if (command == (byte)MidiEventType.NoteOff) {
  221.                     MidiControlEvent e = new MidiControlEvent();
  222.                     e._Type = MidiEventType.NoteOff;
  223.                     e._Time = vt._Time;
  224.                     byte eventData = fdata[offset++];
  225.                     e._Note = eventData;
  226.                     eventData = fdata[offset++];
  227.                     e._Velocity = eventData;
  228.                     e._Channel = channel;
  229.                     mt._Events.Add(e);
  230.                     lastEvent = e;
  231.                 }
  232.                 else if (command == (byte)MidiEventType.KeyAfter) {
  233.                     MidiControlEvent e = new MidiControlEvent();
  234.                     e._Type = MidiEventType.KeyAfter;
  235.                     e._Time = vt._Time;
  236.                     byte eventData = fdata[offset++];
  237.                     e._Note = eventData;
  238.                     eventData = fdata[offset++];
  239.                     e._Velocity = eventData;
  240.                     e._Channel = channel;
  241.                     mt._Events.Add(e);
  242.                     lastEvent = e;
  243.                 }
  244.                 else if (command == (byte)MidiEventType.ControlChange) {
  245.                     MidiControlEvent e = new MidiControlEvent();
  246.                     e._Type = MidiEventType.ControlChange;
  247.                     e._Time = vt._Time;
  248.                     byte eventData = fdata[offset++];
  249.                     e._Data1 = eventData;
  250.                     eventData = fdata[offset++];
  251.                     e._Data2 = eventData;
  252.                     e._Channel = channel;
  253.                     mt._Events.Add(e);
  254.                     lastEvent = e;
  255.                 }
  256.                 else if (command == (byte)MidiEventType.ProgramChange) {
  257.                     MidiControlEvent e = new MidiControlEvent();
  258.                     e._Type = MidiEventType.ProgramChange;
  259.                     e._Time = vt._Time;
  260.                     byte eventData = fdata[offset++];
  261.                     e._Data1 = eventData;
  262.                     e._Channel = channel;
  263.                     mt._Events.Add(e);
  264.                     lastEvent = e;
  265.                 }
  266.                 else if (command == (byte)MidiEventType.ChannelAfter) {
  267.                     MidiControlEvent e = new MidiControlEvent();
  268.                     e._Type = MidiEventType.ChannelAfter;
  269.                     e._Time = vt._Time;
  270.                     byte eventData = fdata[offset++];
  271.                     e._Data1 = eventData;
  272.                     e._Channel = channel;
  273.                     mt._Events.Add(e);
  274.                     lastEvent = e;
  275.                 }
  276.                 else if (command == (byte)MidiEventType.PitchChange) {
  277.                     MidiControlEvent e = new MidiControlEvent();
  278.                     e._Type = MidiEventType.PitchChange;
  279.                     e._Time = vt._Time;
  280.                     byte eventData = fdata[offset++];
  281.                     e._Data1 = eventData;
  282.                     eventData = fdata[offset++];
  283.                     e._Data2 = eventData;
  284.                     e._Channel = channel;
  285.                     mt._Events.Add(e);
  286.                     lastEvent = e;
  287.                 }
  288.  
  289.  
  290.                 else if (origCommand == (byte)MidiEventType.System) {
  291.                     byte curbyte = fdata[offset++];
  292.                     while (curbyte != 247) {
  293.                         curbyte = fdata[offset++];
  294.                     }
  295.                     offset++;
  296.                 }
  297.  
  298.                 else if ((command | channel) == (byte)MidiEventType.Meta) {
  299.                     Console.WriteLine("Meta");
  300.                     byte metaCommand = fdata[offset++];
  301.                    
  302.                     if (metaCommand == (byte)MidiEventType.SetTrackSequence) {
  303.                        
  304.                         MetaMidiEvent me = new MetaMidiEvent();
  305.                         me._Type = MidiEventType.SetTrackSequence;
  306.                         me._Time = vt._Time;
  307.                         byte[] rdata = ReverseEndian(fdata, offset, 2);
  308.                         me._SequenceNum = BitConverter.ToUInt16(rdata, 0);
  309.                         offset += 2;
  310.                         mt._Events.Add(me);
  311.                     }
  312.                     else if ((metaCommand >= 1) && (metaCommand <= 7)) {
  313.                         int numDataBytes = fdata[offset++];
  314.                         MetaMidiEvent me = new MetaMidiEvent();
  315.                         me._Type = (MidiEventType)metaCommand;
  316.                         me._Time = vt._Time;
  317.                         for (int k = 0; k < numDataBytes; k++) {
  318.                             me._Text += (char)fdata[offset++];
  319.                         }
  320.                         mt._Events.Add(me);
  321.                     }
  322.                    
  323.                     // done parsing track so get out
  324.                     else if (metaCommand == (byte)MidiEventType.EndOfTrack) {
  325.                         MetaMidiEvent me = new MetaMidiEvent();
  326.                         me._Type = (MidiEventType)metaCommand;
  327.                         me._Time = vt._Time;
  328.                         offset++;
  329.                         mt._Events.Add(me);
  330.                         _Tracks.Add(mt);
  331.                         return offset;
  332.                     }
  333.                     else if (metaCommand == (byte)MidiEventType.SetTempo) {
  334.                         int numDataBytes = fdata[offset++];
  335.                         MetaMidiEvent me = new MetaMidiEvent();
  336.                         me._Type = MidiEventType.SetTempo;
  337.                         me._Time = vt._Time;
  338.                         me._Tempo = 0;
  339.                         me._Tempo = fdata[offset++];
  340.                         me._Tempo = me._Tempo << 8;
  341.                         me._Tempo += fdata[offset++];
  342.                         me._Tempo = me._Tempo << 8;
  343.                         me._Tempo += fdata[offset++];
  344.                         Debug.Log("Tempo: " + me._Tempo.ToString());
  345.                         _BPM = 60000000 / me._Tempo;
  346.                         Debug.Log("BB: " + _BPM.ToString());
  347.  
  348.                         mt._Events.Add(me);
  349.                     }
  350.                     else if (metaCommand == (byte)MidiEventType.TimeSignature) {
  351.                         int numDataBytes = fdata[offset++];
  352.                         MetaMidiEvent me = new MetaMidiEvent();
  353.                         me._Type = MidiEventType.TimeSignature;
  354.                         me._Time = vt._Time;
  355.                         me._TimeSigNumerator = fdata[offset++];
  356.                         me._TimeSigDenomenator = fdata[offset++];
  357.                         me._MetronomeTicks = fdata[offset++];
  358.                         //_TicksPerQuarter = me._MetronomeTicks;
  359.                         me._32ToQuarters = fdata[offset++];
  360.                         mt._Events.Add(me);
  361.                     }
  362.                     else if (metaCommand == (byte)MidiEventType.KeySignature) {
  363.                         int numDataBytes = fdata[offset++];
  364.                         MetaMidiEvent me = new MetaMidiEvent();
  365.                         me._Type = MidiEventType.KeySignature;
  366.                         me._Time = vt._Time;
  367.                         me._Sharps = fdata[offset++];
  368.                         me._Major = fdata[offset++];
  369.                         mt._Events.Add(me);
  370.                     }
  371.                     else if (metaCommand == (byte)MidiEventType.SequencerSpecific) {
  372.                         uint numDataBytes = fdata[offset++];
  373.                         MetaMidiEvent me = new MetaMidiEvent();
  374.                         me._Type = MidiEventType.SequencerSpecific;
  375.                         me._Time = vt._Time;
  376.                         // we will just skip sequencer specific data
  377.                         offset += numDataBytes;
  378.                         mt._Events.Add(me);
  379.                     }
  380.                     else if (metaCommand == (byte)MidiEventType.TimingClock) {
  381.                         MetaMidiEvent me = new MetaMidiEvent();
  382.                         me._Type = MidiEventType.TimingClock;
  383.                         me._Time = vt._Time;
  384.                         mt._Events.Add(me);
  385.                     }
  386.                     else if (metaCommand == (byte)MidiEventType.StartSequence) {
  387.                         MetaMidiEvent me = new MetaMidiEvent();
  388.                         me._Type = MidiEventType.StartSequence;
  389.                         me._Time = vt._Time;
  390.                         mt._Events.Add(me);
  391.                     }
  392.                     else if (metaCommand == (byte)MidiEventType.ContinueSequence) {
  393.                         MetaMidiEvent me = new MetaMidiEvent();
  394.                         me._Type = MidiEventType.ContinueSequence;
  395.                         me._Time = vt._Time;
  396.                         mt._Events.Add(me);
  397.                     }
  398.                     else if (metaCommand == (byte)MidiEventType.StopSequence) {
  399.                         MetaMidiEvent me = new MetaMidiEvent();
  400.                         me._Type = MidiEventType.StopSequence;
  401.                         me._Time = vt._Time;
  402.                         mt._Events.Add(me);
  403.                     }
  404.                     else {
  405.                         Console.WriteLine("Invalid Meta Event Found: " + metaCommand.ToString());
  406.                         int numDataBytes = fdata[offset++];
  407.                         offset += (uint)numDataBytes; ;
  408.                     }
  409.                    
  410.                    
  411.                 }
  412.                
  413.             }
  414.            
  415.            
  416.            
  417.             return offset + trackSize;
  418.         }
  419.     }
  420. }
  421.  
  422.  
  423. MidiEvent.cs
  424. using System;
  425. using System.Collections.Generic;
  426. using System.Linq;
  427. using System.Text;
  428.  
  429. namespace MidiParser {
  430.  
  431.  
  432.     public enum MidiEventType {
  433.  
  434.         // control events
  435.         NoteOff = 0x80,
  436.         NoteOn = 0x90,
  437.         KeyAfter = 0xA0,
  438.         ControlChange = 0xB0,
  439.         ProgramChange = 0xC0,
  440.         ChannelAfter = 0xD0,
  441.         PitchChange = 0xE0,
  442.         Meta = 0xFF,
  443.         System = 0xF0,
  444.    
  445.         // midi events
  446.         SetTrackSequence = 0x00,
  447.         TextEvent = 0x01,
  448.         Copyright = 0x02,
  449.         TrackName = 0x03,
  450.         InstrumentName = 0x04,
  451.         Lyric = 0x05,
  452.         Marker = 0x06,
  453.         Cue = 0x07,
  454.         EndOfTrack = 0x2f,
  455.         SetTempo = 0x51,
  456.         TimeSignature = 0x58,
  457.         KeySignature = 0x59,
  458.         SequencerSpecific = 0x7F,
  459.         TimingClock = 0xF8,
  460.         StartSequence = 0xFA,
  461.         ContinueSequence = 0xFB,
  462.         StopSequence = 0xFC,
  463.  
  464.         Unknown = 0xDD
  465.     };
  466.  
  467.  
  468.  
  469. MidiTrack.cs
  470.  
  471. using System;
  472. using System.Collections.Generic;
  473. using System.Linq;
  474. using System.Text;
  475. using MidiParser;
  476.  
  477.  
  478. namespace MidiParser {
  479.  
  480.     public class MidiNote {
  481.         public uint _Time;
  482.         public uint _Pitch;
  483.         public uint _Velocity;
  484.         public uint _Length;
  485.         public uint _Channel;
  486.     }
  487.  
  488.  
  489.     public class PMidiTrack {
  490.         public List<MidiEvent> _Events;
  491.  
  492.         public List<MidiNote> _Notes;
  493.  
  494.         public PMidiTrack() {
  495.             _Events = new List<MidiEvent>();
  496.             //  _MetaEvents = new List<MetaMidiEvent>();
  497.             _Notes = new List<MidiNote>();
  498.         }
  499.  
  500.         public void Calculate_Notes() {
  501.             int numNotes = _Events.Count();
  502.             List<MidiNote> toEnd = new List<MidiNote>();
  503.             uint curTime = 0;
  504.            
  505.             MidiNote curNote = new MidiNote();
  506.             bool midNote = false;
  507.            
  508.             for (int i = 0; i < numNotes; i++) {
  509.                
  510.                 MidiEvent e = _Events.ElementAt(i);
  511.                 curTime += e._Time;
  512.                
  513.                 if (e.Is_NoteEvent()) {
  514.                     MidiControlEvent ce = (MidiControlEvent)e;
  515.                    
  516.                     // note on without a note off (is this possible?)
  517.                     if (ce._Type == MidiEventType.NoteOn) {
  518.                        
  519.                         // end a note with implicit NoteOff
  520.                         if (ce._Velocity == 0) {
  521.                             if (toEnd.Count == 0) {
  522.                                 Console.WriteLine("Ending note that is not running?");
  523.                             }
  524.                            
  525.                             bool foundNote = false;
  526.                             foreach (MidiNote mn in toEnd) {
  527.                                 if (mn._Pitch == ce._Note) {
  528.                                     mn._Length = (uint)(curTime - mn._Time);
  529.                                     _Notes.Add(mn);
  530.                                     curNote = new MidiNote();
  531.                                     toEnd.Remove(mn);
  532.                                     foundNote = true;
  533.                                     break;
  534.                                 }
  535.                             }
  536.                             if (!foundNote) {
  537.                                 Console.WriteLine("Could not find matching note to end");
  538.                             }
  539.                         }
  540.                        
  541.                        
  542.                         // note on with non-zero velocity
  543.                         else {
  544.                            
  545.                            
  546.                             //curStart = curTime;
  547.                             curNote._Time = curTime;
  548.                             curNote._Pitch = ce._Note;
  549.                             curNote._Velocity = ce._Velocity;
  550.                             curNote._Channel = ce._Channel;
  551.                             toEnd.Add(curNote);
  552.                             curNote = new MidiNote();
  553.                             // midNote = true;
  554.                         }
  555.                     }
  556.                    
  557.                     // ending note
  558.                     else if (ce._Type == MidiEventType.NoteOff) {
  559.                         if (!midNote) {
  560.                             Console.WriteLine("Turning a note off that isn't on!");
  561.                         }
  562.                        
  563.                         bool foundNote = false;
  564.                         foreach (MidiNote mn in toEnd) {
  565.                             if (mn._Pitch == ce._Note) {
  566.                                 mn._Length = (uint)(curTime - mn._Time);
  567.                                 _Notes.Add(mn);
  568.                                 curNote = new MidiNote();
  569.                                 toEnd.Remove(mn);
  570.                                 foundNote = true;
  571.                                 break;
  572.                             }
  573.                         }
  574.                         if (!foundNote) {
  575.                             Console.WriteLine("Could not find note to match NoteOff");
  576.                         }
  577.                        
  578.                     }
  579.                 }
  580.             }
  581.         }
  582.  
  583.         public void Print_Notes() {
  584.             foreach (MidiNote n in _Notes) {
  585.                 Console.WriteLine(n._Time.ToString() + "   " + n._Pitch.ToString() + "   " + n._Length.ToString() + "    V: " + n._Velocity.ToString());
  586.             }
  587.         }
  588.  
  589.         public uint Get_HighestNote() {
  590.             uint high = 0;
  591.            
  592.             foreach (MidiNote n in _Notes) {
  593.                 if (n._Pitch > high) high = n._Pitch;
  594.             }
  595.             return high;
  596.         }
  597.        
  598.         public uint Get_LowestNote() {
  599.             uint low = 255;
  600.            
  601.             foreach (MidiNote n in _Notes) {
  602.                 if (n._Pitch < low) low = n._Pitch;
  603.             }
  604.             return low;
  605.         }
  606.  
  607.     }
  608. }
  609.  
  610.  
  611. PChannel.cs
  612. using System;
  613. using System.Collections.Generic;
  614. using System.Linq;
  615. using System.Text;
  616.  
  617.  
  618. namespace MidiParser {
  619.     class PChannel {
  620.  
  621.         public List<MidiNote> _Notes;
  622.  
  623.  
  624.         public PChannel() {
  625.             _Notes = new List<MidiNote>();
  626.         }
  627.  
  628.         public void Add_Note(MidiNote n) {
  629.             _Notes.Add(n);
  630.         }
  631.  
  632.         public void Sort_Notes() {
  633.  
  634.             _Notes = _Notes.OrderBy(o => o._Time).ToList();
  635.  
  636.         }
  637.  
  638.  
  639.         public uint Get_HighestNote() {
  640.             uint high = 0;
  641.            
  642.             foreach (MidiNote n in _Notes) {
  643.                 if (n._Pitch > high) high = n._Pitch;
  644.             }
  645.             return high;
  646.         }
  647.        
  648.         public uint Get_LowestNote() {
  649.             uint low = 255;
  650.            
  651.             foreach (MidiNote n in _Notes) {
  652.                 if (n._Pitch < low) low = n._Pitch;
  653.             }
  654.             return low;
  655.         }
  656.  
  657.     }
  658. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement