From 64579d50ac160215d5a8f60dc5f5fa8526b27fbc Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 22 Apr 2021 22:59:47 +0900 Subject: [PATCH] Use only single PlayerInstance for hit sample playback --- .../TestSceneMultiplayerSpectator.cs | 46 ++++++++++++-- .../Spectate/MultiplayerSpectator.cs | 20 +++++++ .../Multiplayer/Spectate/PlayerInstance.cs | 60 +++++++++++++++++-- 3 files changed, 117 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectator.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectator.cs index 9ec7bfb60b..cd89d3bcc1 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectator.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayerSpectator.cs @@ -198,10 +198,40 @@ namespace osu.Game.Tests.Visual.Multiplayer checkPausedInstant(56, false); // Player 2 should catch up to player 1 after unpausing. - AddUntilStep("player 2 not catching up", () => !getInstance(56).GameplayClock.IsCatchingUp); + waitForCatchup(56); AddWaitStep("wait a bit", 10); } + [Test] + public void TestMostInSyncUserIsAudioSource() + { + start(new[] { 55, 56 }); + loadSpectateScreen(); + + assertVolume(55, 0); + assertVolume(56, 0); + + sendFrames(55, 10); + sendFrames(56, 20); + assertVolume(55, 1); + assertVolume(56, 0); + + checkPaused(55, true); + assertVolume(55, 0); + assertVolume(56, 1); + + sendFrames(55, 100); + waitForCatchup(55); + checkPaused(56, true); + assertVolume(55, 1); + assertVolume(56, 0); + + sendFrames(56, 100); + waitForCatchup(56); + assertVolume(55, 1); + assertVolume(56, 0); + } + private void loadSpectateScreen(bool waitForPlayerLoad = true) { AddStep("load screen", () => @@ -255,11 +285,17 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } - private void checkPaused(int userId, bool state) => - AddUntilStep($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType().First().GameplayClock.IsRunning != state); + private void checkPaused(int userId, bool state) + => AddUntilStep($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType().First().GameplayClock.IsRunning != state); - private void checkPausedInstant(int userId, bool state) => - AddAssert($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType().First().GameplayClock.IsRunning != state); + private void checkPausedInstant(int userId, bool state) + => AddAssert($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType().First().GameplayClock.IsRunning != state); + + private void assertVolume(int userId, double volume) + => AddAssert($"{userId} volume is {volume}", () => getInstance(userId).Volume.Value == volume); + + private void waitForCatchup(int userId) + => AddUntilStep($"{userId} not catching up", () => !getInstance(userId).GameplayClock.IsCatchingUp); private Player getPlayer(int userId) => getInstance(userId).ChildrenOfType().Single(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectator.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectator.cs index 2db470da67..33dd57586c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectator.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiplayerSpectator.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -28,6 +29,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate private ISyncManager syncManager; private PlayerGrid grid; private MultiplayerSpectatorLeaderboard leaderboard; + private PlayerInstance currentAudioSource; public MultiplayerSpectator(int[] userIds) : base(userIds.AsSpan().Slice(0, Math.Min(16, userIds.Length)).ToArray()) @@ -85,6 +87,24 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate masterClockContainer.Reset(); } + protected override void Update() + { + base.Update(); + + if (!isCandidateAudioSource(currentAudioSource?.GameplayClock)) + { + currentAudioSource = instances.Where(i => isCandidateAudioSource(i.GameplayClock)) + .OrderBy(i => Math.Abs(i.GameplayClock.CurrentTime - syncManager.Master.CurrentTime)) + .FirstOrDefault(); + + foreach (var instance in instances) + instance.Volume.Value = instance == currentAudioSource ? 1 : 0; + } + } + + private bool isCandidateAudioSource([CanBeNull] ISlaveClock clock) + => clock?.IsRunning == true && !clock.IsCatchingUp && !clock.WaitingOnFrames.Value; + protected override void OnUserStateChanged(int userId, SpectatorState spectatorState) { } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerInstance.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerInstance.cs index 407732ee2e..80d6727599 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerInstance.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/PlayerInstance.cs @@ -3,6 +3,8 @@ using System; using osu.Framework.Allocation; +using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; @@ -13,7 +15,7 @@ using osu.Game.Screens.Play; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { - public class PlayerInstance : CompositeDrawable + public class PlayerInstance : CompositeDrawable, IAdjustableAudioComponent { public bool PlayerLoaded => stack?.CurrentScreen is Player; @@ -25,8 +27,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate [Resolved] private BeatmapManager beatmapManager { get; set; } - private readonly Container content; + private readonly Container gameplayContent; private readonly LoadingLayer loadingLayer; + private readonly AudioContainer audioContainer; private OsuScreenStack stack; public PlayerInstance(int userId, CatchUpSlaveClock gameplayClock) @@ -39,7 +42,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate InternalChildren = new Drawable[] { - content = new DrawSizePreservingFillContainer { RelativeSizeAxes = Axes.Both }, + audioContainer = new AudioContainer + { + RelativeSizeAxes = Axes.Both, + Child = gameplayContent = new DrawSizePreservingFillContainer { RelativeSizeAxes = Axes.Both }, + }, loadingLayer = new LoadingLayer(true) { State = { Value = Visibility.Visible } } }; } @@ -51,7 +58,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate Score = score; - content.Child = new GameplayIsolationContainer(beatmapManager.GetWorkingBeatmap(Score.ScoreInfo.Beatmap, bypassCache: true), Score.ScoreInfo.Ruleset, Score.ScoreInfo.Mods) + gameplayContent.Child = new GameplayIsolationContainer(beatmapManager.GetWorkingBeatmap(Score.ScoreInfo.Beatmap), Score.ScoreInfo.Ruleset, Score.ScoreInfo.Mods) { RelativeSizeAxes = Axes.Both, Child = stack = new OsuScreenStack() @@ -64,5 +71,50 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate // Player interferes with global input, so disable input for now. public override bool PropagatePositionalInputSubTree => false; public override bool PropagateNonPositionalInputSubTree => false; + + #region IAdjustableAudioComponent + + public IBindable AggregateVolume => audioContainer.AggregateVolume; + + public IBindable AggregateBalance => audioContainer.AggregateBalance; + + public IBindable AggregateFrequency => audioContainer.AggregateFrequency; + + public IBindable AggregateTempo => audioContainer.AggregateTempo; + + public void BindAdjustments(IAggregateAudioAdjustment component) + { + audioContainer.BindAdjustments(component); + } + + public void UnbindAdjustments(IAggregateAudioAdjustment component) + { + audioContainer.UnbindAdjustments(component); + } + + public void AddAdjustment(AdjustableProperty type, IBindable adjustBindable) + { + audioContainer.AddAdjustment(type, adjustBindable); + } + + public void RemoveAdjustment(AdjustableProperty type, IBindable adjustBindable) + { + audioContainer.RemoveAdjustment(type, adjustBindable); + } + + public void RemoveAllAdjustments(AdjustableProperty type) + { + audioContainer.RemoveAllAdjustments(type); + } + + public BindableNumber Volume => audioContainer.Volume; + + public BindableNumber Balance => audioContainer.Balance; + + public BindableNumber Frequency => audioContainer.Frequency; + + public BindableNumber Tempo => audioContainer.Tempo; + + #endregion } }