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); }