Move samples to LegacyControlPointInfo

This commit is contained in:
Dean Herbert
2021-08-25 18:00:57 +09:00
parent 6b3cc81e19
commit 7257aae7f2
7 changed files with 110 additions and 63 deletions

View File

@ -10,6 +10,7 @@ using osu.Game.Tests.Resources;
using System.Linq; using System.Linq;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
@ -166,7 +167,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var stream = new LineBufferedReader(resStream)) using (var stream = new LineBufferedReader(resStream))
{ {
var beatmap = decoder.Decode(stream); var beatmap = decoder.Decode(stream);
var controlPoints = beatmap.ControlPointInfo; var controlPoints = (LegacyControlPointInfo)beatmap.ControlPointInfo;
Assert.AreEqual(4, controlPoints.TimingPoints.Count); Assert.AreEqual(4, controlPoints.TimingPoints.Count);
Assert.AreEqual(5, controlPoints.DifficultyPoints.Count); Assert.AreEqual(5, controlPoints.DifficultyPoints.Count);
@ -240,7 +241,7 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var resStream = TestResources.OpenResource("overlapping-control-points.osu")) using (var resStream = TestResources.OpenResource("overlapping-control-points.osu"))
using (var stream = new LineBufferedReader(resStream)) using (var stream = new LineBufferedReader(resStream))
{ {
var controlPoints = decoder.Decode(stream).ControlPointInfo; var controlPoints = (LegacyControlPointInfo)decoder.Decode(stream).ControlPointInfo;
Assert.That(controlPoints.TimingPoints.Count, Is.EqualTo(4)); Assert.That(controlPoints.TimingPoints.Count, Is.EqualTo(4));
Assert.That(controlPoints.DifficultyPoints.Count, Is.EqualTo(3)); Assert.That(controlPoints.DifficultyPoints.Count, Is.EqualTo(3));

View File

@ -64,7 +64,7 @@ namespace osu.Game.Tests.NonVisual
[Test] [Test]
public void TestAddRedundantSample() public void TestAddRedundantSample()
{ {
var cpi = new ControlPointInfo(); var cpi = new LegacyControlPointInfo();
cpi.Add(0, new SampleControlPoint()); // is *not* redundant, special exception for first sample point cpi.Add(0, new SampleControlPoint()); // is *not* redundant, special exception for first sample point
cpi.Add(1000, new SampleControlPoint()); // is redundant cpi.Add(1000, new SampleControlPoint()); // is redundant
@ -142,7 +142,7 @@ namespace osu.Game.Tests.NonVisual
[Test] [Test]
public void TestRemoveGroupAlsoRemovedControlPoints() public void TestRemoveGroupAlsoRemovedControlPoints()
{ {
var cpi = new ControlPointInfo(); var cpi = new LegacyControlPointInfo();
var group = cpi.GroupAt(1000, true); var group = cpi.GroupAt(1000, true);

View File

@ -14,6 +14,64 @@ using osu.Game.Utils;
namespace osu.Game.Beatmaps.ControlPoints namespace osu.Game.Beatmaps.ControlPoints
{ {
public class LegacyControlPointInfo : ControlPointInfo
{
/// <summary>
/// All sound points.
/// </summary>
[JsonProperty]
public IBindableList<SampleControlPoint> SamplePoints => samplePoints;
private readonly BindableList<SampleControlPoint> samplePoints = new BindableList<SampleControlPoint>();
/// <summary>
/// Finds the sound control point that is active at <paramref name="time"/>.
/// </summary>
/// <param name="time">The time to find the sound control point at.</param>
/// <returns>The sound control point.</returns>
[NotNull]
public SampleControlPoint SamplePointAt(double time) => BinarySearchWithFallback(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : SampleControlPoint.DEFAULT);
public override void Clear()
{
base.Clear();
samplePoints.Clear();
}
protected override bool CheckAlreadyExisting(double time, ControlPoint newPoint)
{
if (newPoint is SampleControlPoint _)
{
var existing = BinarySearch(SamplePoints, time);
return newPoint?.IsRedundant(existing) == true;
}
return base.CheckAlreadyExisting(time, newPoint);
}
protected override void GroupItemAdded(ControlPoint controlPoint)
{
if (controlPoint is SampleControlPoint typed)
{
samplePoints.Add(typed);
return;
}
base.GroupItemAdded(controlPoint);
}
protected override void GroupItemRemoved(ControlPoint controlPoint)
{
if (controlPoint is SampleControlPoint typed)
{
samplePoints.Remove(typed);
return;
}
base.GroupItemRemoved(controlPoint);
}
}
[Serializable] [Serializable]
public class ControlPointInfo : IDeepCloneable<ControlPointInfo> public class ControlPointInfo : IDeepCloneable<ControlPointInfo>
{ {
@ -41,14 +99,6 @@ namespace osu.Game.Beatmaps.ControlPoints
private readonly SortedList<DifficultyControlPoint> difficultyPoints = new SortedList<DifficultyControlPoint>(Comparer<DifficultyControlPoint>.Default); private readonly SortedList<DifficultyControlPoint> difficultyPoints = new SortedList<DifficultyControlPoint>(Comparer<DifficultyControlPoint>.Default);
/// <summary>
/// All sound points.
/// </summary>
[JsonProperty]
public IBindableList<SampleControlPoint> SamplePoints => samplePoints;
private readonly BindableList<SampleControlPoint> samplePoints = new BindableList<SampleControlPoint>();
/// <summary> /// <summary>
/// All effect points. /// All effect points.
/// </summary> /// </summary>
@ -69,7 +119,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <param name="time">The time to find the difficulty control point at.</param> /// <param name="time">The time to find the difficulty control point at.</param>
/// <returns>The difficulty control point.</returns> /// <returns>The difficulty control point.</returns>
[NotNull] [NotNull]
public DifficultyControlPoint DifficultyPointAt(double time) => binarySearchWithFallback(DifficultyPoints, time, DifficultyControlPoint.DEFAULT); public DifficultyControlPoint DifficultyPointAt(double time) => BinarySearchWithFallback(DifficultyPoints, time, DifficultyControlPoint.DEFAULT);
/// <summary> /// <summary>
/// Finds the effect control point that is active at <paramref name="time"/>. /// Finds the effect control point that is active at <paramref name="time"/>.
@ -77,15 +127,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <param name="time">The time to find the effect control point at.</param> /// <param name="time">The time to find the effect control point at.</param>
/// <returns>The effect control point.</returns> /// <returns>The effect control point.</returns>
[NotNull] [NotNull]
public EffectControlPoint EffectPointAt(double time) => binarySearchWithFallback(EffectPoints, time, EffectControlPoint.DEFAULT); public EffectControlPoint EffectPointAt(double time) => BinarySearchWithFallback(EffectPoints, time, EffectControlPoint.DEFAULT);
/// <summary>
/// Finds the sound control point that is active at <paramref name="time"/>.
/// </summary>
/// <param name="time">The time to find the sound control point at.</param>
/// <returns>The sound control point.</returns>
[NotNull]
public SampleControlPoint SamplePointAt(double time) => binarySearchWithFallback(SamplePoints, time, SamplePoints.Count > 0 ? SamplePoints[0] : SampleControlPoint.DEFAULT);
/// <summary> /// <summary>
/// Finds the timing control point that is active at <paramref name="time"/>. /// Finds the timing control point that is active at <paramref name="time"/>.
@ -93,7 +135,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <param name="time">The time to find the timing control point at.</param> /// <param name="time">The time to find the timing control point at.</param>
/// <returns>The timing control point.</returns> /// <returns>The timing control point.</returns>
[NotNull] [NotNull]
public TimingControlPoint TimingPointAt(double time) => binarySearchWithFallback(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : TimingControlPoint.DEFAULT); public TimingControlPoint TimingPointAt(double time) => BinarySearchWithFallback(TimingPoints, time, TimingPoints.Count > 0 ? TimingPoints[0] : TimingControlPoint.DEFAULT);
/// <summary> /// <summary>
/// Finds the maximum BPM represented by any timing control point. /// Finds the maximum BPM represented by any timing control point.
@ -112,12 +154,11 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <summary> /// <summary>
/// Remove all <see cref="ControlPointGroup"/>s and return to a pristine state. /// Remove all <see cref="ControlPointGroup"/>s and return to a pristine state.
/// </summary> /// </summary>
public void Clear() public virtual void Clear()
{ {
groups.Clear(); groups.Clear();
timingPoints.Clear(); timingPoints.Clear();
difficultyPoints.Clear(); difficultyPoints.Clear();
samplePoints.Clear();
effectPoints.Clear(); effectPoints.Clear();
} }
@ -129,7 +170,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <returns>Whether the control point was added.</returns> /// <returns>Whether the control point was added.</returns>
public bool Add(double time, ControlPoint controlPoint) public bool Add(double time, ControlPoint controlPoint)
{ {
if (checkAlreadyExisting(time, controlPoint)) if (CheckAlreadyExisting(time, controlPoint))
return false; return false;
GroupAt(time, true).Add(controlPoint); GroupAt(time, true).Add(controlPoint);
@ -147,8 +188,8 @@ namespace osu.Game.Beatmaps.ControlPoints
if (addIfNotExisting) if (addIfNotExisting)
{ {
newGroup.ItemAdded += groupItemAdded; newGroup.ItemAdded += GroupItemAdded;
newGroup.ItemRemoved += groupItemRemoved; newGroup.ItemRemoved += GroupItemRemoved;
groups.Insert(~i, newGroup); groups.Insert(~i, newGroup);
return newGroup; return newGroup;
@ -162,8 +203,8 @@ namespace osu.Game.Beatmaps.ControlPoints
foreach (var item in group.ControlPoints.ToArray()) foreach (var item in group.ControlPoints.ToArray())
group.Remove(item); group.Remove(item);
group.ItemAdded -= groupItemAdded; group.ItemAdded -= GroupItemAdded;
group.ItemRemoved -= groupItemRemoved; group.ItemRemoved -= GroupItemRemoved;
groups.Remove(group); groups.Remove(group);
} }
@ -228,10 +269,10 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <param name="time">The time to find the control point at.</param> /// <param name="time">The time to find the control point at.</param>
/// <param name="fallback">The control point to use when <paramref name="time"/> is before any control points.</param> /// <param name="fallback">The control point to use when <paramref name="time"/> is before any control points.</param>
/// <returns>The active control point at <paramref name="time"/>, or a fallback <see cref="ControlPoint"/> if none found.</returns> /// <returns>The active control point at <paramref name="time"/>, or a fallback <see cref="ControlPoint"/> if none found.</returns>
private T binarySearchWithFallback<T>(IReadOnlyList<T> list, double time, T fallback) protected T BinarySearchWithFallback<T>(IReadOnlyList<T> list, double time, T fallback)
where T : ControlPoint where T : ControlPoint
{ {
return binarySearch(list, time) ?? fallback; return BinarySearch(list, time) ?? fallback;
} }
/// <summary> /// <summary>
@ -240,7 +281,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <param name="list">The list to search.</param> /// <param name="list">The list to search.</param>
/// <param name="time">The time to find the control point at.</param> /// <param name="time">The time to find the control point at.</param>
/// <returns>The active control point at <paramref name="time"/>.</returns> /// <returns>The active control point at <paramref name="time"/>.</returns>
private T binarySearch<T>(IReadOnlyList<T> list, double time) protected virtual T BinarySearch<T>(IReadOnlyList<T> list, double time)
where T : ControlPoint where T : ControlPoint
{ {
if (list == null) if (list == null)
@ -280,7 +321,7 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <param name="time">The time to find the timing control point at.</param> /// <param name="time">The time to find the timing control point at.</param>
/// <param name="newPoint">A point to be added.</param> /// <param name="newPoint">A point to be added.</param>
/// <returns>Whether the new point should be added.</returns> /// <returns>Whether the new point should be added.</returns>
private bool checkAlreadyExisting(double time, ControlPoint newPoint) protected virtual bool CheckAlreadyExisting(double time, ControlPoint newPoint)
{ {
ControlPoint existing = null; ControlPoint existing = null;
@ -288,17 +329,13 @@ namespace osu.Game.Beatmaps.ControlPoints
{ {
case TimingControlPoint _: case TimingControlPoint _:
// Timing points are a special case and need to be added regardless of fallback availability. // Timing points are a special case and need to be added regardless of fallback availability.
existing = binarySearch(TimingPoints, time); existing = BinarySearch(TimingPoints, time);
break; break;
case EffectControlPoint _: case EffectControlPoint _:
existing = EffectPointAt(time); existing = EffectPointAt(time);
break; break;
case SampleControlPoint _:
existing = binarySearch(SamplePoints, time);
break;
case DifficultyControlPoint _: case DifficultyControlPoint _:
existing = DifficultyPointAt(time); existing = DifficultyPointAt(time);
break; break;
@ -307,7 +344,7 @@ namespace osu.Game.Beatmaps.ControlPoints
return newPoint?.IsRedundant(existing) == true; return newPoint?.IsRedundant(existing) == true;
} }
private void groupItemAdded(ControlPoint controlPoint) protected virtual void GroupItemAdded(ControlPoint controlPoint)
{ {
switch (controlPoint) switch (controlPoint)
{ {
@ -319,17 +356,13 @@ namespace osu.Game.Beatmaps.ControlPoints
effectPoints.Add(typed); effectPoints.Add(typed);
break; break;
case SampleControlPoint typed:
samplePoints.Add(typed);
break;
case DifficultyControlPoint typed: case DifficultyControlPoint typed:
difficultyPoints.Add(typed); difficultyPoints.Add(typed);
break; break;
} }
} }
private void groupItemRemoved(ControlPoint controlPoint) protected virtual void GroupItemRemoved(ControlPoint controlPoint)
{ {
switch (controlPoint) switch (controlPoint)
{ {
@ -341,10 +374,6 @@ namespace osu.Game.Beatmaps.ControlPoints
effectPoints.Remove(typed); effectPoints.Remove(typed);
break; break;
case SampleControlPoint typed:
samplePoints.Remove(typed);
break;
case DifficultyControlPoint typed: case DifficultyControlPoint typed:
difficultyPoints.Remove(typed); difficultyPoints.Remove(typed);
break; break;

View File

@ -44,6 +44,13 @@ namespace osu.Game.Beatmaps.Formats
offset = FormatVersion < 5 ? 24 : 0; offset = FormatVersion < 5 ? 24 : 0;
} }
protected override Beatmap CreateTemplateObject()
{
var templateBeatmap = base.CreateTemplateObject();
templateBeatmap.ControlPointInfo = new LegacyControlPointInfo();
return templateBeatmap;
}
protected override void ParseStreamInto(LineBufferedReader stream, Beatmap beatmap) protected override void ParseStreamInto(LineBufferedReader stream, Beatmap beatmap)
{ {
this.beatmap = beatmap; this.beatmap = beatmap;
@ -394,8 +401,16 @@ namespace osu.Game.Beatmaps.Formats
private readonly HashSet<Type> pendingControlPointTypes = new HashSet<Type>(); private readonly HashSet<Type> pendingControlPointTypes = new HashSet<Type>();
private double pendingControlPointsTime; private double pendingControlPointsTime;
private readonly LegacyControlPointInfo controlPointInfo = new LegacyControlPointInfo();
private void addControlPoint(double time, ControlPoint point, bool timingChange) private void addControlPoint(double time, ControlPoint point, bool timingChange)
{ {
if (point is SampleControlPoint)
{
controlPointInfo.Add(time, point);
return;
}
if (time != pendingControlPointsTime) if (time != pendingControlPointsTime)
flushPendingPoints(); flushPendingPoints();
@ -430,8 +445,15 @@ namespace osu.Game.Beatmaps.Formats
parser ??= new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion); parser ??= new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(getOffsetTime(), FormatVersion);
var obj = parser.Parse(line); var obj = parser.Parse(line);
if (obj != null) if (obj != null)
{
// assign legacy control points directly to hitobject
//obj.SampleControlPoint = controlPointInfo.SamplePointAt(obj.StartTime);
obj.ApplyDefaults(controlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
beatmap.HitObjects.Add(obj); beatmap.HitObjects.Add(obj);
}
} }
private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0); private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0);

View File

@ -80,7 +80,7 @@ namespace osu.Game.Beatmaps.Formats
writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.BeatmapInfo.AudioLeadIn}")); writer.WriteLine(FormattableString.Invariant($"AudioLeadIn: {beatmap.BeatmapInfo.AudioLeadIn}"));
writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}")); writer.WriteLine(FormattableString.Invariant($"PreviewTime: {beatmap.Metadata.PreviewTime}"));
writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.BeatmapInfo.Countdown}")); writer.WriteLine(FormattableString.Invariant($"Countdown: {(int)beatmap.BeatmapInfo.Countdown}"));
writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank(beatmap.ControlPointInfo.SamplePointAt(double.MinValue).SampleBank)}")); writer.WriteLine(FormattableString.Invariant($"SampleSet: {toLegacySampleBank((beatmap.HitObjects.FirstOrDefault()?.SampleControlPoint ?? SampleControlPoint.DEFAULT).SampleBank)}"));
writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}")); writer.WriteLine(FormattableString.Invariant($"StackLeniency: {beatmap.BeatmapInfo.StackLeniency}"));
writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}")); writer.WriteLine(FormattableString.Invariant($"Mode: {beatmap.BeatmapInfo.RulesetID}"));
writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}")); writer.WriteLine(FormattableString.Invariant($"LetterboxInBreaks: {(beatmap.BeatmapInfo.LetterboxInBreaks ? '1' : '0')}"));
@ -187,7 +187,7 @@ namespace osu.Game.Beatmaps.Formats
void outputControlPointEffectsAt(double time, bool isTimingPoint) void outputControlPointEffectsAt(double time, bool isTimingPoint)
{ {
var samplePoint = beatmap.ControlPointInfo.SamplePointAt(time); var samplePoint = ((LegacyControlPointInfo)beatmap.ControlPointInfo).SamplePointAt(time);
var effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); var effectPoint = beatmap.ControlPointInfo.EffectPointAt(time);
// Apply the control point to a hit sample to uncover legacy properties (e.g. suffix) // Apply the control point to a hit sample to uncover legacy properties (e.g. suffix)

View File

@ -106,8 +106,11 @@ namespace osu.Game.Rulesets.Objects
{ {
ApplyDefaultsToSelf(controlPointInfo, difficulty); ApplyDefaultsToSelf(controlPointInfo, difficulty);
// This is done here since ApplyDefaultsToSelf may be used to determine the end time if (controlPointInfo is LegacyControlPointInfo legacyInfo)
SampleControlPoint = controlPointInfo.SamplePointAt(this.GetEndTime() + control_point_leniency); {
// This is done here since ApplyDefaultsToSelf may be used to determine the end time
SampleControlPoint = legacyInfo.SamplePointAt(this.GetEndTime() + control_point_leniency);
}
nestedHitObjects.Clear(); nestedHitObjects.Clear();

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // 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. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -42,15 +43,6 @@ namespace osu.Game.Screens.Edit.Timing
} }
} }
protected override SampleControlPoint CreatePoint() protected override SampleControlPoint CreatePoint() => new SampleControlPoint(); // TODO: remove
{
var reference = Beatmap.ControlPointInfo.SamplePointAt(SelectedGroup.Value.Time);
return new SampleControlPoint
{
SampleBank = reference.SampleBank,
SampleVolume = reference.SampleVolume,
};
}
} }
} }