1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 02:42:54 +08:00

Use only single PlayerInstance for hit sample playback

This commit is contained in:
smoogipoo 2021-04-22 22:59:47 +09:00
parent 6588859c32
commit 64579d50ac
3 changed files with 117 additions and 9 deletions

View File

@ -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<GameplayClockContainer>().First().GameplayClock.IsRunning != state);
private void checkPaused(int userId, bool state)
=> AddUntilStep($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType<GameplayClockContainer>().First().GameplayClock.IsRunning != state);
private void checkPausedInstant(int userId, bool state) =>
AddAssert($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType<GameplayClockContainer>().First().GameplayClock.IsRunning != state);
private void checkPausedInstant(int userId, bool state)
=> AddAssert($"{userId} is {(state ? "paused" : "playing")}", () => getPlayer(userId).ChildrenOfType<GameplayClockContainer>().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<Player>().Single();

View File

@ -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)
{
}

View File

@ -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<double> AggregateVolume => audioContainer.AggregateVolume;
public IBindable<double> AggregateBalance => audioContainer.AggregateBalance;
public IBindable<double> AggregateFrequency => audioContainer.AggregateFrequency;
public IBindable<double> 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<double> adjustBindable)
{
audioContainer.AddAdjustment(type, adjustBindable);
}
public void RemoveAdjustment(AdjustableProperty type, IBindable<double> adjustBindable)
{
audioContainer.RemoveAdjustment(type, adjustBindable);
}
public void RemoveAllAdjustments(AdjustableProperty type)
{
audioContainer.RemoveAllAdjustments(type);
}
public BindableNumber<double> Volume => audioContainer.Volume;
public BindableNumber<double> Balance => audioContainer.Balance;
public BindableNumber<double> Frequency => audioContainer.Frequency;
public BindableNumber<double> Tempo => audioContainer.Tempo;
#endregion
}
}