From 5208d8a0b26a5d1ece5d715e54d7f29ee4d6ed8e Mon Sep 17 00:00:00 2001 From: Jamie Taylor Date: Fri, 18 Apr 2025 19:45:30 +0900 Subject: [PATCH] Add audio feedback to BSS process --- .../TestSceneSubmissionStageProgress.cs | 96 +++++++++++++++++++ .../Submission/BeatmapSubmissionScreen.cs | 14 ++- .../Submission/SubmissionStageProgress.cs | 53 +++++++++- 3 files changed, 161 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneSubmissionStageProgress.cs b/osu.Game.Tests/Visual/Editing/TestSceneSubmissionStageProgress.cs index 47414bb24e..51627f0baf 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneSubmissionStageProgress.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneSubmissionStageProgress.cs @@ -1,10 +1,14 @@ // 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 NUnit.Framework; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps.Drawables.Cards; using osu.Game.Overlays; using osu.Game.Screens.Edit.Submission; using osuTK; @@ -16,6 +20,11 @@ namespace osu.Game.Tests.Visual.Editing [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Aquamarine); + [Resolved] + private AudioManager audio { get; set; } = null!; + + private Sample? completeSample; + [Test] public void TestAppearance() { @@ -43,5 +52,92 @@ namespace osu.Game.Tests.Visual.Editing AddStep("failed with long message", () => progress.SetFailed("this is a very very very very VERY VEEEEEEEEEEEEEEEEEEEEEEEEERY long error message like you would never believe")); AddStep("canceled", () => progress.SetCanceled()); } + + [Test] + public void TestAudioSequence() + { + SubmissionStageProgress[] stages = new SubmissionStageProgress[4]; + Container? cardContainer = null; + + AddStep("prepare", () => + { + Child = new Container + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(1), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.8f), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 5), + Children = new Drawable[] + { + stages[0] = new SubmissionStageProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + StageDescription = "Export...", + StageIndex = 0 + }, + stages[1] = new SubmissionStageProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + StageDescription = "CreateSet...", + StageIndex = 1 + }, + stages[2] = new SubmissionStageProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + StageDescription = "Upload...", + StageIndex = 2 + }, + stages[3] = new SubmissionStageProgress + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + StageDescription = "Update...", + StageIndex = 3 + }, + cardContainer = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } + } + }; + + completeSample = audio.Samples.Get(@"UI/bss-complete"); + }); + + for (int i = 0; i < stages.Length; i++) + { + int step = i; + AddStep($"{step}: not started", () => stages[step].SetNotStarted()); + AddStep($"{step}: indeterminate progress", () => stages[step].SetInProgress()); + AddStep($"{step}: 70% progress", () => stages[step].SetInProgress(0.25f)); + AddStep($"{step}: completed", () => stages[step].SetCompleted()); + } + + AddStep("pause for timing", () => { }); + + AddStep("Sequence Complete", () => + { + var beatmapSet = CreateAPIBeatmapSet(Ruleset.Value); + beatmapSet.Beatmaps = Enumerable.Repeat(beatmapSet.Beatmaps.First(), 100).ToArray(); + LoadComponentAsync(new BeatmapCardExtra(beatmapSet, false), loaded => + { + cardContainer?.Add(loaded); + completeSample?.Play(); + }); + }); + } } } diff --git a/osu.Game/Screens/Edit/Submission/BeatmapSubmissionScreen.cs b/osu.Game/Screens/Edit/Submission/BeatmapSubmissionScreen.cs index 2ea710d3ab..94ed813461 100644 --- a/osu.Game/Screens/Edit/Submission/BeatmapSubmissionScreen.cs +++ b/osu.Game/Screens/Edit/Submission/BeatmapSubmissionScreen.cs @@ -8,6 +8,8 @@ using System.IO; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Development; using osu.Framework.Extensions; using osu.Framework.Graphics; @@ -81,8 +83,10 @@ namespace osu.Game.Screens.Edit.Submission private Live? importedSet; + private Sample completedSample = null!; + [BackgroundDependencyLoader] - private void load() + private void load(AudioManager audio) { AddRangeInternal(new Drawable[] { @@ -118,24 +122,28 @@ namespace osu.Game.Screens.Edit.Submission createSetStep = new SubmissionStageProgress { StageDescription = BeatmapSubmissionStrings.Preparing, + StageIndex = 0, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, exportStep = new SubmissionStageProgress { StageDescription = BeatmapSubmissionStrings.Exporting, + StageIndex = 1, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, uploadStep = new SubmissionStageProgress { StageDescription = BeatmapSubmissionStrings.Uploading, + StageIndex = 2, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, updateStep = new SubmissionStageProgress { StageDescription = BeatmapSubmissionStrings.Finishing, + StageIndex = 3, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, @@ -181,6 +189,8 @@ namespace osu.Game.Screens.Edit.Submission } } }); + + completedSample = audio.Samples.Get(@"UI/bss-complete"); } private void createBeatmapSet() @@ -382,6 +392,8 @@ namespace osu.Game.Screens.Edit.Submission successContainer.Add(loaded); flashLayer.FadeOutFromOne(2000, Easing.OutQuint); }); + + completedSample.Play(); }; api.Queue(getBeatmapSetRequest); diff --git a/osu.Game/Screens/Edit/Submission/SubmissionStageProgress.cs b/osu.Game/Screens/Edit/Submission/SubmissionStageProgress.cs index 101313c627..c47aea8a0a 100644 --- a/osu.Game/Screens/Edit/Submission/SubmissionStageProgress.cs +++ b/osu.Game/Screens/Edit/Submission/SubmissionStageProgress.cs @@ -1,13 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -21,6 +25,8 @@ namespace osu.Game.Screens.Edit.Submission { public LocalisableString StageDescription { get; init; } + public int StageIndex { get; init; } + private Bindable status { get; } = new Bindable(); private Bindable progress { get; } = new Bindable(); @@ -33,8 +39,19 @@ namespace osu.Game.Screens.Edit.Submission [Resolved] private OsuColour colours { get; set; } = null!; + private Sample progressSample = null!; + + private const int stage_done_sample_count = 4; + private Sample stageDoneSample = null!; + + private Sample errorSample = null!; + private Sample cancelSample = null!; + + private double? lastSamplePlayback; + private float? previousPercent; + [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) + private void load(OverlayColourProvider colourProvider, AudioManager audio) { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -111,6 +128,13 @@ namespace osu.Game.Screens.Edit.Submission } } }; + + errorSample = audio.Samples.Get(@"UI/generic-error"); + cancelSample = audio.Samples.Get(@"UI/notification-cancel"); + progressSample = audio.Samples.Get(@"UI/bss-progress"); + + int stageSample = Math.Min(stage_done_sample_count - 1, StageIndex); + stageDoneSample = audio.Samples.Get(@$"UI/bss-stage-{stageSample}"); } protected override void LoadComplete() @@ -119,6 +143,25 @@ namespace osu.Game.Screens.Edit.Submission status.BindValueChanged(_ => Scheduler.AddOnce(updateStatus), true); progress.BindValueChanged(_ => Scheduler.AddOnce(updateProgress), true); + + // Binding to `progressBar` updates instead of `progress` for more frequent/granular updates + progressBar.OnUpdate += playProgressSound; + } + + private void playProgressSound(Drawable box) + { + float width = box.Width; + SampleChannel sampleChannel = progressSample.GetChannel(); + + if (Precision.AlmostEquals(previousPercent ?? 0f, width) || (lastSamplePlayback != null && Time.Current - lastSamplePlayback < 10)) + return; + + sampleChannel.Frequency.Value = 0.5f + (width * 1.5f); + sampleChannel.Volume.Value = 0.25f + (width / 2f) * .75f; + sampleChannel.Play(); + + lastSamplePlayback = Time.Current; + previousPercent = width; } public void SetNotStarted() => status.Value = StageStatusType.NotStarted; @@ -176,6 +219,12 @@ namespace osu.Game.Screens.Edit.Submission }; iconContainer.Colour = colours.Green1; iconContainer.FlashColour(Colour4.White, 1000, Easing.OutQuint); + + // manually set progress value, as to trigger sample playback for the final section + progress.Value = 1; + + stageDoneSample.Play(); + break; case StageStatusType.Failed: @@ -186,6 +235,7 @@ namespace osu.Game.Screens.Edit.Submission }; iconContainer.Colour = colours.Red1; iconContainer.FlashColour(Colour4.White, 1000, Easing.OutQuint); + errorSample.Play(); break; case StageStatusType.Canceled: @@ -196,6 +246,7 @@ namespace osu.Game.Screens.Edit.Submission }; iconContainer.Colour = colours.Gray8; iconContainer.FlashColour(Colour4.White, 1000, Easing.OutQuint); + cancelSample.Play(); break; } }