public static Section ReadMidi(string filepath) { var midi = MidiFile.Read(filepath); var section = new Section(Path.GetFileNameWithoutExtension(filepath)); foreach (var midiChunk in midi.Chunks) { if (!(midiChunk is TrackChunk chunk)) { continue; } var phrase = new Phrase(); var programEvent = chunk.Events.OfType <ProgramChangeEvent>().FirstOrDefault(); if (programEvent != null) { phrase.Instrument = (MidiInstrument)(int)programEvent.ProgramNumber; } var nameEvent = chunk.Events.OfType <SequenceTrackNameEvent>().FirstOrDefault(); if (nameEvent != null) { phrase.Description = nameEvent.Text.Replace("\0", ""); } var tempoEvent = chunk.Events.OfType <SetTempoEvent>().FirstOrDefault(); if (tempoEvent != null) { phrase.Bpm = Math.Round(1M / (tempoEvent.MicrosecondsPerQuarterNote / 60M) * 1000000M, 2); } var panEvent = chunk.Events .OfType <ControlChangeEvent>().FirstOrDefault(x => x.ControlNumber == (SevenBitNumber)10); if (panEvent != null) { phrase.Panning = (panEvent.ControlValue / 126M * 2M) - 1M; } using (var manager = new TimedEventsManager(chunk.Events)) { phrase.Elements = manager.Events .Where(x => x.Event is NoteOnEvent) .Select(GetNewPhraseElement) .ToList(); var offNotes = manager.Events .Where(x => x.Event is NoteOffEvent) .Select(x => new Tuple <decimal, int>(Convert.ToDecimal(x.Time) / 24M, ((NoteOffEvent)x.Event).NoteNumber - NoteOffset)) .ToList(); foreach (var element in phrase.Elements) { var offNote = offNotes .FirstOrDefault(x => x.Item1 > element.Position && x.Item2 == element.Note); if (offNote == null) { throw new ApplicationException("No off note found"); } element.Duration = offNote.Item1 - element.Position; } } phrase.IsDrums = chunk.Events.OfType <NoteOnEvent>().Any(x => x.Channel == (FourBitNumber)DrumChannel); phrase.PhraseLength = NoteHelper.GetTotalDuration(phrase); phrase.Elements = phrase.Elements.OrderBy(x => x.Position).ThenBy(x => x.Note).ToList(); section.Phrases.Add(phrase); } if (section.Phrases.Count == 0) { throw new ApplicationException("Invalid Midi File"); } // remove blank tempo phrase if (section.Phrases.Count > 1 && section.Phrases[0].PhraseLength == 0 && section.Phrases[0].Instrument == MidiInstrument.AcousticGrandPiano && !section.Phrases[0].IsDrums) { section.Phrases.RemoveAt(0); } return(section); }