Use existing bindable flow instead

This commit is contained in:
Dean Herbert 2020-09-29 12:45:20 +09:00
parent 585b857a0c
commit d6f3beffb6
10 changed files with 70 additions and 51 deletions

View File

@ -112,10 +112,7 @@ protected override void LoadSamples()
private void updateSlidingSample(ValueChangedEvent<bool> tracking)
{
// note that samples will not start playing if exiting a seek operation in the middle of a slider.
// may be something we want to address at a later point, but not so easy to make happen right now
// (SkinnableSound would need to expose whether the sample is already playing and this logic would need to run in Update).
if (tracking.NewValue && ShouldPlaySamples)
if (tracking.NewValue)
slidingSample?.Play();
else
slidingSample?.Stop();

View File

@ -113,10 +113,7 @@ protected override void LoadSamples()
private void updateSpinningSample(ValueChangedEvent<bool> tracking)
{
// note that samples will not start playing if exiting a seek operation in the middle of a spinner.
// may be something we want to address at a later point, but not so easy to make happen right now
// (SkinnableSound would need to expose whether the sample is already playing and this logic would need to run in Update).
if (tracking.NewValue && ShouldPlaySamples)
if (tracking.NewValue)
{
spinningSample?.Play();
spinningSample?.VolumeTo(1, 200);

View File

@ -22,7 +22,7 @@ namespace osu.Game.Tests.Visual.Gameplay
{
public class TestSceneSkinnableSound : OsuTestScene
{
[Cached]
[Cached(typeof(ISamplePlaybackDisabler))]
private GameplayClock gameplayClock = new GameplayClock(new FramedClock());
private TestSkinSourceContainer skinSource;

View File

@ -360,7 +360,7 @@ protected virtual void ApplySkin(ISkinSource skin, bool allowFallback)
}
[Resolved(canBeNull: true)]
private ISeekableClock seekableClock { get; set; }
private ISamplePlaybackDisabler samplePlaybackDisabler { get; set; }
/// <summary>
/// Calculate the position to be used for sample playback at a specified X position (0..1).
@ -374,18 +374,13 @@ protected double CalculateSamplePlaybackBalance(double position)
return balance_adjust_amount * (userPositionalHitSounds.Value ? position - 0.5f : 0);
}
/// <summary>
/// Whether samples should currently be playing. Will be false during seek operations.
/// </summary>
protected bool ShouldPlaySamples => seekableClock?.IsSeeking != true;
/// <summary>
/// Plays all the hit sounds for this <see cref="DrawableHitObject"/>.
/// This is invoked automatically when this <see cref="DrawableHitObject"/> is hit.
/// </summary>
public virtual void PlaySamples()
{
if (Samples != null && ShouldPlaySamples)
if (Samples != null)
{
Samples.Balance.Value = CalculateSamplePlaybackBalance(SamplePlaybackPosition);
Samples.Play();

View File

@ -107,7 +107,7 @@ private void load(OsuColour colours, GameHost host)
UpdateClockSource();
dependencies.CacheAs(clock);
dependencies.CacheAs<ISeekableClock>(clock);
dependencies.CacheAs<ISamplePlaybackDisabler>(clock);
AddInternal(clock);
// todo: remove caching of this and consume via editorBeatmap?

View File

@ -18,7 +18,7 @@ namespace osu.Game.Screens.Edit
/// <summary>
/// A decoupled clock which adds editor-specific functionality, such as snapping to a user-defined beat divisor.
/// </summary>
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock, ISeekableClock
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock, ISamplePlaybackDisabler
{
public IBindable<Track> Track => track;
@ -32,6 +32,10 @@ public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourc
private readonly DecoupleableInterpolatingFramedClock underlyingClock;
public IBindable<bool> SamplePlaybackDisabled => samplePlaybackDisabled;
private readonly Bindable<bool> samplePlaybackDisabled = new Bindable<bool>();
public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor)
: this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor)
{
@ -167,11 +171,14 @@ public void Start()
public void Stop()
{
samplePlaybackDisabled.Value = true;
underlyingClock.Stop();
}
public bool Seek(double position)
{
samplePlaybackDisabled.Value = true;
ClearTransforms();
return underlyingClock.Seek(position);
}
@ -212,26 +219,34 @@ public bool IsCoupled
private const double transform_time = 300;
public bool IsSeeking { get; private set; }
protected override void Update()
{
base.Update();
if (IsSeeking)
updateSeekingState();
}
private void updateSeekingState()
{
if (samplePlaybackDisabled.Value)
{
bool isPaused = track.Value?.IsRunning != true;
if (track.Value?.IsRunning != true)
{
// seeking in the editor can happen while the track isn't running.
// in this case we always want to expose ourselves as seeking (to avoid sample playback).
return;
}
// we are either running a seek tween or doing an immediate seek.
// in the case of an immediate seek the seeking bool will be set to false after one update.
// this allows for silencing hit sounds and the likes.
IsSeeking = isPaused || Transforms.Any();
samplePlaybackDisabled.Value = Transforms.Any();
}
}
public void SeekTo(double seekDestination)
{
IsSeeking = true;
samplePlaybackDisabled.Value = true;
if (IsRunning)
Seek(seekDestination);

View File

@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play
/// <see cref="IFrameBasedClock"/>, as this should only be done once to ensure accuracy.
/// </remarks>
/// </summary>
public class GameplayClock : IFrameBasedClock, ISeekableClock
public class GameplayClock : IFrameBasedClock, ISamplePlaybackDisabler
{
private readonly IFrameBasedClock underlyingClock;
@ -48,5 +48,7 @@ public void ProcessFrame()
public FrameTimeInfo TimeInfo => underlyingClock.TimeInfo;
public IClock Source => underlyingClock;
public IBindable<bool> SamplePlaybackDisabled => IsPaused;
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Skinning;
namespace osu.Game.Screens.Play
{
/// <summary>
/// Allows a component to disable sample playback dynamically as required.
/// Handled by <see cref="SkinnableSound"/>.
/// </summary>
public interface ISamplePlaybackDisabler
{
/// <summary>
/// Whether sample playback should be disabled (or paused for looping samples).
/// </summary>
IBindable<bool> SamplePlaybackDisabled { get; }
}
}

View File

@ -1,13 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
namespace osu.Game.Screens.Play
{
public interface ISeekableClock
{
/// <summary>
/// Whether an ongoing seek operation is active.
/// </summary>
bool IsSeeking { get; }
}
}

View File

@ -50,25 +50,28 @@ public SkinnableSound(IEnumerable<ISampleInfo> hitSamples)
InternalChild = samplesContainer = new AudioContainer<DrawableSample>();
}
private Bindable<bool> gameplayClockPaused;
private readonly IBindable<bool> samplePlaybackDisabled = new Bindable<bool>();
[BackgroundDependencyLoader(true)]
private void load(GameplayClock gameplayClock)
private void load(ISamplePlaybackDisabler samplePlaybackDisabler)
{
// if in a gameplay context, pause sample playback when gameplay is paused.
gameplayClockPaused = gameplayClock?.IsPaused.GetBoundCopy();
gameplayClockPaused?.BindValueChanged(paused =>
if (samplePlaybackDisabler != null)
{
if (requestedPlaying)
samplePlaybackDisabled.BindTo(samplePlaybackDisabler.SamplePlaybackDisabled);
samplePlaybackDisabled.BindValueChanged(disabled =>
{
if (paused.NewValue)
stop();
// it's not easy to know if a sample has finished playing (to end).
// to keep things simple only resume playing looping samples.
else if (Looping)
play();
}
});
if (requestedPlaying)
{
if (disabled.NewValue)
stop();
// it's not easy to know if a sample has finished playing (to end).
// to keep things simple only resume playing looping samples.
else if (Looping)
play();
}
});
}
}
private bool looping;
@ -94,6 +97,9 @@ public void Play()
private void play()
{
if (samplePlaybackDisabled.Value)
return;
samplesContainer.ForEach(c =>
{
if (PlayWhenZeroVolume || c.AggregateVolume.Value > 0)