66 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
			
		
		
	
	
			66 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Markdown
		
	
	
	
	
	
| # Exploring MIDI Files with MidiFile
 | |
| 
 | |
| The `MidiFile` class in NAudio allows you to open and examine the MIDI events in a standard MIDI file. It can also be used to create or update MIDI files, but this article focuses on reading.
 | |
| 
 | |
| ## Opening a MIDI file
 | |
| 
 | |
| Opening a `MidiFile` is as simple as creating a new `MidiFile` object and passing in the path. You can choose to enable `strictMode` which will throw exceptions if various faults are found with the file such as note on events missing a paired note off or controller values out of range.
 | |
| 
 | |
| ```c#
 | |
| var strictMode = false;
 | |
| var mf = new MidiFile(fileName, strictMode);
 | |
| ```
 | |
| 
 | |
| We can discover what MIDI file format the file is (Type 0 or type 1), as well as how many tracks are present and what the `DeltaTicksPerQuarterNote` value is.
 | |
| 
 | |
| ```c#
 | |
| Console.WriteLine("Format {0}, Tracks {1}, Delta Ticks Per Quarter Note {2}",
 | |
|                 mf.FileFormat, mf.Tracks, mf.DeltaTicksPerQuarterNote);
 | |
| ```
 | |
| 
 | |
| ## Examining the MIDI events
 | |
| 
 | |
| The MIDI events can be accessed with the `Events` property, passing in the index of the track whose events you want to access. This gives you a `MidiEventCollection` you can iterate through.
 | |
| 
 | |
| All the events in the MIDI file will be represented by a class inheriting from `MidiEvent`. The `MidiFile` class will also have set an `AbsoluteTime` property on each note, which represents the timestamp of the MIDI event from the start of file in terms of delta ticks.
 | |
| 
 | |
| For note on events, `MidiFile` will also try to pair up the corresponding `NoteOffEvent` events. This allows you to see the duration of each note (which is simply the difference in time between the absolute time of the `NoteOffEvent` and `NoteOnEvent`.
 | |
| 
 | |
| Each `MidiEvent` has a `ToString` overload with basic information, so we can print out details of all the events in the file like this. (we don't print out the `NoteOffEvent` instances, because they are each paired to a `NoteOnEvent` which reports the duration)
 | |
| 
 | |
| 
 | |
| ```c#
 | |
| for (int n = 0; n < mf.Tracks; n++)
 | |
| {
 | |
|     foreach (var midiEvent in mf.Events[n])
 | |
|     {
 | |
|         if(!MidiEvent.IsNoteOff(midiEvent))
 | |
|         {
 | |
|             Console.WriteLine("{0} {1}\r\n", ToMBT(midiEvent.AbsoluteTime, mf.DeltaTicksPerQuarterNote, timeSignature), midiEvent);
 | |
|         }
 | |
|     }
 | |
| }
 | |
| ```
 | |
| 
 | |
| You'll see that a helper `ToMBT` method is being used above to convert the `AbsoluteTime` into a more helpful Measures Beats Ticks format. Here's a basic implementation (that doesn't take into account any possible time signature events that might take place)
 | |
| 
 | |
| ```c#
 | |
| private string ToMBT(long eventTime, int ticksPerQuarterNote, TimeSignatureEvent timeSignature)
 | |
| {
 | |
|     int beatsPerBar = timeSignature == null ? 4 : timeSignature.Numerator;
 | |
|     int ticksPerBar = timeSignature == null ? ticksPerQuarterNote * 4 : (timeSignature.Numerator * ticksPerQuarterNote * 4) / (1 << timeSignature.Denominator);
 | |
|     int ticksPerBeat = ticksPerBar / beatsPerBar;
 | |
|     long bar = 1 + (eventTime / ticksPerBar);
 | |
|     long beat = 1 + ((eventTime % ticksPerBar) / ticksPerBeat);
 | |
|     long tick = eventTime % ticksPerBeat;
 | |
|     return String.Format("{0}:{1}:{2}", bar, beat, tick);
 | |
| }
 | |
| ```
 | |
| 
 | |
| Note that to get the `TimeSignatureEvent` needed by this function we can simply do something like:
 | |
| 
 | |
| ```c#
 | |
| var timeSignature = mf.Events[0].OfType<TimeSignatureEvent>().FirstOrDefault();
 | |
| ```
 | |
| 
 |