public void ConstrainNotesToMeasureBoundaries() { foreach (var note in this.Notes) { //get start and end boundary points for this note MidiTime startOfBar = new MidiTime(this.File, note.AveragePosition.Measures, 1, 0, this.TimeSignature); MidiTime endOfBar = new MidiTime(this.File, note.AveragePosition.Measures + 1, 1, 0, this.TimeSignature); if (note.StartPosition.GetAsTotalTicks() < startOfBar.GetAsTotalTicks()) { //note is mostly in current bar but the note starts just before the bar start. //Move the note start to the to start of the measure note.StartPosition = new MidiTime(startOfBar); note.CalculateEndPosition(); } else if (note.EndPosition.GetAsTotalTicks() > endOfBar.GetAsTotalTicks()) { ///note is mostly in current bar but the note ends just after the bar end. ///Shorten the length of the note. ///This will only rarely happen because at this point the note lengths are 1 and so arent long enough to span two measures note.Length = endOfBar.GetAsTotalTicks() - note.StartPosition.GetAsTotalTicks(); note.CalculateEndPosition(); } } }
public MidiTime(MidiTime copyMe) { this.File = copyMe.File; this.TimeSignature = copyMe.TimeSignature; this.Measures = copyMe.Measures; this.Beats = copyMe.Beats; this.Ticks = copyMe.Ticks; this.Region = copyMe.Region; }
/// <summary> /// the position of the region boundaries are calculated on the fly by using the current time /// signature and pulses per QN /// </summary> /// <param name="position"></param> /// <param name="pulsesPerQuarterNote"></param> /// <param name="timeSig"></param> /// <returns></returns> public Region GetRegion(MidiTime position, uint pulsesPerQuarterNote, TimeSignatureEvent timeSig) { uint posTicks = position.GetAsTotalTicks(pulsesPerQuarterNote, timeSig); foreach (Region r in this.Regions) { if (posTicks >= r.Start.GetAsTotalTicks(pulsesPerQuarterNote, timeSig) && posTicks < r.End.GetAsTotalTicks(pulsesPerQuarterNote, timeSig)) { return(r); } } return(null); }
public void ConvertToMidiFilesByRegionAndBarAndTrack(String outputPath) { if (this.Header.FormatType != TrackFormatType.MetaAndChannel) { throw new ApplicationException("cannot split file"); } List <MidiFile> midiFiles = new List <MidiFile>(); foreach (TrackChunk origTrack in this.Tracks) { /// do conversion in this order. by doing these conversions we also remove any /// overlapping notes. origTrack.MakeAllNotesSameKey(48); //good for drums where we want the same note for all drum hits origTrack.MakeAllNotesSameLength(10); //smallest unit that wont overlap after cleaning duplicates //origTrack.ConstrainNotesToMeasureBoundaries(); //makes it easy to put the notes into 'measure' files //origTrack.RemoveNotesNotInMeasure(); origTrack.RemoveNotesWhereNoteStartIsNotWithinMeasure(); //origTrack.RemoveDuplicateNotes(); origTrack.RemoveDuplicateNotes(); //uint currentMeasure = 0; uint currentRegionMeasure = 0; TrackChunk newTrack = null; MidiFile newFile = null; Region currentRegion = null; MidiTime currentMeasureStart = null; Note lastNote = null; foreach (var note in origTrack.Notes) { if (note.StartPosition.Region == null) { Debug.WriteLine(origTrack.TrackName.Text + " null region @" + note.StartPosition); //Debug.WriteLine(note.ToString()); continue; } if (note.StartPosition.Region != currentRegion) { ///the first note in a new region /// currentRegion = note.StartPosition.Region; currentRegionMeasure = 0; //reset } if (currentMeasureStart == null || note.StartPosition.Measures != currentMeasureStart.Measures) { currentRegionMeasure++; ///the first note in a new measure /// newFile = new MidiFile(); String temp = origTrack.TrackName.Text.Substring(0, 2); //if (temp.StartsWith("0")) // temp = temp.Substring(1); String newTrackName = note.StartPosition.Region.ID + "-B" + currentRegionMeasure + "-T" + temp; newFile.FileNameAndPath = outputPath + Path.DirectorySeparatorChar + newTrackName + ".mid"; newFile.Header.NumberOfTracks = 1; newFile.Header.FormatType = TrackFormatType.Single; newFile.Header.PulsesPerQuarterNote = this.Header.PulsesPerQuarterNote; midiFiles.Add(newFile); newTrack = new TrackChunk(); newTrack.TrackName = new TrackNameEvent(newTrackName); newTrack.TimeSignature = new TimeSignatureEvent(origTrack.TimeSignature.Numerator, origTrack.TimeSignature.Denominator, origTrack.TimeSignature.MetronomePulse, origTrack.TimeSignature.ThirtySecondNotes); newTrack.Tempo = new TempoEvent(origTrack.Tempo.MicroSecondsPerQuarterNote, origTrack.TimeSignature.Denominator); newTrack.CopyrightNotice = new CopyrightNoticeEvent("Joe Bacon 2013"); newTrack.DeviceName = new DeviceNameEvent("MIDI-Bacon"); newFile.Tracks.Add(newTrack); currentMeasureStart = new MidiTime(this, note.StartPosition.Measures, 1, 0, origTrack.TimeSignature); lastNote = null; } uint deltaTime = 0; if (lastNote == null) { deltaTime = note.StartPosition.GetAsTotalTicks() - currentMeasureStart.GetAsTotalTicks(); //occurs on first note of measure } else { deltaTime = note.StartPosition.GetAsTotalTicks() - lastNote.EndPosition.GetAsTotalTicks(); } ChannelMidiEvent noteOnEvent = ChannelMidiEvent.CreateNoteOnChannelMidiEvent(0, note.NoteNumber, note.Velocity, deltaTime); newTrack.ChannelEvents.Add(noteOnEvent); ChannelMidiEvent noteOffEvent = ChannelMidiEvent.CreateNoteOffChannelMidiEvent(0, note.NoteNumber, note.Length); newTrack.ChannelEvents.Add(noteOffEvent); lastNote = note; } } foreach (MidiFile mf in midiFiles) { mf.Export(mf.FileNameAndPath); } }