コード例 #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
        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);
            }
        }
コード例 #3
0
        public long ConvertFrom(ILength length, long time, TempoMap tempoMap)
        {
            ThrowIfArgument.IsNull(nameof(length), length);
            ThrowIfTimeArgument.IsNegative(nameof(time), time);
            ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap);

            var metricLength = length as MetricLength;

            if (metricLength == null)
            {
                throw new ArgumentException($"Length is not an instance of the {nameof(MetricLength)}.", nameof(length));
            }

            var startTime = TimeConverter.ConvertTo <MetricTime>(time, tempoMap);
            var endTime   = new MetricTime(startTime.TotalMicroseconds + metricLength.TotalMicroseconds);

            return(TimeConverter.ConvertFrom(endTime, tempoMap) - time);
        }
コード例 #4
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)));
        }
コード例 #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
        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));
        }
コード例 #7
0
ファイル: TempoMapManager.cs プロジェクト: mbyht/drywetmidi
        /// <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"><paramref name="timeDivision"/> is null. -or-
        /// <paramref name="eventsCollections"/> is null.</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 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);
        }
コード例 #9
0
ファイル: Pattern.cs プロジェクト: dodohee/drywetmidi
        /// <summary>
        /// Exports the current <see cref="Pattern"/> to track chunk.
        /// </summary>
        /// <param name="tempoMap">Tempo map to process pattern data according with.</param>
        /// <param name="channel">Channel of notes that will be generated by pattern.</param>
        /// <returns>The <see cref="TrackChunk"/> containing notes events generated by the current <see cref="Pattern"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="tempoMap"/> is null.</exception>
        public TrackChunk ToTrackChunk(TempoMap tempoMap, FourBitNumber channel)
        {
            ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap);

            var context = new PatternContext(tempoMap, channel);
            var result  = InvokeActions(0, context);

            //

            var trackChunk = new TrackChunk();

            using (var notesManager = trackChunk.ManageNotes())
            {
                notesManager.Notes.Add(result.Notes ?? Enumerable.Empty <Note>());
            }

            //

            return(trackChunk);
        }
コード例 #10
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);
        }
コード例 #11
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);

            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);
                }
            }
        }
コード例 #12
0
        public long ConvertFrom(ITime time, TempoMap tempoMap)
        {
            ThrowIfArgument.IsNull(nameof(time), time);
            ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap);

            var metricTime = time as MetricTime;

            if (metricTime == null)
            {
                throw new ArgumentException($"Time is not an instance of the {nameof(MetricTime)}.", nameof(time));
            }

            var ticksPerQuarterNoteTimeDivision = tempoMap.TimeDivision as TicksPerQuarterNoteTimeDivision;

            if (ticksPerQuarterNoteTimeDivision != null)
            {
                return(ConvertFromByTicksPerQuarterNote(metricTime, ticksPerQuarterNoteTimeDivision.TicksPerQuarterNote, tempoMap));
            }

            ThrowIfTimeDivision.IsNotSupportedForTimeConversion(tempoMap.TimeDivision);
            return(0);
        }
コード例 #13
0
        public long ConvertFrom(ILength length, long time, TempoMap tempoMap)
        {
            ThrowIfArgument.IsNull(nameof(length), length);
            ThrowIfTimeArgument.IsNegative(nameof(time), time);
            ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap);

            var musicalLength = length as MusicalLength;

            if (musicalLength == null)
            {
                throw new ArgumentException($"Length is not an instance of the {nameof(MusicalLength)}.", nameof(length));
            }

            var ticksPerQuarterNoteTimeDivision = tempoMap.TimeDivision as TicksPerQuarterNoteTimeDivision;

            if (ticksPerQuarterNoteTimeDivision != null)
            {
                return(musicalLength.Fraction.ToTicks(ticksPerQuarterNoteTimeDivision.TicksPerQuarterNote));
            }

            ThrowIfTimeDivision.IsNotSupportedForLengthConversion(tempoMap.TimeDivision);
            return(0);
        }
コード例 #14
0
        /// <summary>
        ///     Replaces tempo map contained in the specified collection of the <see cref="TrackChunk" /> with
        ///     another one.
        /// </summary>
        /// <param name="trackChunks">Collection of the <see cref="TrackChunk" /> holding a tempo map to replace.</param>
        /// <param name="tempoMap">Tempo map to replace the one contained in the <paramref name="trackChunks" />.</param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="trackChunks" /> is null. -or-
        ///     <paramref name="tempoMap" /> is null.
        /// </exception>
        /// <exception cref="ArgumentException"><paramref name="trackChunks" /> is empty.</exception>
        public static void ReplaceTempoMap(this IEnumerable <TrackChunk> trackChunks, TempoMap tempoMap)
        {
            ThrowIfArgument.IsNull(nameof(trackChunks), trackChunks);
            ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap);
            ThrowIfArgument.IsEmptyCollection(nameof(trackChunks),
                                              trackChunks,
                                              $"Collection of {nameof(TrackChunk)} is empty.");

            trackChunks.Select(c => c.Events).ReplaceTempoMap(tempoMap);
        }
