1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-21 18:07:23 +08:00

Replace TimeSignatures enum with struct for storage of arbitrary meter

This commit is contained in:
Bartłomiej Dach 2022-01-22 17:27:27 +01:00
parent ccac7b85be
commit 735414bc49
No known key found for this signature in database
GPG Key ID: BCECCD4FA41F6497
17 changed files with 84 additions and 32 deletions

View File

@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Default
if (!effectPoint.KiaiMode)
return;
if (beatIndex % (int)timingPoint.TimeSignature != 0)
if (beatIndex % timingPoint.TimeSignature.Numerator != 0)
return;
double duration = timingPoint.BeatLength * 2;

View File

@ -178,17 +178,17 @@ namespace osu.Game.Tests.Beatmaps.Formats
var timingPoint = controlPoints.TimingPointAt(0);
Assert.AreEqual(956, timingPoint.Time);
Assert.AreEqual(329.67032967033, timingPoint.BeatLength);
Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature);
Assert.AreEqual(TimeSignature.SimpleQuadruple, timingPoint.TimeSignature);
timingPoint = controlPoints.TimingPointAt(48428);
Assert.AreEqual(956, timingPoint.Time);
Assert.AreEqual(329.67032967033d, timingPoint.BeatLength);
Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature);
Assert.AreEqual(TimeSignature.SimpleQuadruple, timingPoint.TimeSignature);
timingPoint = controlPoints.TimingPointAt(119637);
Assert.AreEqual(119637, timingPoint.Time);
Assert.AreEqual(659.340659340659, timingPoint.BeatLength);
Assert.AreEqual(TimeSignatures.SimpleQuadruple, timingPoint.TimeSignature);
Assert.AreEqual(TimeSignature.SimpleQuadruple, timingPoint.TimeSignature);
var difficultyPoint = controlPoints.DifficultyPointAt(0);
Assert.AreEqual(0, difficultyPoint.Time);

View File

@ -26,7 +26,7 @@ namespace osu.Game.Tests.NonVisual
const int beat_length_numerator = 2000;
const int beat_length_denominator = 7;
const TimeSignatures signature = TimeSignatures.SimpleQuadruple;
TimeSignature signature = TimeSignature.SimpleQuadruple;
var beatmap = new Beatmap
{
@ -49,7 +49,7 @@ namespace osu.Game.Tests.NonVisual
for (int i = 0; i * beat_length_denominator < barLines.Count; i++)
{
var barLine = barLines[i * beat_length_denominator];
int expectedTime = beat_length_numerator * (int)signature * i;
int expectedTime = beat_length_numerator * signature.Numerator * i;
// every seventh bar's start time should be at least greater than the whole number we expect.
// It cannot be less, as that can affect overlapping scroll algorithms
@ -60,7 +60,7 @@ namespace osu.Game.Tests.NonVisual
Assert.IsTrue(Precision.AlmostEquals(barLine.StartTime, expectedTime));
// check major/minor lines for good measure too
Assert.AreEqual(i % (int)signature == 0, barLine.Major);
Assert.AreEqual(i % signature.Numerator == 0, barLine.Major);
}
}

View File

@ -24,8 +24,8 @@ namespace osu.Game.Tests.Visual.Gameplay
Add(new ModNightcore<HitObject>.NightcoreBeatContainer());
AddStep("change signature to quadruple", () => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ForEach(p => p.TimeSignature = TimeSignatures.SimpleQuadruple));
AddStep("change signature to triple", () => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ForEach(p => p.TimeSignature = TimeSignatures.SimpleTriple));
AddStep("change signature to quadruple", () => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ForEach(p => p.TimeSignature = TimeSignature.SimpleQuadruple));
AddStep("change signature to triple", () => Beatmap.Value.Beatmap.ControlPointInfo.TimingPoints.ForEach(p => p.TimeSignature = TimeSignature.SimpleTriple));
}
}
}

View File

