diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs index c353496410..3e9fc1ae27 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/EndTimeObjectPatternGenerator.cs @@ -76,10 +76,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy Duration = endTime - HitObject.StartTime }; - hold.Head.Samples.Add(new SampleInfo - { - Name = SampleInfo.HIT_NORMAL - }); + if (hold.Head.Samples == null) + hold.Head.Samples = new SampleInfoList(); + + hold.Head.Samples.Add(new SampleInfo { Name = SampleInfo.HIT_NORMAL }); hold.Tail.Samples = HitObject.Samples; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 57c1ffab00..befe84e3e9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Scale = s.Scale, ComboColour = s.ComboColour, Samples = s.Samples, - SoundControlPoint = s.SoundControlPoint + SampleControlPoint = s.SampleControlPoint }) }; diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs index 489eacf386..df33aae94e 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHit.cs @@ -67,7 +67,8 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables { validKeyPressed = HitActions.Contains(action); - return UpdateJudgement(true); + // Only count this as handled if the new judgement is a hit + return UpdateJudgement(true) && Judgements.LastOrDefault()?.IsHit == true; } protected override void Update() diff --git a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs index c07eaf4d8b..81b23af1a9 100644 --- a/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs +++ b/osu.Game.Rulesets.Taiko/Objects/Drawables/DrawableHitStrong.cs @@ -83,7 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables return false; // Assume the intention was to hit the strong hit with both keys only if the first key is still being held down - return firstKeyHeld && UpdateJudgement(true); + return firstKeyHeld && UpdateJudgement(true) && Judgements.LastOrDefault()?.IsHit == true; } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index ac3796f5b8..2cc95fc981 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -16,10 +16,17 @@ using osu.Framework.Extensions.Color4Extensions; using System.Linq; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Taiko.Objects.Drawables; +using osu.Framework.Input.Bindings; +using osu.Game.Beatmaps.ControlPoints; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; +using System.Collections.Generic; +using osu.Game.Audio; +using System; namespace osu.Game.Rulesets.Taiko.UI { - public class TaikoPlayfield : ScrollingPlayfield + public class TaikoPlayfield : ScrollingPlayfield, IKeyBindingHandler { /// /// Default height of a when inside a . @@ -54,9 +61,13 @@ namespace osu.Game.Rulesets.Taiko.UI private readonly Box overlayBackground; private readonly Box background; - public TaikoPlayfield() + private readonly ControlPointInfo controlPointInfo; + private Dictionary drumSampleMappings; + + public TaikoPlayfield(ControlPointInfo controlPointInfo) : base(Axes.X) { + this.controlPointInfo = controlPointInfo; AddRangeInternal(new Drawable[] { backgroundContainer = new Container @@ -194,8 +205,19 @@ namespace osu.Game.Rulesets.Taiko.UI } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load(OsuColour colours, AudioManager audio) { + drumSampleMappings = new Dictionary(); + foreach (var s in controlPointInfo.SamplePoints) + { + drumSampleMappings.Add(s, + new DrumSamples + { + Centre = s.GetSampleInfo().GetChannel(audio.Sample), + Rim = s.GetSampleInfo(SampleInfo.HIT_CLAP).GetChannel(audio.Sample) + }); + } + overlayBackgroundContainer.BorderColour = colours.Gray0; overlayBackground.Colour = colours.Gray1; @@ -249,7 +271,9 @@ namespace osu.Game.Rulesets.Taiko.UI { topLevelHitContainer.Add(judgedObject.CreateProxy()); } - catch { } + catch + { + } } hitExplosionContainer.Add(new HitExplosion(judgedObject, isRim)); @@ -258,5 +282,28 @@ namespace osu.Game.Rulesets.Taiko.UI kiaiExplosionContainer.Add(new KiaiHitExplosion(judgedObject, isRim)); } } + + public bool OnPressed(TaikoAction action) + { + var samplePoint = controlPointInfo.SamplePointAt(Clock.CurrentTime); + + if (!drumSampleMappings.TryGetValue(samplePoint, out var samples)) + throw new InvalidOperationException("Current sample set not found."); + + if (action == TaikoAction.LeftCentre || action == TaikoAction.RightCentre) + samples.Centre.Play(); + else + samples.Rim.Play(); + + return true; + } + + public bool OnReleased(TaikoAction action) => false; + + private class DrumSamples + { + public SampleChannel Centre; + public SampleChannel Rim; + } } } diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs index 48ee0a5b42..614b446181 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoRulesetContainer.cs @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Taiko.UI public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); - protected override Playfield CreatePlayfield() => new TaikoPlayfield + protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 86413af4b6..d3e7958ec1 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -146,8 +146,8 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.AreEqual(116999, difficultyPoint.Time); Assert.AreEqual(0.75000000000000189d, difficultyPoint.SpeedMultiplier); - Assert.AreEqual(34, controlPoints.SoundPoints.Count); - var soundPoint = controlPoints.SoundPoints[0]; + Assert.AreEqual(34, controlPoints.SamplePoints.Count); + var soundPoint = controlPoints.SamplePoints[0]; Assert.AreEqual(956, soundPoint.Time); Assert.AreEqual("soft", soundPoint.SampleBank); Assert.AreEqual(60, soundPoint.SampleVolume); diff --git a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs index 01371ad78c..ef7404ad49 100644 --- a/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs +++ b/osu.Game.Tests/Visual/TestCaseEditorSummaryTimeline.cs @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual b.ControlPointInfo.EffectPoints.Add(new EffectControlPoint { Time = random.Next(0, length) }); for (int i = 0; i < random.Next(1, 5); i++) - b.ControlPointInfo.SoundPoints.Add(new SoundControlPoint { Time = random.Next(0, length) }); + b.ControlPointInfo.SamplePoints.Add(new SampleControlPoint { Time = random.Next(0, length) }); b.BeatmapInfo.Bookmarks = new int[random.Next(10, 30)]; for (int i = 0; i < b.BeatmapInfo.Bookmarks.Length; i++) diff --git a/osu.Game/Audio/SampleInfo.cs b/osu.Game/Audio/SampleInfo.cs index 8242ee14df..64a9aa50a0 100644 --- a/osu.Game/Audio/SampleInfo.cs +++ b/osu.Game/Audio/SampleInfo.cs @@ -2,6 +2,7 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using osu.Framework.Audio.Sample; namespace osu.Game.Audio { @@ -13,6 +14,13 @@ namespace osu.Game.Audio public const string HIT_NORMAL = @"hitnormal"; public const string HIT_CLAP = @"hitclap"; + public SampleChannel GetChannel(SampleManager manager) + { + var channel = manager.Get($"Gameplay/{Bank}-{Name}"); + channel.Volume.Value = Volume / 100.0; + return channel; + } + /// /// The bank to load the sample from. /// diff --git a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs index 64b9b837b6..f031ebe353 100644 --- a/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs +++ b/osu.Game/Beatmaps/ControlPoints/ControlPointInfo.cs @@ -28,7 +28,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// All sound points. /// [JsonProperty] - public SortedList SoundPoints { get; private set; } = new SortedList(Comparer.Default); + public SortedList SamplePoints { get; private set; } = new SortedList(Comparer.Default); /// /// All effect points. @@ -55,7 +55,7 @@ namespace osu.Game.Beatmaps.ControlPoints /// /// The time to find the sound control point at. /// The sound control point. - public SoundControlPoint SoundPointAt(double time) => binarySearch(SoundPoints, time); + public SampleControlPoint SamplePointAt(double time) => binarySearch(SamplePoints, time, SamplePoints.FirstOrDefault()); /// /// Finds the timing control point that is active at . diff --git a/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs new file mode 100644 index 0000000000..40e45da13c --- /dev/null +++ b/osu.Game/Beatmaps/ControlPoints/SampleControlPoint.cs @@ -0,0 +1,34 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Game.Audio; + +namespace osu.Game.Beatmaps.ControlPoints +{ + public class SampleControlPoint : ControlPoint + { + public const string DEFAULT_BANK = "normal"; + + /// + /// The default sample bank at this control point. + /// + public string SampleBank = DEFAULT_BANK; + + /// + /// The default sample volume at this control point. + /// + public int SampleVolume; + + /// + /// Create a SampleInfo based on the sample settings in this control point. + /// + /// The name of the same. + /// A populated . + public SampleInfo GetSampleInfo(string sampleName = SampleInfo.HIT_NORMAL) => new SampleInfo + { + Bank = SampleBank, + Name = sampleName, + Volume = SampleVolume, + }; + } +} diff --git a/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs b/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs deleted file mode 100644 index b73a03b95a..0000000000 --- a/osu.Game/Beatmaps/ControlPoints/SoundControlPoint.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) 2007-2017 ppy Pty Ltd . -// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE - -namespace osu.Game.Beatmaps.ControlPoints -{ - public class SoundControlPoint : ControlPoint - { - public const string DEFAULT_BANK = "normal"; - - /// - /// The default sample bank at this control point. - /// - public string SampleBank = DEFAULT_BANK; - - /// - /// The default sample volume at this control point. - /// - public int SampleVolume; - } -} diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index b7004dd3eb..ea29e480ec 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -313,7 +313,7 @@ namespace osu.Game.Beatmaps.Formats stringSampleSet = @"normal"; DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(time); - SoundControlPoint soundPoint = beatmap.ControlPointInfo.SoundPointAt(time); + SampleControlPoint samplePoint = beatmap.ControlPointInfo.SamplePointAt(time); EffectControlPoint effectPoint = beatmap.ControlPointInfo.EffectPointAt(time); if (timingChange) @@ -336,9 +336,9 @@ namespace osu.Game.Beatmaps.Formats }); } - if (stringSampleSet != soundPoint.SampleBank || sampleVolume != soundPoint.SampleVolume) + if (stringSampleSet != samplePoint.SampleBank || sampleVolume != samplePoint.SampleVolume) { - beatmap.ControlPointInfo.SoundPoints.Add(new SoundControlPoint + beatmap.ControlPointInfo.SamplePoints.Add(new SampleControlPoint { Time = time, SampleBank = stringSampleSet, diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 4f5f8898f8..d960ab6b48 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -84,26 +84,27 @@ namespace osu.Game.Rulesets.Objects.Drawables [BackgroundDependencyLoader] private void load(AudioManager audio) { - foreach (SampleInfo sample in HitObject.Samples) + if (HitObject.Samples != null) { - if (HitObject.SoundControlPoint == null) - throw new ArgumentNullException(nameof(HitObject.SoundControlPoint), $"{nameof(HitObject)} must always have an attached {nameof(HitObject.SoundControlPoint)}."); + if (HitObject.SampleControlPoint == null) + throw new ArgumentNullException(nameof(HitObject.SampleControlPoint), $"{nameof(HitObject)} must always have an attached {nameof(HitObject.SampleControlPoint)}."); - var bank = sample.Bank; - if (string.IsNullOrEmpty(bank)) - bank = HitObject.SoundControlPoint.SampleBank; + foreach (SampleInfo s in HitObject.Samples) + { + SampleInfo localSampleInfo = new SampleInfo + { + Bank = s.Bank ?? HitObject.SampleControlPoint.SampleBank, + Name = s.Name, + Volume = s.Volume > 0 ? s.Volume : HitObject.SampleControlPoint.SampleVolume + }; - int volume = sample.Volume; - if (volume == 0) - volume = HitObject.SoundControlPoint.SampleVolume; + SampleChannel channel = localSampleInfo.GetChannel(audio.Sample); - SampleChannel channel = audio.Sample.Get($@"Gameplay/{bank}-{sample.Name}"); + if (channel == null) + continue; - if (channel == null) - continue; - - channel.Volume.Value = volume; - Samples.Add(channel); + Samples.Add(channel); + } } } diff --git a/osu.Game/Rulesets/Objects/HitObject.cs b/osu.Game/Rulesets/Objects/HitObject.cs index 9e331dfea9..e950516bf4 100644 --- a/osu.Game/Rulesets/Objects/HitObject.cs +++ b/osu.Game/Rulesets/Objects/HitObject.cs @@ -32,10 +32,10 @@ namespace osu.Game.Rulesets.Objects /// and can be treated as the default samples for the hit object. /// /// - public SampleInfoList Samples = new SampleInfoList(); + public SampleInfoList Samples; [JsonIgnore] - public SoundControlPoint SoundControlPoint; + public SampleControlPoint SampleControlPoint; /// /// Whether this is in Kiai time. @@ -64,11 +64,11 @@ namespace osu.Game.Rulesets.Objects protected virtual void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { - SoundControlPoint soundPoint = controlPointInfo.SoundPointAt(StartTime); + SampleControlPoint samplePoint = controlPointInfo.SamplePointAt(StartTime); EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime); Kiai = effectPoint.KiaiMode; - SoundControlPoint = soundPoint; + SampleControlPoint = samplePoint; } protected virtual void CreateNestedHitObjects() diff --git a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs index 405befb80a..3759470c9a 100644 --- a/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs +++ b/osu.Game/Screens/Edit/Components/Timelines/Summary/Parts/ControlPointPart.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts cpi.TimingPoints.ForEach(addTimingPoint); // Consider all non-timing points as the same type - cpi.SoundPoints.Select(c => (ControlPoint)c) + cpi.SamplePoints.Select(c => (ControlPoint)c) .Concat(cpi.EffectPoints) .Concat(cpi.DifficultyPoints) .Distinct() diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index e78d10cc4f..f9e67623a3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -258,7 +258,7 @@ - +