From 9bdc80a74934b781216ff924d5ccca79068e467e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 7 Jul 2023 14:46:36 +0900 Subject: [PATCH] Move flourish playback to own trigger source --- .../Skinning/Argon/ArgonDrumSamplePlayer.cs | 18 ++++- .../Argon/ArgonDrumSampleTriggerSource.cs | 39 ---------- .../Argon/ArgonFlourishTriggerSource.cs | 76 +++++++++++++++++++ .../UI/DrumSamplePlayer.cs | 5 +- 4 files changed, 97 insertions(+), 41 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonFlourishTriggerSource.cs diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonDrumSamplePlayer.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonDrumSamplePlayer.cs index 898753b568..2ff36ef9bf 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonDrumSamplePlayer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonDrumSamplePlayer.cs @@ -3,6 +3,7 @@ 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; @@ -11,16 +12,31 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon { internal partial class ArgonDrumSamplePlayer : DrumSamplePlayer { + private ArgonFlourishTriggerSource argonFlourishTrigger = null!; + [BackgroundDependencyLoader] - private void load(IPooledSampleProvider sampleProvider) + 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); + } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonDrumSampleTriggerSource.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonDrumSampleTriggerSource.cs index 958d4e3aec..fb4c1067e3 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonDrumSampleTriggerSource.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonDrumSampleTriggerSource.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Game.Audio; using osu.Game.Rulesets.Taiko.Objects; @@ -14,20 +13,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon { public partial class ArgonDrumSampleTriggerSource : DrumSampleTriggerSource { - private readonly HitObjectContainer hitObjectContainer; - [Resolved] private ISkinSource skinSource { get; set; } = null!; - /// - /// The minimum time to leave between flourishes that are added to strong rim hits. - /// - private const double time_between_flourishes = 2000; - public ArgonDrumSampleTriggerSource(HitObjectContainer hitObjectContainer, SampleBalance balance) : base(hitObjectContainer, balance) { - this.hitObjectContainer = hitObjectContainer; } public override void Play(HitType hitType, bool strong) @@ -49,37 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Argon // let the magic begin... var samplesToPlay = new List { new VolumeAwareHitSampleInfo(originalSample, strong) }; - if (strong && hitType == HitType.Rim && canPlayFlourish(hitObject)) - samplesToPlay.Add(new VolumeAwareHitSampleInfo(hitObject.CreateHitSampleInfo(HitSampleInfo.HIT_FLOURISH), true)); - PlaySamples(samplesToPlay.ToArray()); } - - private bool canPlayFlourish(TaikoHitObject hitObject) - { - double? lastFlourish = null; - - var hitObjects = hitObjectContainer.AliveObjects - .Reverse() - .Select(d => d.HitObject) - .OfType() - .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; - - if (h == hitObject) - return canFlourish; - } - - return false; - } } } diff --git a/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonFlourishTriggerSource.cs b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonFlourishTriggerSource.cs new file mode 100644 index 0000000000..661f737843 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/Skinning/Argon/ArgonFlourishTriggerSource.cs @@ -0,0 +1,76 @@ +// Copyright (c) ppy Pty Ltd . 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 : ArgonDrumSampleTriggerSource + { + private readonly HitObjectContainer hitObjectContainer; + + [Resolved] + private ISkinSource skinSource { get; set; } = null!; + + /// + /// The minimum time to leave between flourishes that are added to strong rim hits. + /// + private const double time_between_flourishes = 2000; + + public ArgonFlourishTriggerSource(HitObjectContainer hitObjectContainer) + : base(hitObjectContainer, SampleBalance.Centre) + { + 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() + .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; + + if (h == hitObject) + return canFlourish; + } + + return false; + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/DrumSamplePlayer.cs b/osu.Game.Rulesets.Taiko/UI/DrumSamplePlayer.cs index 5c9a57724f..310a4c1edb 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrumSamplePlayer.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrumSamplePlayer.cs @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Taiko.UI } } - triggerSource.Play(hitType, strong); + Play(triggerSource, hitType, strong); lastHitTime = Time.Current; lastAction = e.Action; @@ -101,6 +101,9 @@ namespace osu.Game.Rulesets.Taiko.UI 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)