Fix inaccurate beat snapping for osu charts. Fix osu charts with too low BPM not able to be imported.

This commit is contained in:
2023-03-05 20:40:02 +08:00
parent 6d74685cb7
commit 1851bd3c54
3 changed files with 16 additions and 6 deletions

View File

@@ -12,12 +12,12 @@ namespace Cryville.Common.Math {
/// <param name="error">The error.</param>
/// <param name="n">The numerator.</param>
/// <param name="d">The denominator.</param>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="value" /> is less than 0 or <paramref name="error" /> is not greater than 0 or not less than 1.</exception>
/// <exception cref="ArgumentOutOfRangeException"><paramref name="value" /> is less than 0 or <paramref name="error" /> is not greater than 0 or greater than 1.</exception>
public static void ToFraction(double value, double error, out int n, out int d) {
if (value < 0.0)
throw new ArgumentOutOfRangeException("value", "Must be >= 0.");
if (error <= 0.0 || error >= 1.0)
throw new ArgumentOutOfRangeException("accuracy", "Must be > 0 and < 1.");
if (error <= 0.0 || error > 1.0)
throw new ArgumentOutOfRangeException("error", "Must be > 0 and <= 1.");
int num = (int)System.Math.Floor(value);
value -= num;

View File

@@ -42,15 +42,24 @@ namespace Cryville.Crtr.Extensions {
}
}
public class TimeTimingModel : TimingModel {
public TimeTimingModel(double offset = 0) : base(offset) { }
public readonly double InputTimeAccuracy;
public TimeTimingModel(double accuracy = 2e-3, double offset = 0) : base(offset) {
if (accuracy <= 0) throw new ArgumentOutOfRangeException("accuracy");
InputTimeAccuracy = accuracy;
}
public void ForwardTo(double t) {
if (t == Time) return;
if (BPM == 0) throw new InvalidOperationException("BPM not determined");
BeatTime += (t - Time) * BPM / 60;
int n, d;
FractionUtils.ToFraction(BeatTime, 1f / 48 / BPM * 60, out n, out d);
FractionUtils.ToFraction(BeatTime, Math.Min(1, InputTimeAccuracy * BPM / 60), out n, out d);
FractionalBeatTime = new BeatTime(n, d);
Time = t;
}
public void ForceSnap() {
var alignedBeat = (int)Math.Round(BeatTime);
BeatTime = alignedBeat;
FractionalBeatTime = new BeatTime(alignedBeat, 1);
}
}
}

View File

@@ -108,10 +108,11 @@ namespace Cryville.Crtr.Extensions.osu {
else if (ev is osuEvent.TimingChange) {
var tev = (osuEvent.TimingChange)ev;
if (tm == null) {
tm = new TimeTimingModel(tev.StartTime / 1e3);
tm = new TimeTimingModel(tev.StartTime / 1e3) { InputTimeAccuracy = 2e-3 };
bgmEv.offset = (float)(tev.StartTime / 1e3 + OFFSET);
}
tm.BeatLength = tev.BeatLength / 1e3;
tm.ForceSnap();
chart.sigs.Add(new Chart.Signature {
time = tm.FractionalBeatTime,
tempo = (float)tm.BPM,