コード例 #15
0
        /// <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"><paramref name="eventsCollection"/> is null. -or-
        /// <paramref name="midiEvent"/> is null. -or- <paramref name="time"/> is null. -or-
        /// <paramref name="tempoMap"/> is null.</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));
        }
コード例 #16
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));
            }
        }
コード例 #17
0
        /// <summary>
        ///     Replaces tempo map contained in the specified collection of the <see cref="EventsCollection" /> with
        ///     another one.
        /// </summary>
        /// <param name="eventsCollections">Collection of the <see cref="EventsCollection" /> holding a tempo map to replace.</param>
        /// <param name="tempoMap">Tempo map to replace the one contained in the <paramref name="eventsCollections" />.</param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="eventsCollections" /> is null. -or-
        ///     <paramref name="tempoMap" /> is null.
        /// </exception>
        /// <exception cref="ArgumentException"><paramref name="eventsCollections" /> is empty.</exception>
        public static void ReplaceTempoMap(this IEnumerable <EventsCollection> eventsCollections, TempoMap tempoMap)
        {
            ThrowIfArgument.IsNull(nameof(eventsCollections), eventsCollections);
            ThrowIfArgument.IsNull(nameof(tempoMap), tempoMap);
            ThrowIfArgument.IsEmptyCollection(nameof(eventsCollections),
                                              eventsCollections,
                                              $"Collection of {nameof(EventsCollection)} is empty.");

            using (var tempoMapManager = eventsCollections.ManageTempoMap(tempoMap.TimeDivision))
            {
                tempoMapManager.ReplaceTempoMap(tempoMap);
            }
        }
コード例 #18
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));
            }
        }
コード例 #19
0
 public long ConvertFrom(ILength length, ITime time, TempoMap tempoMap)
 {
     return(ConvertFrom(length, TimeConverter.ConvertFrom(time, tempoMap), tempoMap));
 }
コード例 #20
0
 public static TTimeSpan ConvertTo <TTimeSpan>(long timeSpan, long time, TempoMap tempoMap)
     where TTimeSpan : ITimeSpan
 {
     return((TTimeSpan)GetConverter <TTimeSpan>().ConvertTo(timeSpan, time, tempoMap));
 }
コード例 #21
0
 public ILength ConvertTo(long length, ITime time, TempoMap tempoMap)
 {
     return(ConvertTo(length, TimeConverter.ConvertFrom(time, tempoMap), tempoMap));
 }
コード例 #22
0
 /// <summary>
 /// Exports the current <see cref="Pattern"/> to track chunk using zero channel.
 /// </summary>
 /// <param name="tempoMap">Tempo map to process pattern data according with.</param>
 /// <returns>The <see cref="TrackChunk"/> containing notes events generated by the current <see cref="Pattern"/>.</returns>
 /// <exception cref="ArgumentNullException"><paramref name="tempoMap"/> is null.</exception>
 public TrackChunk ToTrackChunk(TempoMap tempoMap)
 {
     return(ToTrackChunk(tempoMap, FourBitNumber.MinValue));
 }
コード例 #23
0
        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));
        }
コード例 #24
0
 /// <summary>
 /// Exports the current <see cref="Pattern"/> to MIDI file using zero channel.
 /// </summary>
 /// <param name="tempoMap">Tempo map to process pattern data according with.</param>
 /// <returns>The <see cref="MidiFile"/> containing notes events generated by the current <see cref="Pattern"/>.</returns>
 /// <exception cref="ArgumentNullException"><paramref name="tempoMap"/> is null.</exception>
 public MidiFile ToFile(TempoMap tempoMap)
 {
     return(ToFile(tempoMap, FourBitNumber.MinValue));
 }
