public static Model Parse(string filename, bool computeNoteLenghts = true, bool computePitchBends = true, bool computeRealVolumes = true, bool determineInstruments = false, bool normalizeMetre = false, bool prolongSustainedNotes = false, bool showBeats = false, bool analyzeKeys = false, bool analyzeChords = false, bool playChords = false, bool discretizeBends = false, Tone?transposeTo = null) { if (playChords) { analyzeChords = true; } if (analyzeChords) { analyzeKeys = true; } if (discretizeBends) { computePitchBends = true; } if (transposeTo != null) { analyzeKeys = true; } var midi = new Model(); using (var reader = new MidiBigEndianReader(File.Open(filename, FileMode.Open))) { ParseHeaderChunk(reader, midi); foreach (var track in midi.Tracks) { ParseTrackChunk(reader, track); } if (computeNoteLenghts || normalizeMetre || showBeats) { TimeCalculator.ComputeRealTimes(midi); TimeCalculator.CreateNoteLengths(midi); } midi.Length = midi.EventsOfType <EndOfTrack>().Max(e => e.AbsoluteRealTime); if (computeRealVolumes || normalizeMetre || showBeats) { var collector = new VolumeChangeCollector(midi); collector.DetermineVolumes(); } if (prolongSustainedNotes || normalizeMetre || showBeats) { var sustainer = new Sustainer(midi); sustainer.ProlongSustainedNotes(); } if (normalizeMetre) { Normalizer.CalculateMetre(midi); Normalizer.Normalize(midi, prolongSustainedNotes); } else if (showBeats) { Normalizer.CalculateMetre(midi); Normalizer.AddBeats(midi, prolongSustainedNotes); } if (computePitchBends) { PitchBendCalculator.JoinPitchBends(midi); PitchBendCalculator.DeterminePitchRanges(midi); if (discretizeBends) { PitchBendCalculator.DiscretizeBends(midi); } } if (determineInstruments) { var collector = new InstrumentChangeCollector(midi); collector.DetermineInstruments(); } if (analyzeKeys) { if (!normalizeMetre && !showBeats) { Normalizer.CalculateMetre(midi); } if (midi.EventsOfType <KeySignature>().Any(k => k.Key.Tone != Tone.C || k.Key.Scale != Scale.Major)) { midi.Key = midi.EventsOfType <KeySignature>().First().Key; midi.IsKeyFoundByMidiItself = true; } else { MLKeyFinder.DetectKey(midi); midi.IsKeyFoundByMidiItself = false; } } if (analyzeChords) { var analyzer = new ChordAnalyzer(midi); analyzer.Analyze(); if (playChords) { analyzer.AddChordNotesToModel(); } } if (transposeTo != null) { Transposer.Transpose(midi, (Tone)transposeTo); } ChannelPlayabilityChecker.Check(midi); } return(midi); }
/// <summary> /// Calculates the meter and saves it in BeatEvents /// </summary> /// <param name="midi"></param> public static void CalculateMetre(Model midi) { var offset = CalculateMeterFitnessMetric(midi); //use beats that are already in the midi file if (midi.GoodnessOfMetreFit > 0.4) { midi.IsNormalizedByMidiItself = true; CalculateImplicitMetre(); } // else detect the meter by the detector else { midi.IsNormalizedByMidiItself = false; PadNotesToZero(midi); var metre = Analyze(midi).Select(TimeSpan.FromMilliseconds).ToList(); for (var i = metre.Sum(t => t.TotalMilliseconds); i <= midi.Length.TotalMilliseconds; i += metre.Last().TotalMilliseconds) { metre.Add(metre.Last()); } CreateBeatEvents(metre); var beatStrengthAnalyzer = new BeatStrengthAnalyzer(midi); beatStrengthAnalyzer.Analyze(); } var firstBeatTime = midi.EventsOfType <BeatEvent>().Min(b => b.AbsoluteRealTime); if (firstBeatTime != TimeSpan.Zero) { var beatEvent = new BeatEvent { AbsoluteRealTime = TimeSpan.Zero, Level = 2, Length = firstBeatTime }; midi.Tracks[0].MetaEvents.Add(beatEvent); } if (!midi.IsNormalizedByMidiItself) { midi.GoodnessOfMetreFit = (float)CalculateMeterFitnessOfBeatEvents(midi); } void CalculateImplicitMetre() { var baseTick = GetTicksPerBeat(midi); var end = midi.EventsOfType <EndOfTrack>().Max(e => e.AbsoluteTime); var timeSignatures = new Queue <TimeSignature>(midi.EventsOfType <TimeSignature>().OrderBy(e => e.AbsoluteTime)); var actualTimeSignature = new TimeSignature { AbsoluteTime = 0, Numerator = 4, Denominator = 4 }; var tick = baseTick * 4 / actualTimeSignature.Denominator; var barCounter = 0; if (offset > 0) { midi.Tracks[0].MetaEvents.Add(new BeatEvent { AbsoluteTime = 0, Level = 2 }); } for (var time = offset; time <= end; time += tick) { if (timeSignatures.Count > 0 && timeSignatures.Peek().AbsoluteTime <= time) { actualTimeSignature = timeSignatures.Dequeue(); barCounter = 0; tick = baseTick * 4 / actualTimeSignature.Denominator; } var level = 2; if (barCounter % actualTimeSignature.Numerator == 0) { level = 0; } else if ((actualTimeSignature.Numerator == 4 && barCounter % actualTimeSignature.Numerator == 2) || (actualTimeSignature.Numerator == 6 && barCounter % actualTimeSignature.Numerator == 3)) { level = 1; } var beat = new BeatEvent { AbsoluteTime = time, Level = (byte)level }; midi.Tracks[0].MetaEvents.Add(beat); barCounter++; } TimeCalculator.ComputeRealTimes(midi); CalculateBeatLengths(); var averageLength = midi.EventsOfType <BeatEvent>().Average(b => b.Length.TotalMilliseconds); while (averageLength > 1000) { var beats = midi.EventsOfType <BeatEvent>().ToArray(); foreach (var beat in beats) { midi.Tracks[0].MetaEvents.Add(new BeatEvent { AbsoluteRealTime = beat.AbsoluteRealTime + beat.Length.Divide(2), Level = 2 }); } CalculateBeatLengths(); averageLength = averageLength / 2; } } void CalculateBeatLengths() { var beatEvents = midi.EventsOfType <BeatEvent>().OrderBy(b => b.AbsoluteRealTime).ToArray(); for (var i = 0; i < beatEvents.Length - 1; i++) { beatEvents[i].Length = beatEvents[i + 1].AbsoluteRealTime - beatEvents[i].AbsoluteRealTime; } beatEvents[beatEvents.Length - 1].Length = beatEvents[beatEvents.Length - 2].Length; } void CreateBeatEvents(IEnumerable <TimeSpan> metre) { var time = TimeSpan.Zero; foreach (var beat in metre) { var beatEvent = new BeatEvent { AbsoluteRealTime = time, Level = 0, Length = beat }; midi.Tracks[0].MetaEvents.Add(beatEvent); time += beat; } } }