Example #1
0
        private static void CalculateComponents(long totalTicks,
                                                TimeSignature timeSignature,
                                                short ticksPerQuarterNote,
                                                out long bars,
                                                out long beats,
                                                out long ticks)
        {
            var barLength = BarBeatUtilities.GetBarLength(timeSignature, ticksPerQuarterNote);

            bars = Math.DivRem(totalTicks, barLength, out ticks);

            var beatLength = BarBeatUtilities.GetBeatLength(timeSignature, ticksPerQuarterNote);

            beats = Math.DivRem(ticks, beatLength, out ticks);
        }
        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));
        }
        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);
        }