/// <summary> /// Shifts events forward inside <see cref="TrackChunk"/> by the specified distance. /// </summary> /// <param name="trackChunk"><see cref="TrackChunk"/> containing events to shift.</param> /// <param name="distance">Distance to shift events by.</param> /// <param name="tempoMap">Tempo map used for internal distance conversions.</param> /// <exception cref="ArgumentNullException"><paramref name="trackChunk"/> is null. -or- /// <paramref name="distance"/> is null. -or- <paramref name="tempoMap"/> is null.</exception> public static void ShiftEvents(this TrackChunk trackChunk, ITimeSpan distance, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk); ThrowIfArgument.IsNull(nameof(distance), distance); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); var convertedDistance = TimeConverter.ConvertFrom(distance, TempoMap.Create(tempoMap.TimeDivision)); var firstEvent = trackChunk.Events.FirstOrDefault(); if (firstEvent == null) { return; } firstEvent.DeltaTime += convertedDistance; }
public ITimeSpan ConvertTo(long timeSpan, long time, TempoMap tempoMap) { var ticksPerQuarterNoteTimeDivision = tempoMap.TimeDivision as TicksPerQuarterNoteTimeDivision; if (ticksPerQuarterNoteTimeDivision == null) { throw new ArgumentException("Time division is not supported for time span conversion.", nameof(tempoMap)); } if (timeSpan == 0) { return(new MusicalTimeSpan()); } var xy = MathUtilities.SolveDiophantineEquation(4 * ticksPerQuarterNoteTimeDivision.TicksPerQuarterNote, -timeSpan); return(new MusicalTimeSpan(Math.Abs(xy.Item1), Math.Abs(xy.Item2))); }
private static long MetricTimeSpanToTicks(MetricTimeSpan timeSpan, TempoMap tempoMap) { var timeMicroseconds = timeSpan.TotalMicroseconds; if (timeMicroseconds == 0) { return(0); } var valuesCache = tempoMap.GetValuesCache <MetricTempoMapValuesCache>(); var accumulatedMicroseconds = valuesCache.Microseconds.TakeWhile(m => m.Microseconds < timeMicroseconds).LastOrDefault(); var lastAccumulatedMicroseconds = accumulatedMicroseconds?.Microseconds ?? 0; var lastTime = accumulatedMicroseconds?.Time ?? 0; var lastTicksPerMicrosecond = accumulatedMicroseconds?.TicksPerMicrosecond ?? valuesCache.DefaultTicksPerMicrosecond; return(RoundMicroseconds(lastTime + (timeMicroseconds - lastAccumulatedMicroseconds) * lastTicksPerMicrosecond)); }
private static void ResizeNotesByRatio(IEnumerable <Note> notes, double ratio, TimeSpanType distanceCalculationType, TempoMap tempoMap, ITimeSpan startTime) { foreach (var note in notes) { var noteLength = note.LengthAs(distanceCalculationType, tempoMap); var noteTime = note.TimeAs(distanceCalculationType, tempoMap); var scaledShiftFromStart = noteTime.Subtract(startTime, TimeSpanMode.TimeTime).Multiply(ratio); note.Time = TimeConverter.ConvertFrom(startTime.Add(scaledShiftFromStart, TimeSpanMode.TimeLength), tempoMap); var scaledLength = noteLength.Multiply(ratio); note.Length = LengthConverter.ConvertFrom(scaledLength, note.Time, tempoMap); } }
public long ConvertFrom(ITimeSpan timeSpan, long time, TempoMap tempoMap) { var ticksPerQuarterNoteTimeDivision = tempoMap.TimeDivision as TicksPerQuarterNoteTimeDivision; if (ticksPerQuarterNoteTimeDivision == null) { throw new ArgumentException("Time division is not supported for time span conversion.", nameof(tempoMap)); } var musicalTimeSpan = (MusicalTimeSpan)timeSpan; if (musicalTimeSpan.Numerator == 0) { return(0); } return(MathUtilities.RoundToLong(4.0 * musicalTimeSpan.Numerator * ticksPerQuarterNoteTimeDivision.TicksPerQuarterNote / musicalTimeSpan.Denominator)); }
public ITimeSpan ConvertTo(long timeSpan, long time, TempoMap tempoMap) { var ticksPerQuarterNoteTimeDivision = tempoMap.TimeDivision as TicksPerQuarterNoteTimeDivision; if (ticksPerQuarterNoteTimeDivision == null) { throw new ArgumentException("Time division is not supported for time span conversion.", nameof(tempoMap)); } if (timeSpan == 0) { return(new MetricTimeSpan()); } var startTimeSpan = TicksToMetricTimeSpan(time, tempoMap); var endTimeSpan = TicksToMetricTimeSpan(time + timeSpan, tempoMap); return(endTimeSpan - startTimeSpan); }
/// <summary> /// Initializes a new instance of the <see cref="TempoMapManager"/> with the specified time division /// and events collections. /// </summary> /// <param name="timeDivision">MIDI file time division which specifies the meaning of the time /// used by events of the file.</param> /// <param name="eventsCollections">Collection of <see cref="EventsCollection"/> which hold events that /// represent tempo map of a MIDI file.</param> /// <exception cref="ArgumentNullException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="timeDivision"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="eventsCollections"/> is <c>null</c>.</description> /// </item> /// </list> /// </exception> /// <exception cref="ArgumentException"><paramref name="eventsCollections"/> is empty.</exception> public TempoMapManager(TimeDivision timeDivision, IEnumerable <EventsCollection> eventsCollections) { ThrowIfArgument.IsNull(nameof(timeDivision), timeDivision); ThrowIfArgument.IsNull(nameof(eventsCollections), eventsCollections); ThrowIfArgument.IsEmptyCollection(nameof(eventsCollections), eventsCollections, $"Collection of {nameof(EventsCollection)} is empty."); _timedEventsManagers = eventsCollections.Where(events => events != null) .Select(events => events.ManageTimedEvents()) .ToList(); // TempoMap = new TempoMap(timeDivision); CollectTimeSignatureChanges(); CollectTempoChanges(); }
public long ConvertFrom(ITimeSpan timeSpan, long time, TempoMap tempoMap) { var ticksPerQuarterNoteTimeDivision = tempoMap.TimeDivision as TicksPerQuarterNoteTimeDivision; if (ticksPerQuarterNoteTimeDivision == null) { throw new ArgumentException("Time division is not supported for time span conversion.", nameof(tempoMap)); } var metricTimeSpan = (MetricTimeSpan)timeSpan; if ((TimeSpan)metricTimeSpan == TimeSpan.Zero) { return(0); } var startTimeSpan = TicksToMetricTimeSpan(time, tempoMap); var endTimeSpan = startTimeSpan + metricTimeSpan; return(MetricTimeSpanToTicks(endTimeSpan, tempoMap) - time); }
/// <summary> /// Gets tempo map represented by the specified time division and events collections of /// the specified track chunks. /// </summary> /// <param name="trackChunks">Collection of <see cref="TrackChunk"/> which hold events /// that represent tempo map of a MIDI file.</param> /// <param name="timeDivision">MIDI file time division which specifies the meaning of the time /// used by events of the file.</param> /// <returns>Tempo map represented by the <paramref name="trackChunks"/> and <paramref name="timeDivision"/>.</returns> /// <exception cref="ArgumentNullException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="trackChunks"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="timeDivision"/> is <c>null</c>.</description> /// </item> /// </list> /// </exception> public static TempoMap GetTempoMap(this IEnumerable <TrackChunk> trackChunks, TimeDivision timeDivision) { ThrowIfArgument.IsNull(nameof(trackChunks), trackChunks); ThrowIfArgument.IsNull(nameof(timeDivision), timeDivision); var eventsCollections = trackChunks.Where(c => c != null).Select(c => c.Events).ToArray(); var eventsCount = eventsCollections.Sum(c => c.Count); var result = new TempoMap(timeDivision); foreach (var timedEventTuple in eventsCollections.GetTimedEventsLazy(eventsCount)) { var timedEvent = timedEventTuple.Item1; var midiEvent = timedEvent.Event; switch (midiEvent.EventType) { case MidiEventType.TimeSignature: { var timeSignatureEvent = (TimeSignatureEvent)midiEvent; result.TimeSignatureLine.SetValue( timedEvent.Time, new TimeSignature( timeSignatureEvent.Numerator, timeSignatureEvent.Denominator)); } break; case MidiEventType.SetTempo: { var setTempoEvent = (SetTempoEvent)midiEvent; result.TempoLine.SetValue( timedEvent.Time, new Tempo(setTempoEvent.MicrosecondsPerQuarterNote)); } break; } } return(result); }
/// <summary> /// Gets points in time of the current grid. /// </summary> /// <param name="tempoMap">Tempo map used to get grid's times.</param> /// <returns>Collection of points in time of the current grid.</returns> /// <exception cref="ArgumentNullException"><paramref name="tempoMap"/> is <c>null</c>.</exception> public IEnumerable <long> GetTimes(TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); if (!Steps.Any()) { yield break; } var time = TimeConverter.ConvertFrom(Start, tempoMap); yield return(time); while (true) { foreach (var step in Steps) { time += LengthConverter.ConvertFrom(step, time, tempoMap); yield return(time); } } }
private static Tuple <TimeSignature, short> GetTimeSignatureAndTicksPerQuarterNote(long bars, TempoMap tempoMap) { var ticksPerQuarterNoteTimeDivision = tempoMap.TimeDivision as TicksPerQuarterNoteTimeDivision; if (ticksPerQuarterNoteTimeDivision == null) { throw new ArgumentException("Time division of the tempo map is not supported.", nameof(tempoMap)); } var ticks = TimeConverter.ConvertFrom(new BarBeatTicksTimeSpan(bars), tempoMap); var timeSignature = tempoMap.TimeSignature.AtTime(ticks); var ticksPerQuarterNote = ticksPerQuarterNoteTimeDivision.TicksPerQuarterNote; return(Tuple.Create(timeSignature, ticksPerQuarterNote)); }
private static long ConvertFromTimeLength(MathTimeSpan mathTimeSpan, long time, TempoMap tempoMap) { var convertedTimeSpan1 = TimeConverter.ConvertFrom(mathTimeSpan.TimeSpan1, tempoMap); switch (mathTimeSpan.Operation) { case MathOperation.Add: return(convertedTimeSpan1 + LengthConverter.ConvertFrom(mathTimeSpan.TimeSpan2, convertedTimeSpan1, tempoMap)); case MathOperation.Subtract: return(convertedTimeSpan1 - LengthConverter.ConvertFrom(mathTimeSpan.TimeSpan2, convertedTimeSpan1, tempoMap.Flip(convertedTimeSpan1))); default: throw new ArgumentException($"{mathTimeSpan.Operation} is not supported by the converter.", nameof(mathTimeSpan)); } }
private static long ConvertFromTimeTime(MathTimeSpan mathTimeSpan, long time, TempoMap tempoMap) { var timeSpan1 = mathTimeSpan.TimeSpan1; var timeSpan2 = mathTimeSpan.TimeSpan2; // Ensure that the first time span is not an instance of the MathTimeSpan. If it is, // convert it to the type of its first time span. For example, if we the following // time span was passed to the method: // // (a1 + b1) + c // // the time span will be transformed to: // // a2 + c var timeSpan1AsMath = mathTimeSpan.TimeSpan1 as MathTimeSpan; if (timeSpan1AsMath != null) { timeSpan1 = TimeSpanConverter.ConvertTo(timeSpan1AsMath, timeSpan1AsMath.TimeSpan1.GetType(), time, tempoMap); } // To subtract one time from another one we need to convert the second time span // to the type of the first one. After that result of subtraction will be of the // first time span type. And finally we convert result time span according to the // specified time. For example, time span shown above will be transformed first to // // a3 // // and then will be converted to MIDI time. switch (mathTimeSpan.Operation) { case MathOperation.Subtract: { var convertedTimeSpan2 = TimeConverter.ConvertTo(timeSpan2, timeSpan1.GetType(), tempoMap); return(TimeSpanConverter.ConvertFrom(timeSpan1.Subtract(convertedTimeSpan2, TimeSpanMode.TimeTime), time, tempoMap)); } default: throw new ArgumentException($"{mathTimeSpan.Operation} is not supported by the converter.", nameof(mathTimeSpan)); } }
/// <summary> /// Initializes a new instance of the <see cref="TempoMapManager"/> with the /// specified time division. /// </summary> /// <param name="timeDivision">Time division of a new tempo that will be managed by this manager.</param> /// <exception cref="ArgumentNullException"><paramref name="timeDivision"/> is <c>null</c>.</exception> public TempoMapManager(TimeDivision timeDivision) { ThrowIfArgument.IsNull(nameof(timeDivision), timeDivision); TempoMap = new TempoMap(timeDivision); }
public ITimeSpan ConvertTo(long timeSpan, long time, TempoMap tempoMap) { throw new NotSupportedException($"Conversion to the {nameof(MathTimeSpan)} is not supported."); }
private static void SetGlobalTimeSignature(TempoMap tempoMap, TimeSignature timeSignature) { tempoMap.TimeSignature.SetValue(0, timeSignature); }
/// <summary> /// Adds a <see cref="MidiEvent"/> into a <see cref="TimedEventsCollection"/> with the specified /// absolute time. /// </summary> /// <param name="eventsCollection"><see cref="TimedEventsCollection"/> to add an event into.</param> /// <param name="midiEvent">Event to add into the <paramref name="eventsCollection"/>.</param> /// <param name="time">Absolute time that will be assigned to the <paramref name="midiEvent"/> /// when it will be placed into the <paramref name="eventsCollection"/>.</param> /// <param name="tempoMap">Tempo map used to place <paramref name="midiEvent"/> into the /// <paramref name="eventsCollection"/> with the specified time.</param> /// <exception cref="ArgumentNullException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="eventsCollection"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="midiEvent"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="time"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="tempoMap"/> is <c>null</c>.</description> /// </item> /// </list> /// </exception> public static void AddEvent(this TimedEventsCollection eventsCollection, MidiEvent midiEvent, ITimeSpan time, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(eventsCollection), eventsCollection); ThrowIfArgument.IsNull(nameof(midiEvent), midiEvent); ThrowIfArgument.IsNull(nameof(time), time); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); eventsCollection.AddEvent(midiEvent, TimeConverter.ConvertFrom(time, tempoMap)); }
public long ConvertFrom(ITimeSpan timeSpan, long time, TempoMap tempoMap) { var ticksPerQuarterNoteTimeDivision = tempoMap.TimeDivision as TicksPerQuarterNoteTimeDivision; if (ticksPerQuarterNoteTimeDivision == null) { throw new ArgumentException("Time division is not supported for time span conversion.", nameof(tempoMap)); } var barBeatFractionTimeSpan = (BarBeatFractionTimeSpan)timeSpan; // TODO: epsilon if (barBeatFractionTimeSpan.Bars == 0 && barBeatFractionTimeSpan.Beats == 0) { return(0); } var ticksPerQuarterNote = ticksPerQuarterNoteTimeDivision.TicksPerQuarterNote; var timeSignatureLine = tempoMap.TimeSignature; // double fractionalBeats = barBeatFractionTimeSpan.Beats; long bars = barBeatFractionTimeSpan.Bars; long beats = (long)Math.Truncate(fractionalBeats); double fraction = fractionalBeats - Math.Truncate(fractionalBeats); var startTimeSignature = timeSignatureLine.AtTime(time); var startBarLength = BarBeatUtilities.GetBarLength(startTimeSignature, ticksPerQuarterNote); var startBeatLength = BarBeatUtilities.GetBeatLength(startTimeSignature, ticksPerQuarterNote); var totalTicks = bars * startBarLength + beats * startBeatLength + ConvertFractionToTicks(fraction, startBeatLength); var timeSignatureChanges = timeSignatureLine.Where(v => v.Time > time && v.Time < time + totalTicks).ToList(); var lastBarLength = 0L; var lastBeatLength = 0L; var firstTimeSignatureChange = timeSignatureChanges.FirstOrDefault(); var lastTimeSignature = firstTimeSignatureChange?.Value ?? startTimeSignature; var lastTime = firstTimeSignatureChange?.Time ?? time; long barsBefore, beatsBefore; double fractionBefore; CalculateComponents(lastTime - time, startTimeSignature, ticksPerQuarterNote, out barsBefore, out beatsBefore, out fractionBefore); bars -= barsBefore; // Balance bars if (bars > 0) { foreach (var timeSignatureChange in timeSignatureLine.Where(v => v.Time > lastTime).ToList()) { var deltaTime = timeSignatureChange.Time - lastTime; lastBarLength = BarBeatUtilities.GetBarLength(lastTimeSignature, ticksPerQuarterNote); lastBeatLength = BarBeatUtilities.GetBeatLength(lastTimeSignature, ticksPerQuarterNote); var currentBars = Math.Min(deltaTime / lastBarLength, bars); bars -= currentBars; lastTime += currentBars * lastBarLength; if (bars == 0) { break; } lastTimeSignature = timeSignatureChange.Value; } if (bars > 0) { lastBarLength = BarBeatUtilities.GetBarLength(lastTimeSignature, ticksPerQuarterNote); lastBeatLength = BarBeatUtilities.GetBeatLength(lastTimeSignature, ticksPerQuarterNote); lastTime += bars * lastBarLength; } } if (beats == beatsBefore && fraction == fractionBefore) { return(lastTime - time); } // Balance beats if (beatsBefore > beats && lastBarLength > 0) { lastTime += -lastBarLength + (startTimeSignature.Numerator - beatsBefore) * lastBeatLength; beatsBefore = 0; } if (beatsBefore < beats) { lastBeatLength = BarBeatUtilities.GetBeatLength(timeSignatureLine.AtTime(lastTime), ticksPerQuarterNote); lastTime += (beats - beatsBefore) * lastBeatLength; } // Balance cents if (fractionBefore > fraction && lastBeatLength > 0) { lastTime += -lastBeatLength + ConvertFractionToTicks(fraction + 1.0 - fractionBefore, lastBeatLength); } if (fractionBefore < fraction) { if (lastBeatLength == 0) { lastBeatLength = BarBeatUtilities.GetBeatLength(timeSignatureLine.AtTime(lastTime), ticksPerQuarterNote); } lastTime += ConvertFractionToTicks(fraction - fractionBefore, lastBeatLength); } // return(lastTime - time); }
/// <summary> /// Gets points in time of the current grid. /// </summary> /// <param name="tempoMap">Tempo map used to get grid's times.</param> /// <returns>Collection of points in time of the current grid.</returns> /// <exception cref="ArgumentNullException"><paramref name="tempoMap"/> is null.</exception> public IEnumerable <long> GetTimes(TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); return(Times.Select(t => TimeConverter.ConvertFrom(t, tempoMap))); }
/// <summary> /// Filters collection of <see cref="ITimedObject"/> to return objects at the specified time. /// </summary> /// <typeparam name="TObject">The type of the elements of <paramref name="objects"/>.</typeparam> /// <param name="objects">A collection to filter.</param> /// <param name="time">Time to filter objects by.</param> /// <param name="tempoMap">Tempo map to filter <paramref name="objects"/> by <paramref name="time"/>.</param> /// <returns>A collection that contains objects from the input sequence that are at the specified time.</returns> /// <remarks> /// Note that changes made on the objects returned by this method will not be saved to an underlying /// data source (events collection, track chunk, file). To change properties of timed objects and /// save them you need to use a manager appropriate for an object's type. /// </remarks> /// <exception cref="ArgumentNullException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="objects"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="time"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="tempoMap"/> is <c>null</c>.</description> /// </item> /// </list> /// </exception> public static IEnumerable <TObject> AtTime <TObject>(this IEnumerable <TObject> objects, ITimeSpan time, TempoMap tempoMap) where TObject : ITimedObject { ThrowIfArgument.IsNull(nameof(objects), objects); ThrowIfArgument.IsNull(nameof(time), time); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); var convertedTime = TimeConverter.ConvertFrom(time, tempoMap); return(AtTime(objects, convertedTime)); }
/// <summary> /// Initializes handler. This method will be called before reading MIDI data. /// </summary> public override void Initialize() { _tempoMapIsReadyForUsage = false; _tempoMap = null; }
/// <summary> /// Sets time and length of the specified chord. /// </summary> /// <param name="chord">Chord to set time and length to.</param> /// <param name="time">Time to set to <paramref name="chord"/>.</param> /// <param name="length">Length to set to <paramref name="chord"/>.</param> /// <param name="tempoMap">Tempo map that will be used for time and length conversion.</param> /// <returns>An input <paramref name="chord"/> with new time and length.</returns> /// <exception cref="ArgumentNullException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="chord"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="time"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="length"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="tempoMap"/> is <c>null</c>.</description> /// </item> /// </list> /// </exception> public static Chord SetTimeAndLength(this Chord chord, ITimeSpan time, ITimeSpan length, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(chord), chord); ThrowIfArgument.IsNull(nameof(time), time); ThrowIfArgument.IsNull(nameof(length), length); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); chord.Time = TimeConverter.ConvertFrom(time, tempoMap); chord.Length = LengthConverter.ConvertFrom(length, chord.Time, tempoMap); return(chord); }
/// <summary> /// Sets time and length of the specified note. /// </summary> /// <param name="note">Note to set time and length to.</param> /// <param name="time">Time to set to <paramref name="note"/>.</param> /// <param name="length">Length to set to <paramref name="note"/>.</param> /// <param name="tempoMap">Tempo map that will be used for time and length conversion.</param> /// <returns>An input <paramref name="note"/> with new time and length.</returns> /// <exception cref="ArgumentNullException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="note"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="time"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="length"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="tempoMap"/> is <c>null</c>.</description> /// </item> /// </list> /// </exception> public static Note SetTimeAndLength(this Note note, ITimeSpan time, ITimeSpan length, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(note), note); ThrowIfArgument.IsNull(nameof(time), time); ThrowIfArgument.IsNull(nameof(length), length); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); note.Time = TimeConverter.ConvertFrom(time, tempoMap); note.Length = LengthConverter.ConvertFrom(length, note.Time, tempoMap); return(note); }
/// <summary> /// Filters collection of <see cref="ILengthedObject"/> to return objects that end at the specified time. /// </summary> /// <typeparam name="TObject">The type of the elements of <paramref name="objects"/>.</typeparam> /// <param name="objects">A collection to filter.</param> /// <param name="time">End time to filter objects by.</param> /// <param name="tempoMap">Tempo map to filter <paramref name="objects"/> by <paramref name="time"/>.</param> /// <returns>A collection that contains objects from the input sequence that end at the specified time.</returns> /// <remarks> /// Note that changes made on the objects returned by this method will not be saved to an underlying /// data source (events collection, track chunk, file). To change properties of lengthed objects and /// save them you need to use a manager appropriate for an object's type. /// </remarks> /// <exception cref="ArgumentNullException"><paramref name="objects"/> is null. -or- <paramref name="time"/> is null. -or- /// <paramref name="tempoMap"/> is null. -or- One of the objects is null.</exception> public static IEnumerable <TObject> EndAtTime <TObject>(this IEnumerable <TObject> objects, ITimeSpan time, TempoMap tempoMap) where TObject : ILengthedObject { return(AtTime(objects, time, tempoMap, LengthedObjectPart.End)); }
public ITimeSpan ConvertTo(long timeSpan, long time, TempoMap tempoMap) { var ticksPerQuarterNoteTimeDivision = tempoMap.TimeDivision as TicksPerQuarterNoteTimeDivision; if (ticksPerQuarterNoteTimeDivision == null) { throw new ArgumentException("Time division is not supported for time span conversion.", nameof(tempoMap)); } if (timeSpan == 0) { return(new BarBeatFractionTimeSpan()); } var ticksPerQuarterNote = ticksPerQuarterNoteTimeDivision.TicksPerQuarterNote; var endTime = time + timeSpan; // var timeSignatureLine = tempoMap.TimeSignature; var timeSignatureChanges = timeSignatureLine .Where(v => v.Time > time && v.Time < endTime) .ToList(); var bars = 0L; // Calculate count of complete bars between time signature changes for (int i = 0; i < timeSignatureChanges.Count - 1; i++) { var timeSignatureChange = timeSignatureChanges[i]; var nextTime = timeSignatureChanges[i + 1].Time; var barLength = BarBeatUtilities.GetBarLength(timeSignatureChange.Value, ticksPerQuarterNote); bars += (nextTime - timeSignatureChange.Time) / barLength; } // Calculate components before first time signature change and after last time signature change var firstTime = timeSignatureChanges.FirstOrDefault()?.Time ?? time; var lastTime = timeSignatureChanges.LastOrDefault()?.Time ?? time; var firstTimeSignature = timeSignatureLine.AtTime(time); var lastTimeSignature = timeSignatureLine.AtTime(lastTime); long barsBefore, beatsBefore; double fractionBefore; CalculateComponents(firstTime - time, firstTimeSignature, ticksPerQuarterNote, out barsBefore, out beatsBefore, out fractionBefore); long barsAfter, beatsAfter; double fractionAfter; CalculateComponents(time + timeSpan - lastTime, lastTimeSignature, ticksPerQuarterNote, out barsAfter, out beatsAfter, out fractionAfter); bars += barsBefore + barsAfter; // Try to complete a bar var beats = beatsBefore + beatsAfter; if (beats > 0) { if (beatsBefore > 0 && beats >= firstTimeSignature.Numerator) { bars++; beats -= firstTimeSignature.Numerator; } } // Try to complete a beat var fraction = fractionBefore + fractionAfter; beats += (long)Math.Truncate(fraction); fraction -= Math.Truncate(fraction); // return(new BarBeatFractionTimeSpan(bars, beats + fraction)); }
/// <summary> /// Filters collection of <see cref="ILengthedObject"/> to return objects at the specified time. /// </summary> /// <typeparam name="TObject">The type of the elements of <paramref name="objects"/>.</typeparam> /// <param name="objects">A collection to filter.</param> /// <param name="time">Time to filter objects by.</param> /// <param name="tempoMap">Tempo map to filter <paramref name="objects"/> by <paramref name="time"/>.</param> /// <param name="matchBy">Part of an object which have to be at <paramref name="time"/>.</param> /// <returns>A collection that contains objects from the input sequence that are at the specified time.</returns> /// <remarks> /// Note that changes made on the objects returned by this method will not be saved to an underlying /// data source (events collection, track chunk, file). To change properties of lengthed objects and /// save them you need to use a manager appropriate for an object's type. /// </remarks> /// <exception cref="ArgumentNullException"><paramref name="objects"/> is null. -or- <paramref name="time"/> is null. -or- /// <paramref name="tempoMap"/> is null. -or- One of the objects is null.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="matchBy"/> specified an invalid value.</exception> public static IEnumerable <TObject> AtTime <TObject>(this IEnumerable <TObject> objects, ITimeSpan time, TempoMap tempoMap, LengthedObjectPart matchBy) where TObject : ILengthedObject { ThrowIfArgument.IsNull(nameof(objects), objects); ThrowIfArgument.IsNull(nameof(time), time); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); ThrowIfArgument.IsInvalidEnumValue(nameof(matchBy), matchBy); var convertedTime = TimeConverter.ConvertFrom(time, tempoMap); return(AtTime(objects, convertedTime, matchBy)); }
private static void SetGlobalTempo(TempoMap tempoMap, Tempo tempo) { tempoMap.Tempo.SetValue(0, tempo); }
/// <summary> /// Gets length of an <see cref="ILengthedObject"/> as an instance of type defined by the /// specified time span type. /// </summary> /// <param name="obj">Object to get length of.</param> /// <param name="lengthType">The type of time span to convert the length of <paramref name="obj"/> to.</param> /// <param name="tempoMap">Tempo map to calculate length of the <paramref name="obj"/>.</param> /// <returns>Time of the specified object as an instance of time span defined by the /// <paramref name="lengthType"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="obj"/> is null. -or- /// <paramref name="tempoMap"/> is null.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="lengthType"/> specified an invalid value.</exception> public static ITimeSpan LengthAs(this ILengthedObject obj, TimeSpanType lengthType, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(obj), obj); ThrowIfArgument.IsInvalidEnumValue(nameof(lengthType), lengthType); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); return(LengthConverter.ConvertTo(obj.Length, lengthType, obj.Time, tempoMap)); }
/// <summary> /// Shifts events forward inside collection of <see cref="TrackChunk"/> by the specified distance. /// </summary> /// <param name="trackChunks">Collection of <see cref="TrackChunk"/> containing events to shift.</param> /// <param name="distance">Distance to shift events by.</param> /// <param name="tempoMap">Tempo map used for internal distance conversions.</param> /// <exception cref="ArgumentNullException"> /// <para>One of the following errors occured:</para> /// <list type="bullet"> /// <item> /// <description><paramref name="trackChunks"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="distance"/> is <c>null</c>.</description> /// </item> /// <item> /// <description><paramref name="tempoMap"/> is <c>null</c>.</description> /// </item> /// </list> /// </exception> public static void ShiftEvents(this IEnumerable <TrackChunk> trackChunks, ITimeSpan distance, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(trackChunks), trackChunks); ThrowIfArgument.IsNull(nameof(distance), distance); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); foreach (var trackChunk in trackChunks) { trackChunk.ShiftEvents(distance, tempoMap); } }
/// <summary> /// Gets end time of an <see cref="ITimedObject"/> as an instance of time span defined by the /// specified time span type. /// </summary> /// <param name="obj">Object to get end time of.</param> /// <param name="timeType">The type of time span to convert the end time of <paramref name="obj"/> to.</param> /// <param name="tempoMap">Tempo map to calculate end time of the <paramref name="obj"/>.</param> /// <returns>End time of the specified object as an instance of time span defined by the /// <paramref name="timeType"/>.</returns> /// <exception cref="ArgumentNullException"><paramref name="obj"/> is null. -or- /// <paramref name="tempoMap"/> is null.</exception> /// <exception cref="InvalidEnumArgumentException"><paramref name="timeType"/> specified an invalid value.</exception> public static ITimeSpan EndTimeAs(this ILengthedObject obj, TimeSpanType timeType, TempoMap tempoMap) { ThrowIfArgument.IsNull(nameof(obj), obj); ThrowIfArgument.IsInvalidEnumValue(nameof(timeType), timeType); ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap); return(TimeConverter.ConvertTo(obj.Time + obj.Length, timeType, tempoMap)); }