1
0
mirror of https://github.com/ppy/osu.git synced 2026-05-30 00:34:47 +08:00

Merge pull request #22754 from peppy/omit-barline-in-timing-control-point

Fix "omit barline" sometimes not applying correctly
This commit is contained in:
Bartłomiej Dach
2023-03-07 23:21:34 +01:00
committed by GitHub
Unverified
16 changed files with 115 additions and 54 deletions
@@ -73,11 +73,10 @@ namespace osu.Game.Rulesets.Taiko.Tests
beatmap.ControlPointInfo.Add(start_time, new TimingControlPoint
{
BeatLength = beat_length,
TimeSignature = new TimeSignature(time_signature_numerator)
TimeSignature = new TimeSignature(time_signature_numerator),
OmitFirstBarLine = true
});
beatmap.ControlPointInfo.Add(start_time, new EffectControlPoint { OmitFirstBarLine = true });
var barlines = new BarLineGenerator<BarLine>(beatmap).BarLines;
AddAssert("first barline ommited", () => barlines.All(b => b.StartTime != start_time));
@@ -72,7 +72,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
converted.ControlPointInfo.Add(hitObject.StartTime, new EffectControlPoint
{
KiaiMode = currentEffectPoint.KiaiMode,
OmitFirstBarLine = currentEffectPoint.OmitFirstBarLine,
ScrollSpeed = lastScrollSpeed = nextScrollSpeed,
});
}
@@ -181,16 +181,19 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual(956, timingPoint.Time);
Assert.AreEqual(329.67032967033, timingPoint.BeatLength);
Assert.AreEqual(TimeSignature.SimpleQuadruple, timingPoint.TimeSignature);
Assert.IsFalse(timingPoint.OmitFirstBarLine);
timingPoint = controlPoints.TimingPointAt(48428);
Assert.AreEqual(956, timingPoint.Time);
Assert.AreEqual(329.67032967033d, timingPoint.BeatLength);
Assert.AreEqual(TimeSignature.SimpleQuadruple, timingPoint.TimeSignature);
Assert.IsFalse(timingPoint.OmitFirstBarLine);
timingPoint = controlPoints.TimingPointAt(119637);
Assert.AreEqual(119637, timingPoint.Time);
Assert.AreEqual(659.340659340659, timingPoint.BeatLength);
Assert.AreEqual(TimeSignature.SimpleQuadruple, timingPoint.TimeSignature);
Assert.IsFalse(timingPoint.OmitFirstBarLine);
var difficultyPoint = controlPoints.DifficultyPointAt(0);
Assert.AreEqual(0, difficultyPoint.Time);
@@ -222,17 +225,14 @@ namespace osu.Game.Tests.Beatmaps.Formats
var effectPoint = controlPoints.EffectPointAt(0);
Assert.AreEqual(0, effectPoint.Time);
Assert.IsFalse(effectPoint.KiaiMode);
Assert.IsFalse(effectPoint.OmitFirstBarLine);
effectPoint = controlPoints.EffectPointAt(53703);
Assert.AreEqual(53703, effectPoint.Time);
Assert.IsTrue(effectPoint.KiaiMode);
Assert.IsFalse(effectPoint.OmitFirstBarLine);
effectPoint = controlPoints.EffectPointAt(116637);
Assert.AreEqual(95901, effectPoint.Time);
Assert.IsFalse(effectPoint.KiaiMode);
Assert.IsFalse(effectPoint.OmitFirstBarLine);
}
}
@@ -273,6 +273,28 @@ namespace osu.Game.Tests.Beatmaps.Formats
}
}
[Test]
public void TestDecodeOmitBarLineEffect()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = TestResources.OpenResource("omit-barline-control-points.osu"))
using (var stream = new LineBufferedReader(resStream))
{
var controlPoints = (LegacyControlPointInfo)decoder.Decode(stream).ControlPointInfo;
Assert.That(controlPoints.TimingPoints.Count, Is.EqualTo(6));
Assert.That(controlPoints.EffectPoints.Count, Is.EqualTo(0));
Assert.That(controlPoints.TimingPointAt(500).OmitFirstBarLine, Is.False);
Assert.That(controlPoints.TimingPointAt(1500).OmitFirstBarLine, Is.True);
Assert.That(controlPoints.TimingPointAt(2500).OmitFirstBarLine, Is.False);
Assert.That(controlPoints.TimingPointAt(3500).OmitFirstBarLine, Is.False);
Assert.That(controlPoints.TimingPointAt(4500).OmitFirstBarLine, Is.False);
Assert.That(controlPoints.TimingPointAt(5500).OmitFirstBarLine, Is.True);
}
}
[Test]
public void TestTimingPointResetsSpeedMultiplier()
{
@@ -43,6 +43,18 @@ namespace osu.Game.Tests.NonVisual
Assert.That(cpi.Groups.Count, Is.EqualTo(2));
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(2));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2));
cpi.Add(1200, new TimingControlPoint { OmitFirstBarLine = true }); // is not redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(3));
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(3));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(3));
cpi.Add(1500, new TimingControlPoint { OmitFirstBarLine = true }); // is not redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(4));
Assert.That(cpi.TimingPoints.Count, Is.EqualTo(4));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(4));
}
[Test]
@@ -95,12 +107,12 @@ namespace osu.Game.Tests.NonVisual
Assert.That(cpi.EffectPoints.Count, Is.EqualTo(0));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
cpi.Add(1000, new EffectControlPoint { KiaiMode = true, OmitFirstBarLine = true }); // is not redundant
cpi.Add(1400, new EffectControlPoint { KiaiMode = true, OmitFirstBarLine = true }); // same settings, but is not redundant
cpi.Add(1000, new EffectControlPoint { KiaiMode = true }); // is not redundant
cpi.Add(1400, new EffectControlPoint { KiaiMode = true }); // is redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(2));
Assert.That(cpi.EffectPoints.Count, Is.EqualTo(2));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(2));
Assert.That(cpi.Groups.Count, Is.EqualTo(1));
Assert.That(cpi.EffectPoints.Count, Is.EqualTo(1));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(1));
}
[Test]
@@ -0,0 +1,27 @@
osu file format v14
[TimingPoints]
// Uninherited: none, inherited: none
0,500,4,2,0,100,1,0
0,-50,4,3,0,100,0,0
// Uninherited: omit, inherited: none
1000,500,4,2,0,100,1,8
1000,-50,4,3,0,100,0,0
// Uninherited: none, inherited: omit (should be ignored, inheriting cannot omit)
2000,500,4,2,0,100,1,0
2000,-50,4,3,0,100,0,8
// Inherited: none, uninherited: none
3000,-50,4,3,0,100,0,0
3000,500,4,2,0,100,1,0
// Inherited: omit, uninherited: none (should be ignored, inheriting cannot omit)
4000,-50,4,3,0,100,0,8
4000,500,4,2,0,100,1,0
// Inherited: none, uninherited: omit
5000,-50,4,3,0,100,0,0
5000,500,4,2,0,100,1,8
@@ -13,15 +13,9 @@ namespace osu.Game.Beatmaps.ControlPoints
public static readonly EffectControlPoint DEFAULT = new EffectControlPoint
{
KiaiModeBindable = { Disabled = true },
OmitFirstBarLineBindable = { Disabled = true },
ScrollSpeedBindable = { Disabled = true }
};
/// <summary>
/// Whether the first bar line of this control point is ignored.
/// </summary>
public readonly BindableBool OmitFirstBarLineBindable = new BindableBool();
/// <summary>
/// The relative scroll speed at this control point.
/// </summary>
@@ -43,15 +37,6 @@ namespace osu.Game.Beatmaps.ControlPoints
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Purple;
/// <summary>
/// Whether the first bar line of this control point is ignored.
/// </summary>
public bool OmitFirstBarLine
{
get => OmitFirstBarLineBindable.Value;
set => OmitFirstBarLineBindable.Value = value;
}
/// <summary>
/// Whether this control point enables Kiai mode.
/// </summary>
@@ -67,16 +52,13 @@ namespace osu.Game.Beatmaps.ControlPoints
}
public override bool IsRedundant(ControlPoint? existing)
=> !OmitFirstBarLine
&& existing is EffectControlPoint existingEffect
=> existing is EffectControlPoint existingEffect
&& KiaiMode == existingEffect.KiaiMode
&& OmitFirstBarLine == existingEffect.OmitFirstBarLine
&& ScrollSpeed == existingEffect.ScrollSpeed;
public override void CopyFrom(ControlPoint other)
{
KiaiMode = ((EffectControlPoint)other).KiaiMode;
OmitFirstBarLine = ((EffectControlPoint)other).OmitFirstBarLine;
ScrollSpeed = ((EffectControlPoint)other).ScrollSpeed;
base.CopyFrom(other);
@@ -88,10 +70,9 @@ namespace osu.Game.Beatmaps.ControlPoints
public bool Equals(EffectControlPoint? other)
=> base.Equals(other)
&& OmitFirstBarLine == other.OmitFirstBarLine
&& ScrollSpeed == other.ScrollSpeed
&& KiaiMode == other.KiaiMode;
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), OmitFirstBarLine, ScrollSpeed, KiaiMode);
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), ScrollSpeed, KiaiMode);
}
}
@@ -16,6 +16,11 @@ namespace osu.Game.Beatmaps.ControlPoints
/// </summary>
public readonly Bindable<TimeSignature> TimeSignatureBindable = new Bindable<TimeSignature>(TimeSignature.SimpleQuadruple);
/// <summary>
/// Whether the first bar line of this control point is ignored.
/// </summary>
public readonly BindableBool OmitFirstBarLineBindable = new BindableBool();
/// <summary>
/// Default length of a beat in milliseconds. Used whenever there is no beatmap or track playing.
/// </summary>
@@ -30,6 +35,7 @@ namespace osu.Game.Beatmaps.ControlPoints
Value = default_beat_length,
Disabled = true
},
OmitFirstBarLineBindable = { Disabled = true },
TimeSignatureBindable = { Disabled = true }
};
@@ -42,6 +48,15 @@ namespace osu.Game.Beatmaps.ControlPoints
set => TimeSignatureBindable.Value = value;
}
/// <summary>
/// Whether the first bar line of this control point is ignored.
/// </summary>
public bool OmitFirstBarLine
{
get => OmitFirstBarLineBindable.Value;
set => OmitFirstBarLineBindable.Value = value;
}
public const double DEFAULT_BEAT_LENGTH = 1000;
/// <summary>
@@ -73,6 +88,7 @@ namespace osu.Game.Beatmaps.ControlPoints
public override void CopyFrom(ControlPoint other)
{
TimeSignature = ((TimingControlPoint)other).TimeSignature;
OmitFirstBarLine = ((TimingControlPoint)other).OmitFirstBarLine;
BeatLength = ((TimingControlPoint)other).BeatLength;
base.CopyFrom(other);
@@ -85,8 +101,9 @@ namespace osu.Game.Beatmaps.ControlPoints
public bool Equals(TimingControlPoint? other)
=> base.Equals(other)
&& TimeSignature.Equals(other.TimeSignature)
&& OmitFirstBarLine == other.OmitFirstBarLine
&& BeatLength.Equals(other.BeatLength);
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), TimeSignature, BeatLength);
public override int GetHashCode() => HashCode.Combine(base.GetHashCode(), TimeSignature, BeatLength, OmitFirstBarLine);
}
}
@@ -431,6 +431,7 @@ namespace osu.Game.Beatmaps.Formats
controlPoint.BeatLength = beatLength;
controlPoint.TimeSignature = timeSignature;
controlPoint.OmitFirstBarLine = omitFirstBarSignature;
addControlPoint(time, controlPoint, true);
}
@@ -447,7 +448,6 @@ namespace osu.Game.Beatmaps.Formats
var effectPoint = new EffectControlPoint
{
KiaiMode = kiaiMode,
OmitFirstBarLine = omitFirstBarSignature,
};
// osu!taiko and osu!mania use effect points rather than difficulty points for scroll speed adjustments.
@@ -222,6 +222,7 @@ namespace osu.Game.Beatmaps.Formats
{
var samplePoint = legacyControlPoints.SamplePointAt(time);
var effectPoint = legacyControlPoints.EffectPointAt(time);
var timingPoint = legacyControlPoints.TimingPointAt(time);
// Apply the control point to a hit sample to uncover legacy properties (e.g. suffix)
HitSampleInfo tempHitSample = samplePoint.ApplyTo(new ConvertHitObjectParser.LegacyHitSampleInfo(string.Empty));
@@ -230,10 +231,10 @@ namespace osu.Game.Beatmaps.Formats
LegacyEffectFlags effectFlags = LegacyEffectFlags.None;
if (effectPoint.KiaiMode)
effectFlags |= LegacyEffectFlags.Kiai;
if (effectPoint.OmitFirstBarLine)
if (timingPoint.OmitFirstBarLine)
effectFlags |= LegacyEffectFlags.OmitFirstBarLine;
writer.Write(FormattableString.Invariant($"{legacyControlPoints.TimingPointAt(time).TimeSignature.Numerator},"));
writer.Write(FormattableString.Invariant($"{timingPoint.TimeSignature.Numerator},"));
writer.Write(FormattableString.Invariant($"{(int)toLegacySampleBank(tempHitSample.Bank)},"));
writer.Write(FormattableString.Invariant($"{toLegacyCustomSampleBank(tempHitSample)},"));
writer.Write(FormattableString.Invariant($"{tempHitSample.Volume},"));
@@ -114,7 +114,7 @@ namespace osu.Game.Graphics.Containers
while (beatLength < MinimumBeatLength)
beatLength *= 2;
int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (effectPoint.OmitFirstBarLine ? 1 : 0);
int beatIndex = (int)((currentTrackTime - timingPoint.Time) / beatLength) - (timingPoint.OmitFirstBarLine ? 1 : 0);
// The beats before the start of the first control point are off by 1, this should do the trick
if (currentTrackTime < timingPoint.Time)
@@ -38,7 +38,6 @@ namespace osu.Game.Rulesets.Objects
for (int i = 0; i < timingPoints.Count; i++)
{
TimingControlPoint currentTimingPoint = timingPoints[i];
EffectControlPoint currentEffectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTimingPoint.Time);
int currentBeat = 0;
// Don't generate barlines before the hit object or t=0 (whichever is earliest). Some beatmaps use very unrealistic values here (although none are ranked).
@@ -66,7 +65,7 @@ namespace osu.Game.Rulesets.Objects
startTime = currentTimingPoint.Time + barCount * barLength;
}
if (currentEffectPoint.OmitFirstBarLine)
if (currentTimingPoint.OmitFirstBarLine)
{
startTime += barLength;
}
@@ -26,7 +26,7 @@ namespace osu.Game.Screens.Edit.Timing
[Resolved]
private EditorClock clock { get; set; } = null!;
public const float TIMING_COLUMN_WIDTH = 230;
public const float TIMING_COLUMN_WIDTH = 300;
public IEnumerable<ControlPointGroup> ControlGroups
{
@@ -14,7 +14,6 @@ namespace osu.Game.Screens.Edit.Timing
internal partial class EffectSection : Section<EffectControlPoint>
{
private LabelledSwitchButton kiai = null!;
private LabelledSwitchButton omitBarLine = null!;
private SliderWithTextBoxInput<double> scrollSpeedSlider = null!;
@@ -24,7 +23,6 @@ namespace osu.Game.Screens.Edit.Timing
Flow.AddRange(new Drawable[]
{
kiai = new LabelledSwitchButton { Label = "Kiai Time" },
omitBarLine = new LabelledSwitchButton { Label = "Skip Bar Line" },
scrollSpeedSlider = new SliderWithTextBoxInput<double>("Scroll Speed")
{
Current = new EffectControlPoint().ScrollSpeedBindable,
@@ -38,7 +36,6 @@ namespace osu.Game.Screens.Edit.Timing
base.LoadComplete();
kiai.Current.BindValueChanged(_ => saveChanges());
omitBarLine.Current.BindValueChanged(_ => saveChanges());
scrollSpeedSlider.Current.BindValueChanged(_ => saveChanges());
var drawableRuleset = Beatmap.BeatmapInfo.Ruleset.CreateInstance().CreateDrawableRulesetWith(Beatmap.PlayableBeatmap);
@@ -60,7 +57,6 @@ namespace osu.Game.Screens.Edit.Timing
isRebinding = true;
kiai.Current = point.NewValue.KiaiModeBindable;
omitBarLine.Current = point.NewValue.OmitFirstBarLineBindable;
scrollSpeedSlider.Current = point.NewValue.ScrollSpeedBindable;
isRebinding = false;
@@ -74,7 +70,6 @@ namespace osu.Game.Screens.Edit.Timing
return new EffectControlPoint
{
KiaiMode = reference.KiaiMode,
OmitFirstBarLine = reference.OmitFirstBarLine,
ScrollSpeed = reference.ScrollSpeed,
};
}
@@ -11,18 +11,15 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes
public partial class EffectRowAttribute : RowAttribute
{
private readonly Bindable<bool> kiaiMode;
private readonly Bindable<bool> omitBarLine;
private readonly BindableNumber<double> scrollSpeed;
private AttributeText kiaiModeBubble = null!;
private AttributeText omitBarLineBubble = null!;
private AttributeText text = null!;
public EffectRowAttribute(EffectControlPoint effect)
: base(effect, "effect")
{
kiaiMode = effect.KiaiModeBindable.GetBoundCopy();
omitBarLine = effect.OmitFirstBarLineBindable.GetBoundCopy();
scrollSpeed = effect.ScrollSpeedBindable.GetBoundCopy();
}
@@ -37,11 +34,9 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes
},
text = new AttributeText(Point) { Width = 45 },
kiaiModeBubble = new AttributeText(Point) { Text = "kiai" },
omitBarLineBubble = new AttributeText(Point) { Text = "no barline" },
});
kiaiMode.BindValueChanged(enabled => kiaiModeBubble.FadeTo(enabled.NewValue ? 1 : 0), true);
omitBarLine.BindValueChanged(enabled => omitBarLineBubble.FadeTo(enabled.NewValue ? 1 : 0), true);
scrollSpeed.BindValueChanged(_ => updateText(), true);
}
@@ -4,6 +4,7 @@
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
using osu.Game.Graphics.Sprites;
@@ -14,24 +15,32 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes
public partial class TimingRowAttribute : RowAttribute
{
private readonly BindableNumber<double> beatLength;
private readonly Bindable<bool> omitBarLine;
private readonly Bindable<TimeSignature> timeSignature;
private AttributeText omitBarLineBubble = null!;
private OsuSpriteText text = null!;
public TimingRowAttribute(TimingControlPoint timing)
: base(timing, "timing")
{
timeSignature = timing.TimeSignatureBindable.GetBoundCopy();
omitBarLine = timing.OmitFirstBarLineBindable.GetBoundCopy();
beatLength = timing.BeatLengthBindable.GetBoundCopy();
}
[BackgroundDependencyLoader]
private void load(OverlayColourProvider colourProvider)
{
Content.Add(text = new AttributeText(Point));
Content.AddRange(new[]
{
text = new AttributeText(Point),
omitBarLineBubble = new AttributeText(Point) { Text = "no barline" },
});
Background.Colour = colourProvider.Background4;
timeSignature.BindValueChanged(_ => updateText());
omitBarLine.BindValueChanged(enabled => omitBarLineBubble.FadeTo(enabled.NewValue ? 1 : 0), true);
beatLength.BindValueChanged(_ => updateText(), true);
}
@@ -12,6 +12,7 @@ namespace osu.Game.Screens.Edit.Timing
internal partial class TimingSection : Section<TimingControlPoint>
{
private LabelledTimeSignature timeSignature = null!;
private LabelledSwitchButton omitBarLine = null!;
private BPMTextBox bpmTextEntry = null!;
[BackgroundDependencyLoader]
@@ -24,7 +25,8 @@ namespace osu.Game.Screens.Edit.Timing
timeSignature = new LabelledTimeSignature
{
Label = "Time Signature"
}
},
omitBarLine = new LabelledSwitchButton { Label = "Skip Bar Line" },
});
}
@@ -33,6 +35,7 @@ namespace osu.Game.Screens.Edit.Timing
base.LoadComplete();
bpmTextEntry.Current.BindValueChanged(_ => saveChanges());
omitBarLine.Current.BindValueChanged(_ => saveChanges());
timeSignature.Current.BindValueChanged(_ => saveChanges());
void saveChanges()
@@ -51,6 +54,7 @@ namespace osu.Game.Screens.Edit.Timing
bpmTextEntry.Bindable = point.NewValue.BeatLengthBindable;
timeSignature.Current = point.NewValue.TimeSignatureBindable;
omitBarLine.Current = point.NewValue.OmitFirstBarLineBindable;
isRebinding = false;
}
@@ -63,7 +67,8 @@ namespace osu.Game.Screens.Edit.Timing
return new TimingControlPoint
{
BeatLength = reference.BeatLength,
TimeSignature = reference.TimeSignature
TimeSignature = reference.TimeSignature,
OmitFirstBarLine = reference.OmitFirstBarLine,
};
}