@ -13,7 +13,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <summary>
/// The time signature at this control point.
/// </summary>
public readonly Bindable<TimeSignatures> TimeSignatureBindable = new Bindable<TimeSignatures>(TimeSignatures.SimpleQuadruple) { Default = TimeSignatures.SimpleQuadruple };
public readonly Bindable<TimeSignature> TimeSignatureBindable = new Bindable<TimeSignature>(TimeSignature.SimpleQuadruple);
/// <summary>
/// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing.
@ -35,7 +35,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <summary>
/// The time signature at this control point.
/// </summary>
public TimeSignatures TimeSignature
public TimeSignature TimeSignature
{
get => TimeSignatureBindable.Value;
set => TimeSignatureBindable.Value = value;

View File

@ -340,9 +340,9 @@ namespace osu.Game.Beatmaps.Formats
double beatLength = Parsing.ParseDouble(split[1].Trim());
double speedMultiplier = beatLength < 0 ? 100.0 / -beatLength : 1;
TimeSignatures timeSignature = TimeSignatures.SimpleQuadruple;
TimeSignature timeSignature = TimeSignature.SimpleQuadruple;
if (split.Length >= 3)
timeSignature = split[2][0] == '0' ? TimeSignatures.SimpleQuadruple : (TimeSignatures)Parsing.ParseInt(split[2]);
timeSignature = split[2][0] == '0' ? TimeSignature.SimpleQuadruple : new TimeSignature(Parsing.ParseInt(split[2]));
LegacySampleBank sampleSet = defaultSampleBank;
if (split.Length >= 4)

View File

@ -227,7 +227,7 @@ namespace osu.Game.Beatmaps.Formats
if (effectPoint.OmitFirstBarLine)
effectFlags |= LegacyEffectFlags.OmitFirstBarLine;
writer.Write(FormattableString.Invariant($"{(int)legacyControlPoints.TimingPointAt(time).TimeSignature},"));
writer.Write(FormattableString.Invariant($"{legacyControlPoints.TimingPointAt(time).TimeSignature.Numerator},"));
writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},"));
writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample)},"));
writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},"));

View File

@ -0,0 +1,45 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
namespace osu.Game.Beatmaps.Timing
{
/// <summary>
/// Stores the time signature of a track.
/// For now, the lower numeral can only be 4; support for other denominators can be considered at a later date.
/// </summary>
public class TimeSignature : IEquatable<TimeSignature>
{
/// <summary>
/// The numerator of a signature.
/// </summary>
public int Numerator { get; }
// TODO: support time signatures with a denominator other than 4
// this in particular requires a new beatmap format.
public TimeSignature(int numerator)
{
if (numerator < 1)
throw new ArgumentOutOfRangeException(nameof(numerator), numerator, "The numerator of a time signature must be positive.");
Numerator = numerator;
}
public static TimeSignature SimpleTriple => new TimeSignature(3);
public static TimeSignature SimpleQuadruple => new TimeSignature(4);
public override string ToString() => $"{Numerator}/4";
public bool Equals(TimeSignature other)
{
if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true;
return Numerator == other.Numerator;
}
public override int GetHashCode() => Numerator;
}
}

View File

