1
0
mirror of https://github.com/ppy/osu.git synced 2025-02-21 03:02:54 +08:00

Merge pull request #28619 from bdach/fix-tail-volume-not-saving

Fix slider tail volume not saving
This commit is contained in:
Dan Balasescu 2024-07-03 16:13:18 +09:00 committed by GitHub
commit 537e3a1642
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 69 additions and 14 deletions

View File

@ -528,8 +528,17 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.AreEqual("Gameplay/normal-hitnormal2", getTestableSampleInfo(hitObjects[2]).LookupNames.First());
Assert.AreEqual("Gameplay/normal-hitnormal", getTestableSampleInfo(hitObjects[3]).LookupNames.First());
// The control point at the end time of the slider should be applied
Assert.AreEqual("Gameplay/soft-hitnormal8", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
// The fourth object is a slider.
// `Samples` of a slider are presumed to control the volume of sounds that last the entire duration of the slider
// (such as ticks, slider slide sounds, etc.)
// Thus, the point of query of control points used for `Samples` is just beyond the start time of the slider.
Assert.AreEqual("Gameplay/soft-hitnormal11", getTestableSampleInfo(hitObjects[4]).LookupNames.First());
// That said, the `NodeSamples` of the slider are responsible for the sounds of the slider's head / tail / repeats / large ticks etc.
// Therefore, they should be read at the time instant correspondent to the given node.
// This means that the tail should use bank 8 rather than 11.
Assert.AreEqual("Gameplay/soft-hitnormal11", ((ConvertSlider)hitObjects[4]).NodeSamples[0][0].LookupNames.First());
Assert.AreEqual("Gameplay/soft-hitnormal8", ((ConvertSlider)hitObjects[4]).NodeSamples[1][0].LookupNames.First());
}
static HitSampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.Samples[0];

View File

@ -0,0 +1,22 @@
osu file format v128
[General]
SampleSet: Normal
[TimingPoints]
15,1000,4,1,0,100,1,0
2271,-100,4,1,0,5,0,0
6021,-100,4,1,0,100,0,0
8515,-100,4,1,0,5,0,0
12765,-100,4,1,0,100,0,0
14764,-100,4,1,0,5,0,0
14770,-100,4,1,0,50,0,0
17264,-100,4,1,0,5,0,0
17270,-100,4,1,0,50,0,0
22264,-100,4,1,0,100,0,0
[HitObjects]
113,54,2265,6,0,L|422:55,1,300,0|0,1:0|1:0,1:0:0:0:
82,206,6015,2,0,L|457:204,1,350,0|0,2:0|2:0,2:0:0:0:
75,310,10265,2,0,L|435:312,1,350,0|0,3:0|3:0,3:0:0:0:
75,310,14764,2,0,L|435:312,3,350,0|0|0|0,3:0|3:0|3:0|3:0,3:0:0:0:

View File

@ -32,7 +32,7 @@ namespace osu.Game.Beatmaps.Formats
/// <remarks>
/// Compare: https://github.com/peppy/osu-stable-reference/blob/master/osu!/GameplayElements/HitObjects/HitObject.cs#L319
/// </remarks>
private const double control_point_leniency = 5;
public const double CONTROL_POINT_LENIENCY = 5;
internal static RulesetStore? RulesetStore;
@ -159,20 +159,24 @@ namespace osu.Game.Beatmaps.Formats
private void applySamples(HitObject hitObject)
{
SampleControlPoint sampleControlPoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(hitObject.GetEndTime() + control_point_leniency) ?? SampleControlPoint.DEFAULT;
hitObject.Samples = hitObject.Samples.Select(o => sampleControlPoint.ApplyTo(o)).ToList();
if (hitObject is IHasRepeats hasRepeats)
{
SampleControlPoint sampleControlPoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(hitObject.StartTime + CONTROL_POINT_LENIENCY + 1) ?? SampleControlPoint.DEFAULT;
hitObject.Samples = hitObject.Samples.Select(o => sampleControlPoint.ApplyTo(o)).ToList();
for (int i = 0; i < hasRepeats.NodeSamples.Count; i++)
{
double time = hitObject.StartTime + i * hasRepeats.Duration / hasRepeats.SpanCount() + control_point_leniency;
double time = hitObject.StartTime + i * hasRepeats.Duration / hasRepeats.SpanCount() + CONTROL_POINT_LENIENCY;
var nodeSamplePoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(time) ?? SampleControlPoint.DEFAULT;
hasRepeats.NodeSamples[i] = hasRepeats.NodeSamples[i].Select(o => nodeSamplePoint.ApplyTo(o)).ToList();
}
}
else
{
SampleControlPoint sampleControlPoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.SamplePointAt(hitObject.GetEndTime() + CONTROL_POINT_LENIENCY) ?? SampleControlPoint.DEFAULT;
hitObject.Samples = hitObject.Samples.Select(o => sampleControlPoint.ApplyTo(o)).ToList();
}
}
/// <summary>

View File

@ -282,19 +282,39 @@ namespace osu.Game.Beatmaps.Formats
{
foreach (var hitObject in hitObjects)
{
if (hitObject.Samples.Count > 0)
if (hitObject is IHasRepeats hasNodeSamples)
{
int volume = hitObject.Samples.Max(o => o.Volume);
int customIndex = hitObject.Samples.Any(o => o is ConvertHitObjectParser.LegacyHitSampleInfo)
? hitObject.Samples.OfType<ConvertHitObjectParser.LegacyHitSampleInfo>().Max(o => o.CustomSampleBank)
: -1;
double spanDuration = hasNodeSamples.Duration / hasNodeSamples.SpanCount();
yield return new LegacyBeatmapDecoder.LegacySampleControlPoint { Time = hitObject.GetEndTime(), SampleVolume = volume, CustomSampleBank = customIndex };
for (int i = 0; i < hasNodeSamples.NodeSamples.Count; ++i)
{
double nodeTime = hitObject.StartTime + i * spanDuration;
if (hasNodeSamples.NodeSamples[i].Count > 0)
yield return createSampleControlPointFor(nodeTime, hasNodeSamples.NodeSamples[i]);
if (spanDuration > LegacyBeatmapDecoder.CONTROL_POINT_LENIENCY + 1 && hitObject.Samples.Count > 0 && i < hasNodeSamples.NodeSamples.Count - 1)
yield return createSampleControlPointFor(nodeTime + LegacyBeatmapDecoder.CONTROL_POINT_LENIENCY + 1, hitObject.Samples);
}
}
else if (hitObject.Samples.Count > 0)
{
yield return createSampleControlPointFor(hitObject.GetEndTime(), hitObject.Samples);
}
foreach (var nested in collectSampleControlPoints(hitObject.NestedHitObjects))
yield return nested;
}
SampleControlPoint createSampleControlPointFor(double time, IList<HitSampleInfo> samples)
{
int volume = samples.Max(o => o.Volume);
int customIndex = samples.Any(o => o is ConvertHitObjectParser.LegacyHitSampleInfo)
? samples.OfType<ConvertHitObjectParser.LegacyHitSampleInfo>().Max(o => o.CustomSampleBank)
: -1;
return new LegacyBeatmapDecoder.LegacySampleControlPoint { Time = time, SampleVolume = volume, CustomSampleBank = customIndex };
}
}
void extractSampleControlPoints(IEnumerable<HitObject> hitObject)