public void setMidiData(MidiEventCollection m, MidiSelection e) { // Get a temporary filename for the MIDI file. // We do this because we don't want to save to the // user's folders unless they request it. String temp = getTemporaryMidiFileName(); if (e == MidiSelection.HARMONY_ONLY) { // Store the incoming MIDI data, but strip out // track 0 m.RemoveTrack(0); midiData[(int)e] = m; } else { // Store the incoming MIDI data midiData[(int)e] = m; } // Export the temp copy... // NOTE: this is how we save MIDI files... m.PrepareForExport(); MidiFile.Export(temp, m); // And update the filename so we remember where the // MIDI temp file is... fileNames[(int)e] = temp; return; }
private static void RemoveTracks(MidiEventCollection midi, IEnumerable <int> trackNumbersToRemove) { // Remove in reverse order so the number of the remaining tracks to remove // aren't adjusted by the removals as we go foreach (var t in trackNumbersToRemove.OrderByDescending(i => i)) { midi.RemoveTrack(t); } }
private bool RemoveTrackIfEmpty(MidiEventCollection midi, int trackNumber) { // If a track only has an EndTrack (and optionally a name) then it is "empty" if (midi[trackNumber].All(e => e.IsSequenceTrackName() || MidiEvent.IsEndTrack(e))) { midi.RemoveTrack(trackNumber); return(true); } return(false); }
public void ConsolidateTracks(MidiEventCollection midi) { // find all of the names var nameCounts = new Dictionary <string, int>(); for (var t = 0; t < midi.Tracks; t++) { var names = midi[t]. OfType <TextEvent>(). Where(e => e.MetaEventType == MetaEventType.SequenceTrackName). ToList(); if (names.Count > 1) { var detail = string.Join(", ", names.Select(n => $"'{n.Text}'")); throw new InvalidOperationException($"Multiple names {detail} on the same track."); } var name = names.FirstOrDefault()?.Text ?? ""; int count; nameCounts.TryGetValue(name, out count); nameCounts[name] = count + 1; } /* For all of the names that appear on more than one track * find all the other tracks that have this name and consolidate them. * We iterate multiple times because the track numbers will change every * time tracks are consolidated. */ foreach (var kvp in nameCounts.Where(kvp => kvp.Value > 1)) { var name = kvp.Key; var list = new List <MidiEvent>(); // iterate in reverse so track numbers don't change mid iteration for (var t = midi.Tracks - 1; t >= 0; t--) { if (!midi[t].OfType <TextEvent>().Any(e => e.IsSequenceTrackName() && e.Text == name)) { continue; } var events = midi[t].Where(e => !MidiEvent.IsEndTrack(e) && !e.IsSequenceTrackName()); list.AddRange(events); midi.RemoveTrack(t); } midi.AddNamedTrack(name, list.OrderBy(e => e.AbsoluteTime)); } }
public void ProcessTimeSignatures(MidiEventCollection midi) { // This is way easier if these have already been consolidated ConsolidateTimeTracks(midi); var timeSigTrackNo = midi.FindTrackNumberByName(TrackName.InputTimeSig.ToString()); if (timeSigTrackNo == -1) { AddInfo($"No '{TrackName.InputTimeSig}' track"); return; } var timeEvents = midi[midi.FindTrackNumberByName(TrackName.TempoMap.ToString())]; var inputTimeSignatureEvents = midi[timeSigTrackNo].OfType <NoteOnEvent>(); var groups = inputTimeSignatureEvents.GroupBy(e => e.AbsoluteTime); var error = false; foreach (var pair in groups) { var time = pair.Key; // The higher velocity value is the numerator (top) // And the lower velocity value is the denominator (bottom) var sorted = pair.OrderByDescending(e => e.Velocity).ToArray(); if (sorted.Length != 2) { error = true; var detail = string.Join(", ", sorted.Select(e => $"<{e.NoteName} ({e.NoteNumber}), Velocity: {e.Velocity}>")); AddError($"Incorrect number of time signature notes at {GetBarInfo(midi, time)}: {detail}"); continue; } if (sorted[0].Velocity == sorted[1].Velocity) { error = true; var detail = string.Join(", ", sorted.Select(e => $"<{e.NoteName} ({e.NoteNumber}), Velocity: {e.Velocity}>")); AddError($"Multiple notes with the same velocity at {GetBarInfo(midi, time)}: {detail}"); continue; } var numerator = sorted[0].NoteNumber; int denominator; if (!TryConvertToDenominator(sorted[1].NoteNumber, out denominator)) { error = true; AddError($"Invalid denominator note '{sorted[1].NoteNumber}' at {time}"); continue; } var timeSigEvent = new TimeSignatureEvent(time, numerator, denominator, TicksInClick, NumberOfThirtySecondNotesInQuarterNote); var existingTimeSigEvent = timeEvents.OfType <TimeSignatureEvent>().SingleOrDefault(e => e.AbsoluteTime == time); if (existingTimeSigEvent != null) { timeEvents.Remove(existingTimeSigEvent); } timeEvents.Add(timeSigEvent); } if (error) { throw new InvalidOperationException("Invalid time signature input"); } // Clean up input track midi.RemoveTrack(timeSigTrackNo); UpdateTrackEnd(timeEvents, timeEvents.OrderBy(e => e.AbsoluteTime).Last().AbsoluteTime); // TODO: If there is no TimeSignatureEvent or TempoEvent at 0, wig out }