Beispiel #1
0
        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);
        }