Пример #1
0
        /// <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;
        }
Пример #2
0
        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)));
        }
Пример #3
0
        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));
        }
Пример #4
0
        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);
            }
        }
Пример #5
0
        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));
        }
Пример #6
0
        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();
        }
Пример #8
0
        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);
        }
Пример #9
0
        /// <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));
        }
Пример #12
0
        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));
            }
        }
Пример #13
0
        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);
        }
Пример #15
0
 public ITimeSpan ConvertTo(long timeSpan, long time, TempoMap tempoMap)
 {
     throw new NotSupportedException($"Conversion to the {nameof(MathTimeSpan)} is not supported.");
 }
Пример #16
0
 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);
        }
Пример #19
0
        /// <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));
        }
Пример #21
0
 /// <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);
        }
Пример #23
0
        /// <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);
        }
Пример #24
0
 /// <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));
        }
Пример #26
0
        /// <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));
        }
Пример #27
0
 private static void SetGlobalTempo(TempoMap tempoMap, Tempo tempo)
 {
     tempoMap.Tempo.SetValue(0, tempo);
 }
Пример #28
0
        /// <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));
        }
Пример #29
0
        /// <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);
            }
        }
Пример #30
0
        /// <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));
        }