-
-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix results screen sounds persisting after exit #31742
Conversation
I think it would be best to let other quick samples play out rather than potentially get clipped to zero volume on exiting the screen. Given how nested the playback of applause it, the easiest way to fix this might be to just move playback into 10 minute mock-up: diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs
index 319a87fdfc..dc95647538 100644
--- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs
+++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs
@@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Audio;
@@ -101,7 +100,6 @@ public partial class AccuracyCircle : CompositeDrawable
private PoolableSkinnableSample? badgeMaxSound;
private PoolableSkinnableSample? swooshUpSound;
private PoolableSkinnableSample? rankImpactSound;
- private PoolableSkinnableSample? rankApplauseSound;
private readonly Bindable<double> tickPlaybackRate = new Bindable<double>();
@@ -119,6 +117,9 @@ public partial class AccuracyCircle : CompositeDrawable
private readonly bool isFailedSDueToMisses;
private RankText failedSRankText = null!;
+ [Resolved]
+ private ResultsScreen? results { get; set; }
+
public AccuracyCircle(ScoreInfo score, bool withFlair = false)
{
this.score = score;
@@ -197,15 +198,9 @@ private void load()
if (withFlair)
{
- var applauseSamples = new List<string> { applauseSampleName };
- if (score.Rank >= ScoreRank.B)
- // when rank is B or higher, play legacy applause sample on legacy skins.
- applauseSamples.Insert(0, @"applause");
-
AddRangeInternal(new Drawable[]
{
rankImpactSound = new PoolableSkinnableSample(new SampleInfo(impactSampleName)),
- rankApplauseSound = new PoolableSkinnableSample(new SampleInfo(applauseSamples.ToArray())),
scoreTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/score-tick")),
badgeTickSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink")),
badgeMaxSound = new PoolableSkinnableSample(new SampleInfo(@"Results/badge-dink-max")),
@@ -333,16 +328,9 @@ protected override void LoadComplete()
});
const double applause_pre_delay = 545f;
- const double applause_volume = 0.8f;
using (BeginDelayedSequence(applause_pre_delay))
- {
- Schedule(() =>
- {
- rankApplauseSound!.VolumeTo(applause_volume);
- rankApplauseSound!.Play();
- });
- }
+ Schedule(() => results?.PlayApplause(score.Rank));
}
}
@@ -384,34 +372,6 @@ protected override void Update()
}
}
- private string applauseSampleName
- {
- get
- {
- switch (score.Rank)
- {
- default:
- case ScoreRank.D:
- return @"Results/applause-d";
-
- case ScoreRank.C:
- return @"Results/applause-c";
-
- case ScoreRank.B:
- return @"Results/applause-b";
-
- case ScoreRank.A:
- return @"Results/applause-a";
-
- case ScoreRank.S:
- case ScoreRank.SH:
- case ScoreRank.X:
- case ScoreRank.XH:
- return @"Results/applause-s";
- }
- }
- }
-
private string impactSampleName
{
get
diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs
index 5e91171051..34e51dc73d 100644
--- a/osu.Game/Screens/Ranking/ResultsScreen.cs
+++ b/osu.Game/Screens/Ranking/ResultsScreen.cs
@@ -17,6 +17,7 @@
using osu.Framework.Input.Bindings;
using osu.Framework.Input.Events;
using osu.Framework.Screens;
+using osu.Game.Audio;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
@@ -29,10 +30,12 @@
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Screens.Ranking.Statistics;
+using osu.Game.Skinning;
using osuTK;
namespace osu.Game.Screens.Ranking
{
+ [Cached]
public abstract partial class ResultsScreen : ScreenWithBeatmapBackground, IKeyBindingHandler<GlobalAction>
{
protected const float BACKGROUND_BLUR = 20;
@@ -263,6 +266,64 @@ protected override void Update()
}
}
+ #region Applause
+
+ public void PlayApplause(ScoreRank rank)
+ {
+ const double applause_volume = 0.8f;
+
+ if (!this.IsCurrentScreen())
+ return;
+
+ rankApplauseSound?.Dispose();
+
+ var applauseSamples = new List<string>();
+
+ if (rank >= ScoreRank.B)
+ // when rank is B or higher, play legacy applause sample on legacy skins.
+ applauseSamples.Insert(0, @"applause");
+
+ switch (rank)
+ {
+ default:
+ case ScoreRank.D:
+ applauseSamples.Add(@"Results/applause-d");
+ break;
+
+ case ScoreRank.C:
+ applauseSamples.Add(@"Results/applause-c");
+ break;
+
+ case ScoreRank.B:
+ applauseSamples.Add(@"Results/applause-b");
+ break;
+
+ case ScoreRank.A:
+ applauseSamples.Add(@"Results/applause-a");
+ break;
+
+ case ScoreRank.S:
+ case ScoreRank.SH:
+ case ScoreRank.X:
+ case ScoreRank.XH:
+ applauseSamples.Add(@"Results/applause-s");
+ break;
+ }
+
+ LoadComponentAsync(rankApplauseSound = new PoolableSkinnableSample(new SampleInfo(applauseSamples.ToArray())), s =>
+ {
+ if (!this.IsCurrentScreen() || s != rankApplauseSound)
+ return;
+
+ rankApplauseSound.VolumeTo(applause_volume);
+ rankApplauseSound.Play();
+ });
+ }
+
+ private PoolableSkinnableSample? rankApplauseSound;
+
+ #endregion
+
/// <summary>
/// Performs a fetch/refresh of scores to be displayed.
/// </summary>
@@ -330,6 +391,8 @@ public override bool OnExiting(ScreenExitEvent e)
if (!skipExitTransition)
this.FadeOut(100);
+
+ rankApplauseSound?.Stop();
return false;
}
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
as commented
What's the difference between letting other quick samples play out and not clip, and letting this one clip? |
The real killer with applause samples is that many popular skins these days have full songs as With the approach of moving it to the results screen, we could also do a fade on applause rather than immediately cut it off for bonus points. |
I suppose, but I mentioned the reason why it's not a fade in the OP. And in that case I would expect everything to fade uniformly. I will apply your change regardless. |
Applied. To be clear, this is what I'm referring to by the above - how a similar situation will behave now which isn't an issue with my original changes: 2025-02-03.14-14-32.mp4 |
Hmm, that one is also pretty long. With the |
That's been answered in the OP. ppy/osu-framework#6518 |
I'm guessing fixing that framework side is not going to be as simple as running one final |
Who knows. Anything can be hacked if you put your mind to it. Would you like me to switch gears and try to understand whether lifetimes in o!f are completely broken? This was intended as a simple/quick change, and in general I agree with the sentiment that the applause is a special case skinned to be minutes in length. |
Yeah let's go with this for now. This sound-on-screen-exit isn't going to be limited to just the results screen. I can imagine us wanting to use the |
Resolves #31740
I probably uncovered about 4 issues down multiple implementations, and this is the best/simplest that I've come up with for now.
Testing:
Primarily, I ran into ppy/osu-framework#6518. In most cases it works fine but there's the odd case where the sample remains audible and feels very bad.
I judge the immediate fade out to be acceptable here as it's masked by the screen exiting sound. To my ears at least...
Note that this will also result in immediate fade out as above.
For
AccuracyCircle
it'll work fine (with few changes), however there are other components involved in this screen that don't use or can't easily be made to usePausableSkinnableSound
because they shouldn't be skinnable. Without this, some sounds pause but others continue for a little while which feels bad to me.We don't really have a
PausableDrawableSample
that responds to this interface right now for non-skinnable cases, and it's not immediately obvious what its semantics would be.Using
AudioContainer
means that all sounds are muted at the same time.I think this will also work, but I don't want to deal with nested mixers right now.