@ -1,11 +1,13 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.ComponentModel;
namespace osu.Game.Beatmaps.Timing
{
public enum TimeSignatures
[Obsolete("Use osu.Game.Beatmaps.Timing.TimeSignature instead.")]
public enum TimeSignatures // can be removed 20220722
{
[Description("4/4")]
SimpleQuadruple = 4,

View File

@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Mods
if (!IsBeatSyncedWithTrack) return;
int timeSignature = (int)timingPoint.TimeSignature;
int timeSignature = timingPoint.TimeSignature.Numerator;
// play metronome from one measure before the first object.
if (BeatSyncClock.CurrentTime < firstHitTime - timingPoint.BeatLength * timeSignature)

View File

@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Mods
{
base.OnNewBeat(beatIndex, timingPoint, effectPoint, amplitudes);
int beatsPerBar = (int)timingPoint.TimeSignature;
int beatsPerBar = timingPoint.TimeSignature.Numerator;
int segmentLength = beatsPerBar * Divisor * bars_per_segment;
if (!IsBeatSyncedWithTrack)
@ -102,14 +102,14 @@ namespace osu.Game.Rulesets.Mods
playBeatFor(beatIndex % segmentLength, timingPoint.TimeSignature);
}
private void playBeatFor(int beatIndex, TimeSignatures signature)
private void playBeatFor(int beatIndex, TimeSignature signature)
{
if (beatIndex == 0)
finishSample?.Play();
switch (signature)
switch (signature.Numerator)
{
case TimeSignatures.SimpleTriple:
case 3:
switch (beatIndex % 6)
{
case 0:
@ -127,7 +127,7 @@ namespace osu.Game.Rulesets.Mods
break;
case TimeSignatures.SimpleQuadruple:
case 4:
switch (beatIndex % 4)
{
case 0:

View File

@ -41,9 +41,9 @@ namespace osu.Game.Rulesets.Objects
int currentBeat = 0;
// Stop on the beat before the next timing point, or if there is no next timing point stop slightly past the last object
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature;
double endTime = i < timingPoints.Count - 1 ? timingPoints[i + 1].Time - currentTimingPoint.BeatLength : lastHitTime + currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator;
double barLength = currentTimingPoint.BeatLength * (int)currentTimingPoint.TimeSignature;
double barLength = currentTimingPoint.BeatLength * currentTimingPoint.TimeSignature.Numerator;
for (double t = currentTimingPoint.Time; Precision.DefinitelyBigger(endTime, t); t += barLength, currentBeat++)
{
@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Objects
BarLines.Add(new TBarLine
{
StartTime = t,
Major = currentBeat % (int)currentTimingPoint.TimeSignature == 0
Major = currentBeat % currentTimingPoint.TimeSignature.Numerator == 0
});
}
}

View File

@ -125,7 +125,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (beat == 0 && i == 0)
nextMinTick = float.MinValue;
int indexInBar = beat % ((int)point.TimeSignature * beatDivisor.Value);
int indexInBar = beat % (point.TimeSignature.Numerator * beatDivisor.Value);
int divisor = BindableBeatDivisor.GetDivisorForBeatIndex(beat, beatDivisor.Value);
var colour = BindableBeatDivisor.GetColourFor(divisor, colours);

View File

@ -13,7 +13,7 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes
public class TimingRowAttribute : RowAttribute
{
private readonly BindableNumber<double> beatLength;
private readonly Bindable<TimeSignatures> timeSignature;
private readonly Bindable<TimeSignature> timeSignature;
private OsuSpriteText text;
public TimingRowAttribute(TimingControlPoint timing)

View File

@ -15,7 +15,7 @@ namespace osu.Game.Screens.Edit.Timing
internal class TimingSection : Section<TimingControlPoint>
{
private SettingsSlider<double> bpmSlider;
private SettingsEnumDropdown<TimeSignatures> timeSignature;
private SettingsDropdown<TimeSignature> timeSignature;
private BPMTextBox bpmTextEntry;
[BackgroundDependencyLoader]
@ -25,9 +25,14 @@ namespace osu.Game.Screens.Edit.Timing
{
bpmTextEntry = new BPMTextBox(),
bpmSlider = new BPMSlider(),
timeSignature = new SettingsEnumDropdown<TimeSignatures>
timeSignature = new SettingsDropdown<TimeSignature>
{
LabelText = "Time Signature"
LabelText = "Time Signature",
Items = new[]
{
TimeSignature.SimpleTriple,
TimeSignature.SimpleQuadruple
}
},
});
}

View File

@ -94,9 +94,9 @@ namespace osu.Game.Screens.Menu
if (beatIndex < 0)
return;
if (effectPoint.KiaiMode ? beatIndex % 2 == 0 : beatIndex % (int)timingPoint.TimeSignature == 0)
if (effectPoint.KiaiMode ? beatIndex % 2 == 0 : beatIndex % timingPoint.TimeSignature.Numerator == 0)
flash(leftBox, timingPoint.BeatLength, effectPoint.KiaiMode, amplitudes);
if (effectPoint.KiaiMode ? beatIndex % 2 == 1 : beatIndex % (int)timingPoint.TimeSignature == 0)
if (effectPoint.KiaiMode ? beatIndex % 2 == 1 : beatIndex % timingPoint.TimeSignature.Numerator == 0)
flash(rightBox, timingPoint.BeatLength, effectPoint.KiaiMode, amplitudes);
}

View File

@ -282,7 +282,7 @@ namespace osu.Game.Screens.Menu
{
this.Delay(early_activation).Schedule(() =>
{
if (beatIndex % (int)timingPoint.TimeSignature == 0)
if (beatIndex % timingPoint.TimeSignature.Numerator == 0)
sampleDownbeat.Play();
else
sampleBeat.Play();