mirror of
https://github.com/ppy/osu
synced 2024-12-28 09:52:56 +00:00
Merge pull request #23618 from bdach/fix-taiko-swells-drumrolls-silent
Fix hitsounds not playing before and during taiko drum rolls and swells
This commit is contained in:
commit
7c039b0b67
@ -0,0 +1,337 @@
|
||||
// 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.Collections.Generic;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Rulesets.UI.Scrolling;
|
||||
using osu.Game.Tests.Visual;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Tests
|
||||
{
|
||||
public partial class TestSceneDrumSampleTriggerSource : OsuTestScene
|
||||
{
|
||||
private readonly ManualClock manualClock = new ManualClock();
|
||||
|
||||
[Cached(typeof(IScrollingInfo))]
|
||||
private ScrollingTestContainer.TestScrollingInfo info = new ScrollingTestContainer.TestScrollingInfo
|
||||
{
|
||||
Direction = { Value = ScrollingDirection.Left },
|
||||
TimeRange = { Value = 200 },
|
||||
};
|
||||
|
||||
private ScrollingHitObjectContainer hitObjectContainer = null!;
|
||||
private TestDrumSampleTriggerSource triggerSource = null!;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
{
|
||||
hitObjectContainer = new ScrollingHitObjectContainer();
|
||||
manualClock.CurrentTime = 0;
|
||||
|
||||
Child = new Container
|
||||
{
|
||||
Clock = new FramedClock(manualClock),
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Children = new Drawable[]
|
||||
{
|
||||
hitObjectContainer,
|
||||
triggerSource = new TestDrumSampleTriggerSource(hitObjectContainer)
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
[Test]
|
||||
public void TestNormalHit()
|
||||
{
|
||||
AddStep("add hit with normal samples", () =>
|
||||
{
|
||||
var hit = new Hit
|
||||
{
|
||||
StartTime = 100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
||||
}
|
||||
};
|
||||
hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
var drawableHit = new DrawableHit(hit);
|
||||
hitObjectContainer.Add(drawableHit);
|
||||
});
|
||||
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
AddStep("seek past hit", () => manualClock.CurrentTime = 200);
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSoftHit()
|
||||
{
|
||||
AddStep("add hit with soft samples", () =>
|
||||
{
|
||||
var hit = new Hit
|
||||
{
|
||||
StartTime = 100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "soft")
|
||||
}
|
||||
};
|
||||
hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
var drawableHit = new DrawableHit(hit);
|
||||
hitObjectContainer.Add(drawableHit);
|
||||
});
|
||||
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft");
|
||||
|
||||
AddStep("seek past hit", () => manualClock.CurrentTime = 200);
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDrumStrongHit()
|
||||
{
|
||||
AddStep("add strong hit with drum samples", () =>
|
||||
{
|
||||
var hit = new Hit
|
||||
{
|
||||
StartTime = 100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "drum"),
|
||||
new HitSampleInfo(HitSampleInfo.HIT_FINISH, "drum") // implies strong
|
||||
}
|
||||
};
|
||||
hit.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
var drawableHit = new DrawableHit(hit);
|
||||
hitObjectContainer.Add(drawableHit);
|
||||
});
|
||||
|
||||
AddAssert("most valid object is strong nested hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit.StrongNestedHit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
|
||||
AddStep("seek past hit", () => manualClock.CurrentTime = 200);
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalDrumRoll()
|
||||
{
|
||||
AddStep("add drum roll with normal samples", () =>
|
||||
{
|
||||
var drumRoll = new DrumRoll
|
||||
{
|
||||
StartTime = 100,
|
||||
EndTime = 1100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
||||
}
|
||||
};
|
||||
drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
var drawableDrumRoll = new DrawableDrumRoll(drumRoll);
|
||||
hitObjectContainer.Add(drawableDrumRoll);
|
||||
});
|
||||
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
AddStep("seek to middle of drum roll", () => manualClock.CurrentTime = 600);
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
AddStep("seek past drum roll", () => manualClock.CurrentTime = 1200);
|
||||
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestSoftDrumRoll()
|
||||
{
|
||||
AddStep("add drum roll with soft samples", () =>
|
||||
{
|
||||
var drumRoll = new DrumRoll
|
||||
{
|
||||
StartTime = 100,
|
||||
EndTime = 1100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "soft")
|
||||
}
|
||||
};
|
||||
drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
var drawableDrumRoll = new DrawableDrumRoll(drumRoll);
|
||||
hitObjectContainer.Add(drawableDrumRoll);
|
||||
});
|
||||
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft");
|
||||
|
||||
AddStep("seek to middle of drum roll", () => manualClock.CurrentTime = 600);
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft");
|
||||
|
||||
AddStep("seek past drum roll", () => manualClock.CurrentTime = 1200);
|
||||
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "soft");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "soft");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDrumStrongDrumRoll()
|
||||
{
|
||||
AddStep("add strong drum roll with drum samples", () =>
|
||||
{
|
||||
var drumRoll = new DrumRoll
|
||||
{
|
||||
StartTime = 100,
|
||||
EndTime = 1100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "drum"),
|
||||
new HitSampleInfo(HitSampleInfo.HIT_FINISH, "drum") // implies strong
|
||||
}
|
||||
};
|
||||
drumRoll.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
var drawableDrumRoll = new DrawableDrumRoll(drumRoll);
|
||||
hitObjectContainer.Add(drawableDrumRoll);
|
||||
});
|
||||
|
||||
AddAssert("most valid object is drum roll tick's nested strong hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick.StrongNestedHit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
|
||||
AddStep("seek to middle of drum roll", () => manualClock.CurrentTime = 600);
|
||||
AddAssert("most valid object is drum roll tick's nested strong hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick.StrongNestedHit>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
|
||||
AddStep("seek past drum roll", () => manualClock.CurrentTime = 1200);
|
||||
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestNormalSwell()
|
||||
{
|
||||
AddStep("add swell with normal samples", () =>
|
||||
{
|
||||
var swell = new Swell
|
||||
{
|
||||
StartTime = 100,
|
||||
EndTime = 1100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
|
||||
}
|
||||
};
|
||||
swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
var drawableSwell = new DrawableSwell(swell);
|
||||
hitObjectContainer.Add(drawableSwell);
|
||||
});
|
||||
|
||||
AddAssert("most valid object is swell tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<SwellTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
AddStep("seek to middle of swell", () => manualClock.CurrentTime = 600);
|
||||
AddAssert("most valid object is swell tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<SwellTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
AddStep("seek past swell", () => manualClock.CurrentTime = 1200);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDrumSwell()
|
||||
{
|
||||
AddStep("add swell with drum samples", () =>
|
||||
{
|
||||
var swell = new Swell
|
||||
{
|
||||
StartTime = 100,
|
||||
EndTime = 1100,
|
||||
Samples = new List<HitSampleInfo>
|
||||
{
|
||||
new HitSampleInfo(HitSampleInfo.HIT_NORMAL, "drum")
|
||||
}
|
||||
};
|
||||
swell.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
|
||||
var drawableSwell = new DrawableSwell(swell);
|
||||
hitObjectContainer.Add(drawableSwell);
|
||||
});
|
||||
|
||||
AddAssert("most valid object is swell tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<SwellTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
|
||||
AddStep("seek to middle of swell", () => manualClock.CurrentTime = 600);
|
||||
AddAssert("most valid object is swell tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<SwellTick>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
|
||||
AddStep("seek past swell", () => manualClock.CurrentTime = 1200);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSound(HitType.Centre, HitSampleInfo.HIT_NORMAL, "drum");
|
||||
checkSound(HitType.Rim, HitSampleInfo.HIT_CLAP, "drum");
|
||||
}
|
||||
|
||||
private void checkSound(HitType hitType, string expectedName, string expectedBank)
|
||||
{
|
||||
AddStep($"hit {hitType}", () => triggerSource.Play(hitType));
|
||||
AddAssert($"last played sample is {expectedName}", () => triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().Single().Name, () => Is.EqualTo(expectedName));
|
||||
AddAssert($"last played sample has {expectedBank} bank", () => triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().Single().Bank, () => Is.EqualTo(expectedBank));
|
||||
}
|
||||
|
||||
private partial class TestDrumSampleTriggerSource : DrumSampleTriggerSource
|
||||
{
|
||||
public ISampleInfo[]? LastPlayedSamples { get; private set; }
|
||||
|
||||
public TestDrumSampleTriggerSource(HitObjectContainer hitObjectContainer)
|
||||
: base(hitObjectContainer)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void PlaySamples(ISampleInfo[] samples)
|
||||
{
|
||||
base.PlaySamples(samples);
|
||||
LastPlayedSamples = samples;
|
||||
}
|
||||
|
||||
public new HitObject GetMostValidObject() => base.GetMostValidObject();
|
||||
}
|
||||
}
|
||||
}
|
@ -118,6 +118,9 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
{
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
}
|
||||
|
||||
// Most osu!taiko hitsounds are managed by the drum (see DrumSampleTriggerSource).
|
||||
public override IEnumerable<HitSampleInfo> GetSamples() => Enumerable.Empty<HitSampleInfo>();
|
||||
}
|
||||
|
||||
public abstract partial class DrawableTaikoHitObject<TObject> : DrawableTaikoHitObject
|
||||
@ -157,9 +160,6 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
Content.Add(MainPiece = CreateMainPiece());
|
||||
}
|
||||
|
||||
// Most osu!taiko hitsounds are managed by the drum (see DrumSampleMapping).
|
||||
public override IEnumerable<HitSampleInfo> GetSamples() => Enumerable.Empty<HitSampleInfo>();
|
||||
|
||||
protected abstract SkinnableDrawable CreateMainPiece();
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,9 @@
|
||||
|
||||
#nullable disable
|
||||
|
||||
using System.Linq;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using System.Threading;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
@ -98,7 +96,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
TickSpacing = tickSpacing,
|
||||
StartTime = t,
|
||||
IsStrong = IsStrong,
|
||||
Samples = Samples.Where(s => s.Name == HitSampleInfo.HIT_FINISH).ToList()
|
||||
Samples = Samples
|
||||
});
|
||||
|
||||
first = false;
|
||||
@ -109,7 +107,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
|
||||
protected override HitWindows CreateHitWindows() => HitWindows.Empty;
|
||||
|
||||
protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime };
|
||||
protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit
|
||||
{
|
||||
StartTime = startTime,
|
||||
Samples = Samples
|
||||
};
|
||||
|
||||
public class StrongNestedHit : StrongNestedHitObject
|
||||
{
|
||||
|
@ -33,7 +33,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
|
||||
public override double MaximumJudgementOffset => HitWindow;
|
||||
|
||||
protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime };
|
||||
protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit
|
||||
{
|
||||
StartTime = startTime,
|
||||
Samples = Samples
|
||||
};
|
||||
|
||||
public class StrongNestedHit : StrongNestedHitObject
|
||||
{
|
||||
|
@ -72,7 +72,11 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
}
|
||||
}
|
||||
|
||||
protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit { StartTime = startTime };
|
||||
protected override StrongNestedHitObject CreateStrongNestedHit(double startTime) => new StrongNestedHit
|
||||
{
|
||||
StartTime = startTime,
|
||||
Samples = Samples
|
||||
};
|
||||
|
||||
public class StrongNestedHit : StrongNestedHitObject
|
||||
{
|
||||
|
@ -33,7 +33,10 @@ namespace osu.Game.Rulesets.Taiko.Objects
|
||||
for (int i = 0; i < RequiredHits; i++)
|
||||
{
|
||||
cancellationToken.ThrowIfCancellationRequested();
|
||||
AddNested(new SwellTick());
|
||||
AddNested(new SwellTick
|
||||
{
|
||||
Samples = Samples
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.UI
|
||||
PlaySamples(samples);
|
||||
}
|
||||
|
||||
protected void PlaySamples(ISampleInfo[] samples) => Schedule(() =>
|
||||
protected virtual void PlaySamples(ISampleInfo[] samples) => Schedule(() =>
|
||||
{
|
||||
var hitSound = getNextSample();
|
||||
hitSound.Samples = samples;
|
||||
|
Loading…
Reference in New Issue
Block a user