mirror of
https://github.com/ppy/osu.git
synced 2024-11-13 15:27:30 +08:00
Merge pull request #24119 from peppy/taiko-hitsounding-final-attempt
Implement new argon osu!taiko hitsounds
This commit is contained in:
commit
b9eee29442
@ -72,13 +72,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(200);
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -100,13 +100,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
|
||||
seekTo(200);
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -145,23 +145,23 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is first hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(first));
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
||||
|
||||
seekTo(120);
|
||||
AddAssert("most valid object is first hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(first));
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_NORMAL);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_NORMAL);
|
||||
|
||||
seekTo(480);
|
||||
AddAssert("most valid object is second hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(second));
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
|
||||
seekTo(700);
|
||||
AddAssert("most valid object is second hit", () => triggerSource.GetMostValidObject(), () => Is.EqualTo(second));
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -184,13 +184,13 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is nested strong hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit.StrongNestedHit>);
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(200);
|
||||
AddAssert("most valid object is hit", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Hit>);
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -213,18 +213,18 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -247,18 +247,18 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_SOFT);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_SOFT);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -282,18 +282,18 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
});
|
||||
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is drum roll tick", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRollTick>);
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is drum roll", () => triggerSource.GetMostValidObject(), Is.InstanceOf<DrumRoll>);
|
||||
checkSamples(HitType.Centre, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Centre, true, $"{HitSampleInfo.HIT_NORMAL},{HitSampleInfo.HIT_FINISH}", HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, true, $"{HitSampleInfo.HIT_CLAP},{HitSampleInfo.HIT_WHISTLE}", HitSampleInfo.BANK_DRUM);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -319,18 +319,18 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
// This works fine in gameplay because they are judged whenever the user pressed, rather than being timed hits.
|
||||
// But for sample playback purposes they can be ignored as noise.
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, SampleControlPoint.DEFAULT_BANK);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, SampleControlPoint.DEFAULT_BANK);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -356,23 +356,23 @@ namespace osu.Game.Rulesets.Taiko.Tests
|
||||
// This works fine in gameplay because they are judged whenever the user pressed, rather than being timed hits.
|
||||
// But for sample playback purposes they can be ignored as noise.
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(600);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
||||
|
||||
seekTo(1200);
|
||||
AddAssert("most valid object is swell", () => triggerSource.GetMostValidObject(), Is.InstanceOf<Swell>);
|
||||
checkSamples(HitType.Centre, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Centre, false, HitSampleInfo.HIT_NORMAL, HitSampleInfo.BANK_DRUM);
|
||||
checkSamples(HitType.Rim, false, HitSampleInfo.HIT_CLAP, HitSampleInfo.BANK_DRUM);
|
||||
}
|
||||
|
||||
private void checkSamples(HitType hitType, string expectedSamplesCsv, string expectedBank)
|
||||
private void checkSamples(HitType hitType, bool strong, string expectedSamplesCsv, string expectedBank)
|
||||
{
|
||||
AddStep($"hit {hitType}", () => triggerSource.Play(hitType));
|
||||
AddStep($"hit {hitType}", () => triggerSource.Play(hitType, strong));
|
||||
AddAssert($"last played sample is {expectedSamplesCsv}", () => string.Join(',', triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().Select(s => s.Name)),
|
||||
() => Is.EqualTo(expectedSamplesCsv));
|
||||
AddAssert($"last played sample has {expectedBank} bank", () => triggerSource.LastPlayedSamples!.OfType<HitSampleInfo>().First().Bank, () => Is.EqualTo(expectedBank));
|
||||
|
@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
/// The lenience for the second key press.
|
||||
/// This does not adjust by map difficulty in ScoreV2 yet.
|
||||
/// </summary>
|
||||
private const double second_hit_window = 30;
|
||||
public const double SECOND_HIT_WINDOW = 30;
|
||||
|
||||
public StrongNestedHit()
|
||||
: this(null)
|
||||
@ -223,12 +223,12 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
|
||||
|
||||
if (!userTriggered)
|
||||
{
|
||||
if (timeOffset - ParentHitObject.Result.TimeOffset > second_hit_window)
|
||||
if (timeOffset - ParentHitObject.Result.TimeOffset > SECOND_HIT_WINDOW)
|
||||
ApplyResult(r => r.Type = r.Judgement.MinResult);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Math.Abs(timeOffset - ParentHitObject.Result.TimeOffset) <= second_hit_window)
|
||||
if (Math.Abs(timeOffset - ParentHitObject.Result.TimeOffset) <= SECOND_HIT_WINDOW)
|
||||
ApplyResult(r => r.Type = r.Judgement.MaxResult);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,42 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
||||
{
|
||||
internal partial class ArgonDrumSamplePlayer : DrumSamplePlayer
|
||||
{
|
||||
private ArgonFlourishTriggerSource argonFlourishTrigger = null!;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(Playfield playfield, IPooledSampleProvider sampleProvider)
|
||||
{
|
||||
var hitObjectContainer = playfield.HitObjectContainer;
|
||||
|
||||
// Warm up pools for non-standard samples.
|
||||
sampleProvider.GetPooledSample(new VolumeAwareHitSampleInfo(new HitSampleInfo(HitSampleInfo.HIT_NORMAL), true));
|
||||
sampleProvider.GetPooledSample(new VolumeAwareHitSampleInfo(new HitSampleInfo(HitSampleInfo.HIT_CLAP), true));
|
||||
sampleProvider.GetPooledSample(new VolumeAwareHitSampleInfo(new HitSampleInfo(HitSampleInfo.HIT_FLOURISH), true));
|
||||
|
||||
// We want to play back flourishes in an isolated source as to not have them cancelled.
|
||||
AddInternal(argonFlourishTrigger = new ArgonFlourishTriggerSource(hitObjectContainer));
|
||||
}
|
||||
|
||||
protected override DrumSampleTriggerSource CreateTriggerSource(HitObjectContainer hitObjectContainer, SampleBalance balance) =>
|
||||
new ArgonDrumSampleTriggerSource(hitObjectContainer, balance);
|
||||
|
||||
protected override void Play(DrumSampleTriggerSource triggerSource, HitType hitType, bool strong)
|
||||
{
|
||||
base.Play(triggerSource, hitType, strong);
|
||||
|
||||
// This won't always play something, but the logic for flourish playback is contained within.
|
||||
argonFlourishTrigger.Play(hitType, strong);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
||||
{
|
||||
public partial class ArgonDrumSampleTriggerSource : DrumSampleTriggerSource
|
||||
{
|
||||
[Resolved]
|
||||
private ISkinSource skinSource { get; set; } = null!;
|
||||
|
||||
public ArgonDrumSampleTriggerSource(HitObjectContainer hitObjectContainer, SampleBalance balance)
|
||||
: base(hitObjectContainer, balance)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Play(HitType hitType, bool strong)
|
||||
{
|
||||
TaikoHitObject? hitObject = GetMostValidObject() as TaikoHitObject;
|
||||
|
||||
if (hitObject == null)
|
||||
return;
|
||||
|
||||
var originalSample = hitObject.CreateHitSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL);
|
||||
|
||||
// If the sample is provided by a legacy skin, we should not try and do anything special.
|
||||
if (skinSource.FindProvider(s => s.GetSample(originalSample) != null) is LegacySkinTransformer)
|
||||
{
|
||||
base.Play(hitType, strong);
|
||||
return;
|
||||
}
|
||||
|
||||
// let the magic begin...
|
||||
var samplesToPlay = new List<ISampleInfo> { new VolumeAwareHitSampleInfo(originalSample, strong) };
|
||||
|
||||
PlaySamples(samplesToPlay.ToArray());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,79 @@
|
||||
// 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.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.UI;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
||||
{
|
||||
internal partial class ArgonFlourishTriggerSource : DrumSampleTriggerSource
|
||||
{
|
||||
private readonly HitObjectContainer hitObjectContainer;
|
||||
|
||||
[Resolved]
|
||||
private ISkinSource skinSource { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// The minimum time to leave between flourishes that are added to strong rim hits.
|
||||
/// </summary>
|
||||
private const double time_between_flourishes = 2000;
|
||||
|
||||
public ArgonFlourishTriggerSource(HitObjectContainer hitObjectContainer)
|
||||
: base(hitObjectContainer)
|
||||
{
|
||||
this.hitObjectContainer = hitObjectContainer;
|
||||
}
|
||||
|
||||
public override void Play(HitType hitType, bool strong)
|
||||
{
|
||||
TaikoHitObject? hitObject = GetMostValidObject() as TaikoHitObject;
|
||||
|
||||
if (hitObject == null)
|
||||
return;
|
||||
|
||||
var originalSample = hitObject.CreateHitSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL);
|
||||
|
||||
// If the sample is provided by a legacy skin, we should not try and do anything special.
|
||||
if (skinSource.FindProvider(s => s.GetSample(originalSample) != null) is LegacySkinTransformer)
|
||||
return;
|
||||
|
||||
if (strong && hitType == HitType.Rim && canPlayFlourish(hitObject))
|
||||
PlaySamples(new ISampleInfo[] { new VolumeAwareHitSampleInfo(hitObject.CreateHitSampleInfo(HitSampleInfo.HIT_FLOURISH), true) });
|
||||
}
|
||||
|
||||
private bool canPlayFlourish(TaikoHitObject hitObject)
|
||||
{
|
||||
double? lastFlourish = null;
|
||||
|
||||
var hitObjects = hitObjectContainer.AliveObjects
|
||||
.Reverse()
|
||||
.Select(d => d.HitObject)
|
||||
.OfType<Hit>()
|
||||
.Where(h => h.IsStrong && h.Type == HitType.Rim);
|
||||
|
||||
// Add an additional 'flourish' sample to strong rim hits (that are at least `time_between_flourishes` apart).
|
||||
// This is applied to hitobjects in reverse order, as to sound more musically coherent by biasing towards to
|
||||
// end of groups/combos of strong rim hits instead of the start.
|
||||
foreach (var h in hitObjects)
|
||||
{
|
||||
bool canFlourish = lastFlourish == null || lastFlourish - h.StartTime >= time_between_flourishes;
|
||||
|
||||
if (canFlourish)
|
||||
lastFlourish = h.StartTime;
|
||||
|
||||
// hitObject can be either the strong hit itself (if hit late), or its nested strong object (if hit early)
|
||||
// due to `GetMostValidObject()` idiosyncrasies.
|
||||
// whichever it is, if we encounter it during iteration, stop looking.
|
||||
if (h == hitObject || h.NestedHitObjects.Contains(hitObject))
|
||||
return canFlourish;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
@ -60,6 +60,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
||||
// the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield.
|
||||
return Drawable.Empty().With(d => d.Expire());
|
||||
|
||||
case TaikoSkinComponents.DrumSamplePlayer:
|
||||
return new ArgonDrumSamplePlayer();
|
||||
|
||||
case TaikoSkinComponents.TaikoExplosionGreat:
|
||||
case TaikoSkinComponents.TaikoExplosionMiss:
|
||||
case TaikoSkinComponents.TaikoExplosionOk:
|
||||
|
@ -0,0 +1,52 @@
|
||||
// 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 osu.Game.Audio;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.Skinning.Argon
|
||||
{
|
||||
public class VolumeAwareHitSampleInfo : HitSampleInfo
|
||||
{
|
||||
public const int SAMPLE_VOLUME_THRESHOLD_HARD = 90;
|
||||
public const int SAMPLE_VOLUME_THRESHOLD_MEDIUM = 60;
|
||||
|
||||
public VolumeAwareHitSampleInfo(HitSampleInfo sampleInfo, bool isStrong = false)
|
||||
: base(sampleInfo.Name, isStrong ? BANK_STRONG : getBank(sampleInfo.Bank, sampleInfo.Name, sampleInfo.Volume), sampleInfo.Suffix, sampleInfo.Volume)
|
||||
{
|
||||
}
|
||||
|
||||
public override IEnumerable<string> LookupNames
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (string name in base.LookupNames)
|
||||
yield return name.Insert(name.LastIndexOf('/') + 1, "Argon/taiko-");
|
||||
}
|
||||
}
|
||||
|
||||
private static string getBank(string originalBank, string sampleName, int volume)
|
||||
{
|
||||
// So basically we're overwriting mapper's bank intentions here.
|
||||
// The rationale is that most taiko beatmaps only use a single bank, but regularly adjust volume.
|
||||
|
||||
switch (sampleName)
|
||||
{
|
||||
case HIT_NORMAL:
|
||||
case HIT_CLAP:
|
||||
{
|
||||
if (volume >= SAMPLE_VOLUME_THRESHOLD_HARD)
|
||||
return BANK_DRUM;
|
||||
|
||||
if (volume >= SAMPLE_VOLUME_THRESHOLD_MEDIUM)
|
||||
return BANK_NORMAL;
|
||||
|
||||
return BANK_SOFT;
|
||||
}
|
||||
|
||||
default:
|
||||
return originalBank;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -52,6 +52,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy
|
||||
|
||||
return null;
|
||||
|
||||
case TaikoSkinComponents.DrumSamplePlayer:
|
||||
return null;
|
||||
|
||||
case TaikoSkinComponents.CentreHit:
|
||||
case TaikoSkinComponents.RimHit:
|
||||
if (hasHitCircle)
|
||||
|
@ -21,6 +21,7 @@ namespace osu.Game.Rulesets.Taiko
|
||||
TaikoExplosionKiai,
|
||||
Scroller,
|
||||
Mascot,
|
||||
KiaiGlow
|
||||
KiaiGlow,
|
||||
DrumSamplePlayer
|
||||
}
|
||||
}
|
||||
|
@ -1,57 +1,151 @@
|
||||
// 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 osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.Taiko.Objects.Drawables;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
internal partial class DrumSamplePlayer : CompositeDrawable, IKeyBindingHandler<TaikoAction>
|
||||
{
|
||||
private readonly DrumSampleTriggerSource leftRimSampleTriggerSource;
|
||||
private readonly DrumSampleTriggerSource leftCentreSampleTriggerSource;
|
||||
private readonly DrumSampleTriggerSource rightCentreSampleTriggerSource;
|
||||
private readonly DrumSampleTriggerSource rightRimSampleTriggerSource;
|
||||
private DrumSampleTriggerSource leftCentreTrigger = null!;
|
||||
private DrumSampleTriggerSource rightCentreTrigger = null!;
|
||||
private DrumSampleTriggerSource leftRimTrigger = null!;
|
||||
private DrumSampleTriggerSource rightRimTrigger = null!;
|
||||
private DrumSampleTriggerSource strongCentreTrigger = null!;
|
||||
private DrumSampleTriggerSource strongRimTrigger = null!;
|
||||
|
||||
public DrumSamplePlayer(HitObjectContainer hitObjectContainer)
|
||||
private double lastHitTime;
|
||||
private TaikoAction? lastAction;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(Playfield playfield)
|
||||
{
|
||||
var hitObjectContainer = playfield.HitObjectContainer;
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
leftRimSampleTriggerSource = new DrumSampleTriggerSource(hitObjectContainer),
|
||||
leftCentreSampleTriggerSource = new DrumSampleTriggerSource(hitObjectContainer),
|
||||
rightCentreSampleTriggerSource = new DrumSampleTriggerSource(hitObjectContainer),
|
||||
rightRimSampleTriggerSource = new DrumSampleTriggerSource(hitObjectContainer),
|
||||
leftCentreTrigger = CreateTriggerSource(hitObjectContainer, SampleBalance.Left),
|
||||
rightCentreTrigger = CreateTriggerSource(hitObjectContainer, SampleBalance.Right),
|
||||
leftRimTrigger = CreateTriggerSource(hitObjectContainer, SampleBalance.Left),
|
||||
rightRimTrigger = CreateTriggerSource(hitObjectContainer, SampleBalance.Right),
|
||||
strongCentreTrigger = CreateTriggerSource(hitObjectContainer, SampleBalance.Centre),
|
||||
strongRimTrigger = CreateTriggerSource(hitObjectContainer, SampleBalance.Centre)
|
||||
};
|
||||
}
|
||||
|
||||
protected virtual DrumSampleTriggerSource CreateTriggerSource(HitObjectContainer hitObjectContainer, SampleBalance balance)
|
||||
=> new DrumSampleTriggerSource(hitObjectContainer);
|
||||
|
||||
public bool OnPressed(KeyBindingPressEvent<TaikoAction> e)
|
||||
{
|
||||
if ((Clock as IGameplayClock)?.IsRewinding == true)
|
||||
return false;
|
||||
|
||||
HitType hitType;
|
||||
|
||||
DrumSampleTriggerSource triggerSource;
|
||||
|
||||
bool strong = checkStrongValidity(e.Action, lastAction, Time.Current - lastHitTime);
|
||||
|
||||
switch (e.Action)
|
||||
{
|
||||
case TaikoAction.LeftRim:
|
||||
leftRimSampleTriggerSource.Play(HitType.Rim);
|
||||
break;
|
||||
|
||||
case TaikoAction.LeftCentre:
|
||||
leftCentreSampleTriggerSource.Play(HitType.Centre);
|
||||
hitType = HitType.Centre;
|
||||
triggerSource = strong ? strongCentreTrigger : leftCentreTrigger;
|
||||
break;
|
||||
|
||||
case TaikoAction.RightCentre:
|
||||
rightCentreSampleTriggerSource.Play(HitType.Centre);
|
||||
hitType = HitType.Centre;
|
||||
triggerSource = strong ? strongCentreTrigger : rightCentreTrigger;
|
||||
break;
|
||||
|
||||
case TaikoAction.LeftRim:
|
||||
hitType = HitType.Rim;
|
||||
triggerSource = strong ? strongRimTrigger : leftRimTrigger;
|
||||
break;
|
||||
|
||||
case TaikoAction.RightRim:
|
||||
rightRimSampleTriggerSource.Play(HitType.Rim);
|
||||
hitType = HitType.Rim;
|
||||
triggerSource = strong ? strongRimTrigger : rightRimTrigger;
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strong)
|
||||
{
|
||||
switch (hitType)
|
||||
{
|
||||
case HitType.Centre:
|
||||
flushCenterTriggerSources();
|
||||
break;
|
||||
|
||||
case HitType.Rim:
|
||||
flushRimTriggerSources();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Play(triggerSource, hitType, strong);
|
||||
|
||||
lastHitTime = Time.Current;
|
||||
lastAction = e.Action;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
protected virtual void Play(DrumSampleTriggerSource triggerSource, HitType hitType, bool strong) =>
|
||||
triggerSource.Play(hitType, strong);
|
||||
|
||||
private bool checkStrongValidity(TaikoAction newAction, TaikoAction? lastAction, double timeBetweenActions)
|
||||
{
|
||||
if (lastAction == null)
|
||||
return false;
|
||||
|
||||
if (timeBetweenActions < 0 || timeBetweenActions > DrawableHit.StrongNestedHit.SECOND_HIT_WINDOW)
|
||||
return false;
|
||||
|
||||
switch (newAction)
|
||||
{
|
||||
case TaikoAction.LeftCentre:
|
||||
return lastAction == TaikoAction.RightCentre;
|
||||
|
||||
case TaikoAction.RightCentre:
|
||||
return lastAction == TaikoAction.LeftCentre;
|
||||
|
||||
case TaikoAction.LeftRim:
|
||||
return lastAction == TaikoAction.RightRim;
|
||||
|
||||
case TaikoAction.RightRim:
|
||||
return lastAction == TaikoAction.LeftRim;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void flushCenterTriggerSources()
|
||||
{
|
||||
leftCentreTrigger.StopAllPlayback();
|
||||
rightCentreTrigger.StopAllPlayback();
|
||||
strongCentreTrigger.StopAllPlayback();
|
||||
}
|
||||
|
||||
private void flushRimTriggerSources()
|
||||
{
|
||||
leftRimTrigger.StopAllPlayback();
|
||||
rightRimTrigger.StopAllPlayback();
|
||||
strongRimTrigger.StopAllPlayback();
|
||||
}
|
||||
|
||||
public void OnReleased(KeyBindingReleaseEvent<TaikoAction> e)
|
||||
{
|
||||
}
|
||||
|
@ -2,20 +2,38 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Rulesets.Taiko.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Rulesets.Taiko.UI
|
||||
{
|
||||
public partial class DrumSampleTriggerSource : GameplaySampleTriggerSource
|
||||
{
|
||||
public DrumSampleTriggerSource(HitObjectContainer hitObjectContainer)
|
||||
private const double stereo_separation = 0.2;
|
||||
|
||||
public DrumSampleTriggerSource(HitObjectContainer hitObjectContainer, SampleBalance balance = SampleBalance.Centre)
|
||||
: base(hitObjectContainer)
|
||||
{
|
||||
switch (balance)
|
||||
{
|
||||
case SampleBalance.Left:
|
||||
AudioContainer.Balance.Value = -stereo_separation;
|
||||
break;
|
||||
|
||||
case SampleBalance.Centre:
|
||||
AudioContainer.Balance.Value = 0;
|
||||
break;
|
||||
|
||||
case SampleBalance.Right:
|
||||
AudioContainer.Balance.Value = stereo_separation;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void Play(HitType hitType)
|
||||
public virtual void Play(HitType hitType, bool strong)
|
||||
{
|
||||
TaikoHitObject? hitObject = GetMostValidObject() as TaikoHitObject;
|
||||
|
||||
@ -24,7 +42,7 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
|
||||
var baseSample = hitObject.CreateHitSampleInfo(hitType == HitType.Rim ? HitSampleInfo.HIT_CLAP : HitSampleInfo.HIT_NORMAL);
|
||||
|
||||
if ((hitObject as TaikoStrongableHitObject)?.IsStrong == true || hitObject is StrongNestedHitObject)
|
||||
if (strong)
|
||||
{
|
||||
PlaySamples(new ISampleInfo[]
|
||||
{
|
||||
@ -39,5 +57,19 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
}
|
||||
|
||||
public override void Play() => throw new InvalidOperationException(@"Use override with HitType parameter instead");
|
||||
|
||||
protected override void ApplySampleInfo(SkinnableSound hitSound, ISampleInfo[] samples)
|
||||
{
|
||||
base.ApplySampleInfo(hitSound, samples);
|
||||
|
||||
hitSound.Balance.Value = -0.05 + RNG.NextDouble(0.1);
|
||||
}
|
||||
}
|
||||
|
||||
public enum SampleBalance
|
||||
{
|
||||
Left,
|
||||
Centre,
|
||||
Right
|
||||
}
|
||||
}
|
||||
|
@ -170,7 +170,10 @@ namespace osu.Game.Rulesets.Taiko.UI
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
drumRollHitContainer.CreateProxy(),
|
||||
new DrumSamplePlayer(HitObjectContainer),
|
||||
new SkinnableDrawable(new TaikoSkinComponentLookup(TaikoSkinComponents.DrumSamplePlayer), _ => new DrumSamplePlayer())
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
},
|
||||
// this is added at the end of the hierarchy to receive input before taiko objects.
|
||||
// but is proxied below everything to not cover visual effects such as hit explosions.
|
||||
inputDrum,
|
||||
|
@ -205,7 +205,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
|
||||
private void checkValidObjectIndex(int index) =>
|
||||
AddAssert($"check valid object is {index}", () => sampleTriggerSource.GetMostValidObject(), () => Is.EqualTo(beatmap.HitObjects[index]));
|
||||
AddAssert($"check object at index {index} is correct", () => sampleTriggerSource.GetMostValidObject(), () => Is.EqualTo(beatmap.HitObjects[index]));
|
||||
|
||||
private DrawableHitObject? getNextAliveObject() =>
|
||||
Player.DrawableRuleset.Playfield.HitObjectContainer.AliveObjects.FirstOrDefault();
|
||||
|
@ -24,6 +24,12 @@ namespace osu.Game.Audio
|
||||
public const string BANK_SOFT = @"soft";
|
||||
public const string BANK_DRUM = @"drum";
|
||||
|
||||
// new sample used exclusively by taiko for now.
|
||||
public const string HIT_FLOURISH = "hitflourish";
|
||||
|
||||
// new bank used exclusively by taiko for now.
|
||||
public const string BANK_STRONG = @"strong";
|
||||
|
||||
/// <summary>
|
||||
/// All valid sample addition constants.
|
||||
/// </summary>
|
||||
|
@ -34,14 +34,19 @@ namespace osu.Game.Rulesets.UI
|
||||
[Resolved]
|
||||
private IGameplayClock? gameplayClock { get; set; }
|
||||
|
||||
protected readonly AudioContainer AudioContainer;
|
||||
|
||||
public GameplaySampleTriggerSource(HitObjectContainer hitObjectContainer)
|
||||
{
|
||||
this.hitObjectContainer = hitObjectContainer;
|
||||
|
||||
InternalChild = hitSounds = new Container<SkinnableSound>
|
||||
InternalChild = AudioContainer = new AudioContainer
|
||||
{
|
||||
Name = "concurrent sample pool",
|
||||
ChildrenEnumerable = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new PausableSkinnableSound())
|
||||
Child = hitSounds = new Container<SkinnableSound>
|
||||
{
|
||||
Name = "concurrent sample pool",
|
||||
ChildrenEnumerable = Enumerable.Range(0, max_concurrent_hitsounds).Select(_ => new PausableSkinnableSound())
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -64,11 +69,22 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
protected virtual void PlaySamples(ISampleInfo[] samples) => Schedule(() =>
|
||||
{
|
||||
var hitSound = getNextSample();
|
||||
hitSound.Samples = samples;
|
||||
var hitSound = GetNextSample();
|
||||
ApplySampleInfo(hitSound, samples);
|
||||
hitSound.Play();
|
||||
});
|
||||
|
||||
protected virtual void ApplySampleInfo(SkinnableSound hitSound, ISampleInfo[] samples)
|
||||
{
|
||||
hitSound.Samples = samples;
|
||||
}
|
||||
|
||||
public void StopAllPlayback() => Schedule(() =>
|
||||
{
|
||||
foreach (var sound in hitSounds)
|
||||
sound.Stop();
|
||||
});
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -118,7 +134,7 @@ namespace osu.Game.Rulesets.UI
|
||||
return getAllNested(mostValidObject.HitObject).OrderBy(h => h.GetEndTime()).SkipWhile(h => h.GetEndTime() <= getReferenceTime()).FirstOrDefault() ?? mostValidObject.HitObject;
|
||||
}
|
||||
|
||||
private bool isAlreadyHit(HitObjectLifetimeEntry h) => h.Result?.HasResult == true;
|
||||
private bool isAlreadyHit(HitObjectLifetimeEntry h) => h.AllJudged;
|
||||
private bool isCloseEnoughToCurrentTime(HitObject h) => getReferenceTime() >= h.StartTime - h.HitWindows.WindowFor(HitResult.Miss) * 2;
|
||||
|
||||
private double getReferenceTime() => gameplayClock?.CurrentTime ?? Clock.CurrentTime;
|
||||
@ -134,7 +150,7 @@ namespace osu.Game.Rulesets.UI
|
||||
}
|
||||
}
|
||||
|
||||
private SkinnableSound getNextSample()
|
||||
protected SkinnableSound GetNextSample()
|
||||
{
|
||||
SkinnableSound hitSound = hitSounds[nextHitSoundIndex];
|
||||
|
||||
|
@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
[Cached(typeof(IPooledHitObjectProvider))]
|
||||
[Cached(typeof(IPooledSampleProvider))]
|
||||
[Cached]
|
||||
public abstract partial class Playfield : CompositeDrawable, IPooledHitObjectProvider, IPooledSampleProvider
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -8,7 +8,7 @@ namespace osu.Game.Skinning
|
||||
/// <summary>
|
||||
/// Provides pooled samples to be used by <see cref="SkinnableSound"/>s.
|
||||
/// </summary>
|
||||
internal interface IPooledSampleProvider
|
||||
public interface IPooledSampleProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Retrieves a <see cref="PoolableSkinnableSample"/> from a pool.
|
||||
|
Loading…
Reference in New Issue
Block a user