コード例 #25
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));
            }

            var ticksPerQuarterNote = ticksPerQuarterNoteTimeDivision.TicksPerQuarterNote;
            var endTime             = time + timeSpan;

            //

            var timeSignatureLine    = tempoMap.TimeSignature;
            var timeSignatureChanges = timeSignatureLine
                                       .Values
                                       .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 = 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, ticksBefore;

            CalculateComponents(firstTime - time,
                                firstTimeSignature,
                                ticksPerQuarterNote,
                                out barsBefore,
                                out beatsBefore,
                                out ticksBefore);

            long barsAfter, beatsAfter, ticksAfter;

            CalculateComponents(time + timeSpan - lastTime,
                                lastTimeSignature,
                                ticksPerQuarterNote,
                                out barsAfter,
                                out beatsAfter,
                                out ticksAfter);

            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 ticks = ticksBefore + ticksAfter;

            if (ticks > 0)
            {
                var beatLength = GetBeatLength(firstTimeSignature, ticksPerQuarterNote);
                if (ticksBefore > 0 && ticks >= beatLength)
                {
                    beats++;
                    ticks -= beatLength;
                }
            }

            //

            return(new BarBeatTimeSpan((int)bars, (int)beats, (int)ticks));
        }
コード例 #26
0
        public static ITimeSpan ConvertTo(ITimeSpan timeSpan, Type timeSpanType, long time, TempoMap tempoMap)
        {
            if (timeSpan.GetType() == timeSpanType)
            {
                return(timeSpan.Clone());
            }

            return(GetConverter(timeSpanType).ConvertTo(ConvertFrom(timeSpan, time, tempoMap), time, tempoMap));
        }
コード例 #27
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, ITime 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));
        }
コード例 #28
0
 public static long ConvertFrom(ITimeSpan timeSpan, long time, TempoMap tempoMap)
 {
     return(GetConverter(timeSpan.GetType()).ConvertFrom(timeSpan, time, tempoMap));
 }
コード例 #29
0
 /// <summary>
 /// Filters collection of <see cref="ILengthedObject"/> to return objects that start 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">Start 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 start 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> StartAtTime <TObject>(this IEnumerable <TObject> objects, ITime time, TempoMap tempoMap)
     where TObject : ILengthedObject
 {
     return(AtTime(objects, time, tempoMap, LengthedObjectPart.Start));
 }
コード例 #30
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 barBeatTimeSpan = (BarBeatTimeSpan)timeSpan;

            var ticksPerQuarterNote = ticksPerQuarterNoteTimeDivision.TicksPerQuarterNote;
            var timeSignatureLine   = tempoMap.TimeSignature;

            //

            long bars  = barBeatTimeSpan.Bars;
            long beats = barBeatTimeSpan.Beats;
            long ticks = barBeatTimeSpan.Ticks;

            var startTimeSignature = timeSignatureLine.AtTime(time);
            var startBarLength     = GetBarLength(startTimeSignature, ticksPerQuarterNote);
            var startBeatLength    = GetBeatLength(startTimeSignature, ticksPerQuarterNote);

            var totalTicks           = bars * startBarLength + beats * startBeatLength + ticks;
            var timeSignatureChanges = timeSignatureLine.Values
                                       .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, ticksBefore;

            CalculateComponents(lastTime - time,
                                startTimeSignature,
                                ticksPerQuarterNote,
                                out barsBefore,
                                out beatsBefore,
                                out ticksBefore);

            bars -= barsBefore;

            // Balance bars

            foreach (var timeSignatureChange in timeSignatureLine.Values.Where(v => v.Time > lastTime).ToList())
            {
                var deltaTime = timeSignatureChange.Time - lastTime;

                lastBarLength  = GetBarLength(lastTimeSignature, ticksPerQuarterNote);
                lastBeatLength = 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  = GetBarLength(lastTimeSignature, ticksPerQuarterNote);
                lastBeatLength = GetBeatLength(lastTimeSignature, ticksPerQuarterNote);
                lastTime      += bars * lastBarLength;
            }

            if (beats == beatsBefore && ticks == ticksBefore)
            {
                return(lastTime - time);
            }

            // Balance beats

            if (beatsBefore > beats && lastBarLength > 0)
            {
                lastTime   += -lastBarLength + (startTimeSignature.Numerator - beatsBefore) * lastBeatLength;
                beatsBefore = 0;
            }

            if (beatsBefore < beats)
            {
                lastBeatLength = GetBeatLength(timeSignatureLine.AtTime(lastTime), ticksPerQuarterNote);
                lastTime      += (beats - beatsBefore) * lastBeatLength;
            }

            // Balance ticks

            if (ticksBefore > ticks && lastBeatLength > 0)
            {
                lastTime   += -lastBeatLength + startBeatLength - ticksBefore;
                ticksBefore = 0;
            }

            if (ticksBefore < ticks)
            {
                lastTime += ticks - ticksBefore;
            }

            //

            return(lastTime - time);
        }