mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 08:32:57 +08:00
Merge branch 'master' into fix-storyboard-sample-pausing
This commit is contained in:
commit
367b35bb10
@ -52,6 +52,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.925.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.930.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
@ -7,7 +7,6 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
@ -194,13 +193,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
addSeekStep(0);
|
||||
|
||||
AddStep("adjust track rate", () => MusicController.CurrentTrack.AddAdjustment(AdjustableProperty.Tempo, new BindableDouble(rate)));
|
||||
// autoplay replay frames use track time;
|
||||
// if a spin takes 1000ms in track time and we're playing with a 2x rate adjustment, the spin will take 500ms of *real* time.
|
||||
// therefore we need to apply the rate adjustment to the replay itself to change from track time to real time,
|
||||
// as real time is what we care about for spinners
|
||||
// (so we're making the spin take 1000ms in real time *always*, regardless of the track clock's rate).
|
||||
transformReplay(replay => applyRateAdjustment(replay, rate));
|
||||
AddStep("adjust track rate", () => Player.GameplayClockContainer.UserPlaybackRate.Value = rate);
|
||||
|
||||
addSeekStep(1000);
|
||||
AddAssert("progress almost same", () => Precision.AlmostEquals(expectedProgress, drawableSpinner.Progress, 0.05));
|
||||
|
@ -15,6 +15,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components
|
||||
{
|
||||
private readonly ManualSliderBody body;
|
||||
|
||||
/// <summary>
|
||||
/// Offset in absolute (local) coordinates from the start of the curve.
|
||||
/// </summary>
|
||||
public Vector2 PathStartLocation => body.PathOffset;
|
||||
|
||||
public SliderBodyPiece()
|
||||
{
|
||||
InternalChild = body = new ManualSliderBody
|
||||
|
@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders
|
||||
new OsuMenuItem("Add control point", MenuItemType.Standard, () => addControlPoint(rightClickPosition)),
|
||||
};
|
||||
|
||||
public override Vector2 ScreenSpaceSelectionPoint => ((DrawableSlider)DrawableObject).HeadCircle.ScreenSpaceDrawQuad.Centre;
|
||||
public override Vector2 ScreenSpaceSelectionPoint => BodyPiece.ToScreenSpace(BodyPiece.PathStartLocation);
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => BodyPiece.ReceivePositionalInputAt(screenSpacePos);
|
||||
|
||||
|
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
Tracking.BindValueChanged(updateSlidingSample);
|
||||
}
|
||||
|
||||
private SkinnableSound slidingSample;
|
||||
private PausableSkinnableSound slidingSample;
|
||||
|
||||
protected override void LoadSamples()
|
||||
{
|
||||
@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
||||
clone.Name = "sliderslide";
|
||||
|
||||
AddInternal(slidingSample = new SkinnableSound(clone)
|
||||
AddInternal(slidingSample = new PausableSkinnableSound(clone)
|
||||
{
|
||||
Looping = true
|
||||
});
|
||||
|
@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
isSpinning.BindValueChanged(updateSpinningSample);
|
||||
}
|
||||
|
||||
private SkinnableSound spinningSample;
|
||||
private PausableSkinnableSound spinningSample;
|
||||
private const float spinning_sample_initial_frequency = 1.0f;
|
||||
private const float spinning_sample_modulated_base_frequency = 0.5f;
|
||||
|
||||
@ -102,7 +102,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
||||
var clone = HitObject.SampleControlPoint.ApplyTo(firstSample);
|
||||
clone.Name = "spinnerspin";
|
||||
|
||||
AddInternal(spinningSample = new SkinnableSound(clone)
|
||||
AddInternal(spinningSample = new PausableSkinnableSound(clone)
|
||||
{
|
||||
Volume = { Value = 0 },
|
||||
Looping = true,
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -93,7 +92,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
base.LoadComplete();
|
||||
|
||||
drawableSpinner.RotationTracker.Complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200));
|
||||
drawableSpinner.State.BindValueChanged(updateStateTransforms, true);
|
||||
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
@ -123,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation;
|
||||
}
|
||||
|
||||
private void updateStateTransforms(ValueChangedEvent<ArmedState> state)
|
||||
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||
{
|
||||
centre.ScaleTo(0);
|
||||
mainContainer.ScaleTo(0);
|
||||
@ -144,11 +143,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
}
|
||||
|
||||
// transforms we have from completing the spinner will be rolled back, so reapply immediately.
|
||||
updateComplete(state.NewValue == ArmedState.Hit, 0);
|
||||
updateComplete(state == ArmedState.Hit, 0);
|
||||
|
||||
using (BeginDelayedSequence(spinner.Duration, true))
|
||||
{
|
||||
switch (state.NewValue)
|
||||
switch (state)
|
||||
{
|
||||
case ArmedState.Hit:
|
||||
this.ScaleTo(Scale * 1.2f, 320, Easing.Out);
|
||||
@ -185,5 +184,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (drawableSpinner != null)
|
||||
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,11 +2,13 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
@ -77,6 +79,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
|
||||
private bool rotationTransferred;
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private GameplayClock gameplayClock { get; set; }
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -126,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||
currentRotation += angle;
|
||||
// rate has to be applied each frame, because it's not guaranteed to be constant throughout playback
|
||||
// (see: ModTimeRamp)
|
||||
RateAdjustedRotation += (float)(Math.Abs(angle) * Clock.Rate);
|
||||
RateAdjustedRotation += (float)(Math.Abs(angle) * (gameplayClock?.TrueGameplayRate ?? Clock.Rate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -72,10 +71,10 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
base.LoadComplete();
|
||||
|
||||
this.FadeOut();
|
||||
drawableSpinner.State.BindValueChanged(updateStateTransforms, true);
|
||||
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
||||
}
|
||||
|
||||
private void updateStateTransforms(ValueChangedEvent<ArmedState> state)
|
||||
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||
{
|
||||
var spinner = (Spinner)drawableSpinner.HitObject;
|
||||
|
||||
@ -95,5 +94,13 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
|
||||
Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * 0.2f));
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (drawableSpinner != null)
|
||||
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
@ -86,10 +85,10 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
base.LoadComplete();
|
||||
|
||||
this.FadeOut();
|
||||
drawableSpinner.State.BindValueChanged(updateStateTransforms, true);
|
||||
drawableSpinner.ApplyCustomUpdateState += updateStateTransforms;
|
||||
}
|
||||
|
||||
private void updateStateTransforms(ValueChangedEvent<ArmedState> state)
|
||||
private void updateStateTransforms(DrawableHitObject drawableHitObject, ArmedState state)
|
||||
{
|
||||
var spinner = drawableSpinner.HitObject;
|
||||
|
||||
@ -127,5 +126,13 @@ namespace osu.Game.Rulesets.Osu.Skinning
|
||||
|
||||
return (float)barCount / total_bars * final_metre_height;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
if (drawableSpinner != null)
|
||||
drawableSpinner.ApplyCustomUpdateState -= updateStateTransforms;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,9 +42,9 @@ namespace osu.Game.Rulesets.Taiko.Audio
|
||||
}
|
||||
}
|
||||
|
||||
private SkinnableSound addSound(HitSampleInfo hitSampleInfo, double lifetimeStart, double lifetimeEnd)
|
||||
private PausableSkinnableSound addSound(HitSampleInfo hitSampleInfo, double lifetimeStart, double lifetimeEnd)
|
||||
{
|
||||
var drawable = new SkinnableSound(hitSampleInfo)
|
||||
var drawable = new PausableSkinnableSound(hitSampleInfo)
|
||||
{
|
||||
LifetimeStart = lifetimeStart,
|
||||
LifetimeEnd = lifetimeEnd
|
||||
@ -57,8 +57,8 @@ namespace osu.Game.Rulesets.Taiko.Audio
|
||||
|
||||
public class DrumSample
|
||||
{
|
||||
public SkinnableSound Centre;
|
||||
public SkinnableSound Rim;
|
||||
public PausableSkinnableSound Centre;
|
||||
public PausableSkinnableSound Rim;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Rulesets.Osu;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Timing;
|
||||
|
||||
@ -17,16 +17,26 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
[Cached(typeof(IBeatSnapProvider))]
|
||||
private readonly EditorBeatmap editorBeatmap;
|
||||
|
||||
protected override bool ScrollUsingMouseWheel => false;
|
||||
|
||||
public TestSceneTimingScreen()
|
||||
{
|
||||
editorBeatmap = new EditorBeatmap(new OsuBeatmap());
|
||||
editorBeatmap = new EditorBeatmap(CreateBeatmap(new OsuRuleset().RulesetInfo));
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
Beatmap.Value = CreateWorkingBeatmap(editorBeatmap.PlayableBeatmap);
|
||||
Beatmap.Disabled = true;
|
||||
|
||||
Child = new TimingScreen();
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
Beatmap.Disabled = false;
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
private GameplayClock gameplayClock = new GameplayClock(new FramedClock());
|
||||
|
||||
private TestSkinSourceContainer skinSource;
|
||||
private SkinnableSound skinnableSound;
|
||||
private PausableSkinnableSound skinnableSound;
|
||||
|
||||
[SetUp]
|
||||
public void SetUp() => Schedule(() =>
|
||||
@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
Clock = gameplayClock,
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Child = skinnableSound = new SkinnableSound(new SampleInfo("normal-sliderslide"))
|
||||
Child = skinnableSound = new PausableSkinnableSound(new SampleInfo("normal-sliderslide"))
|
||||
},
|
||||
};
|
||||
});
|
||||
|
26
osu.Game/Extensions/EditorDisplayExtensions.cs
Normal file
26
osu.Game/Extensions/EditorDisplayExtensions.cs
Normal file
@ -0,0 +1,26 @@
|
||||
// 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 System;
|
||||
|
||||
namespace osu.Game.Extensions
|
||||
{
|
||||
public static class EditorDisplayExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get an editor formatted string (mm:ss:mss)
|
||||
/// </summary>
|
||||
/// <param name="milliseconds">A time value in milliseconds.</param>
|
||||
/// <returns>An editor formatted display string.</returns>
|
||||
public static string ToEditorFormattedString(this double milliseconds) =>
|
||||
ToEditorFormattedString(TimeSpan.FromMilliseconds(milliseconds));
|
||||
|
||||
/// <summary>
|
||||
/// Get an editor formatted string (mm:ss:mss)
|
||||
/// </summary>
|
||||
/// <param name="timeSpan">A time value.</param>
|
||||
/// <returns>An editor formatted display string.</returns>
|
||||
public static string ToEditorFormattedString(this TimeSpan timeSpan) =>
|
||||
$"{(timeSpan < TimeSpan.Zero ? "-" : string.Empty)}{timeSpan:mm\\:ss\\:fff}";
|
||||
}
|
||||
}
|
@ -52,10 +52,10 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public class NightcoreBeatContainer : BeatSyncedContainer
|
||||
{
|
||||
private SkinnableSound hatSample;
|
||||
private SkinnableSound clapSample;
|
||||
private SkinnableSound kickSample;
|
||||
private SkinnableSound finishSample;
|
||||
private PausableSkinnableSound hatSample;
|
||||
private PausableSkinnableSound clapSample;
|
||||
private PausableSkinnableSound kickSample;
|
||||
private PausableSkinnableSound finishSample;
|
||||
|
||||
private int? firstBeat;
|
||||
|
||||
@ -69,10 +69,10 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
InternalChildren = new Drawable[]
|
||||
{
|
||||
hatSample = new SkinnableSound(new SampleInfo("nightcore-hat")),
|
||||
clapSample = new SkinnableSound(new SampleInfo("nightcore-clap")),
|
||||
kickSample = new SkinnableSound(new SampleInfo("nightcore-kick")),
|
||||
finishSample = new SkinnableSound(new SampleInfo("nightcore-finish")),
|
||||
hatSample = new PausableSkinnableSound(new SampleInfo("nightcore-hat")),
|
||||
clapSample = new PausableSkinnableSound(new SampleInfo("nightcore-clap")),
|
||||
kickSample = new PausableSkinnableSound(new SampleInfo("nightcore-kick")),
|
||||
finishSample = new PausableSkinnableSound(new SampleInfo("nightcore-finish")),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -17,7 +17,6 @@ using osu.Game.Rulesets.Objects.Types;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
using osu.Game.Skinning;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Screens.Play;
|
||||
using osuTK.Graphics;
|
||||
|
||||
namespace osu.Game.Rulesets.Objects.Drawables
|
||||
@ -34,7 +33,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
/// </summary>
|
||||
public readonly Bindable<Color4> AccentColour = new Bindable<Color4>(Color4.Gray);
|
||||
|
||||
protected SkinnableSound Samples { get; private set; }
|
||||
protected PausableSkinnableSound Samples { get; private set; }
|
||||
|
||||
public virtual IEnumerable<HitSampleInfo> GetSamples() => HitObject.Samples;
|
||||
|
||||
@ -179,7 +178,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
+ $" This is an indication that {nameof(HitObject.ApplyDefaults)} has not been invoked on {this}.");
|
||||
}
|
||||
|
||||
Samples = new SkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)));
|
||||
Samples = new PausableSkinnableSound(samples.Select(s => HitObject.SampleControlPoint.ApplyTo(s)));
|
||||
AddInternal(Samples);
|
||||
}
|
||||
|
||||
@ -359,9 +358,6 @@ namespace osu.Game.Rulesets.Objects.Drawables
|
||||
{
|
||||
}
|
||||
|
||||
[Resolved(canBeNull: true)]
|
||||
private ISamplePlaybackDisabler samplePlaybackDisabler { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Calculate the position to be used for sample playback at a specified X position (0..1).
|
||||
/// </summary>
|
||||
|
@ -2,7 +2,10 @@
|
||||
// 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.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
@ -59,7 +62,7 @@ namespace osu.Game.Rulesets.UI
|
||||
{
|
||||
if (clock != null)
|
||||
{
|
||||
stabilityGameplayClock.ParentGameplayClock = parentGameplayClock = clock;
|
||||
parentGameplayClock = stabilityGameplayClock.ParentGameplayClock = clock;
|
||||
GameplayClock.IsPaused.BindTo(clock.IsPaused);
|
||||
}
|
||||
}
|
||||
@ -215,7 +218,9 @@ namespace osu.Game.Rulesets.UI
|
||||
|
||||
private class StabilityGameplayClock : GameplayClock
|
||||
{
|
||||
public IFrameBasedClock ParentGameplayClock;
|
||||
public GameplayClock ParentGameplayClock;
|
||||
|
||||
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => ParentGameplayClock?.NonGameplayAdjustments ?? Enumerable.Empty<Bindable<double>>();
|
||||
|
||||
public StabilityGameplayClock(FramedClock underlyingClock)
|
||||
: base(underlyingClock)
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components
|
||||
@ -35,9 +35,7 @@ namespace osu.Game.Screens.Edit.Components
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
var timespan = TimeSpan.FromMilliseconds(editorClock.CurrentTime);
|
||||
trackTimer.Text = $"{(timespan < TimeSpan.Zero ? "-" : string.Empty)}{timespan:mm\\:ss\\:fff}";
|
||||
trackTimer.Text = editorClock.CurrentTime.ToEditorFormattedString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -14,9 +14,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
{
|
||||
public class TimelineArea : Container
|
||||
{
|
||||
private readonly Timeline timeline = new Timeline { RelativeSizeAxes = Axes.Both };
|
||||
public readonly Timeline Timeline = new Timeline { RelativeSizeAxes = Axes.Both };
|
||||
|
||||
protected override Container<Drawable> Content => timeline;
|
||||
protected override Container<Drawable> Content => Timeline;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
@ -107,7 +107,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
}
|
||||
}
|
||||
},
|
||||
timeline
|
||||
Timeline
|
||||
},
|
||||
},
|
||||
ColumnDimensions = new[]
|
||||
@ -121,9 +121,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
waveformCheckbox.Current.Value = true;
|
||||
|
||||
timeline.WaveformVisible.BindTo(waveformCheckbox.Current);
|
||||
Timeline.WaveformVisible.BindTo(waveformCheckbox.Current);
|
||||
}
|
||||
|
||||
private void changeZoom(float change) => timeline.Zoom += change;
|
||||
private void changeZoom(float change) => Timeline.Zoom += change;
|
||||
}
|
||||
}
|
||||
|
@ -3,18 +3,23 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Colour;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Effects;
|
||||
using osu.Framework.Graphics.Primitives;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -34,11 +39,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
private readonly List<Container> shadowComponents = new List<Container>();
|
||||
|
||||
private DrawableHitObject drawableHitObject;
|
||||
|
||||
private Bindable<Color4> comboColour;
|
||||
|
||||
private readonly Container mainComponents;
|
||||
|
||||
private readonly OsuSpriteText comboIndexText;
|
||||
|
||||
private Bindable<int> comboIndex;
|
||||
|
||||
private const float thickness = 5;
|
||||
|
||||
private const float shadow_radius = 5;
|
||||
|
||||
private const float circle_size = 16;
|
||||
private const float circle_size = 24;
|
||||
|
||||
public TimelineHitObjectBlueprint(HitObject hitObject)
|
||||
: base(hitObject)
|
||||
@ -54,14 +69,28 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
RelativeSizeAxes = Axes.X;
|
||||
AutoSizeAxes = Axes.Y;
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
{
|
||||
mainComponents = new Container
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.CentreLeft,
|
||||
RelativeSizeAxes = Axes.X,
|
||||
AutoSizeAxes = Axes.Y,
|
||||
},
|
||||
comboIndexText = new OsuSpriteText
|
||||
{
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.Centre,
|
||||
Font = OsuFont.Numeric.With(size: circle_size / 2, weight: FontWeight.Black),
|
||||
},
|
||||
});
|
||||
|
||||
circle = new Circle
|
||||
{
|
||||
Size = new Vector2(circle_size),
|
||||
Anchor = Anchor.CentreLeft,
|
||||
Origin = Anchor.Centre,
|
||||
RelativePositionAxes = Axes.X,
|
||||
AlwaysPresent = true,
|
||||
Colour = Color4.White,
|
||||
EdgeEffect = new EdgeEffectParameters
|
||||
{
|
||||
Type = EdgeEffectType.Shadow,
|
||||
@ -77,7 +106,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
DragBar dragBarUnderlay;
|
||||
Container extensionBar;
|
||||
|
||||
AddRangeInternal(new Drawable[]
|
||||
mainComponents.AddRange(new Drawable[]
|
||||
{
|
||||
extensionBar = new Container
|
||||
{
|
||||
@ -117,12 +146,54 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
}
|
||||
else
|
||||
{
|
||||
AddInternal(circle);
|
||||
mainComponents.Add(circle);
|
||||
}
|
||||
|
||||
updateShadows();
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(HitObjectComposer composer)
|
||||
{
|
||||
if (composer != null)
|
||||
{
|
||||
// best effort to get the drawable representation for grabbing colour and what not.
|
||||
drawableHitObject = composer.HitObjects.FirstOrDefault(d => d.HitObject == HitObject);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
if (HitObject is IHasComboInformation comboInfo)
|
||||
{
|
||||
comboIndex = comboInfo.IndexInCurrentComboBindable.GetBoundCopy();
|
||||
comboIndex.BindValueChanged(combo =>
|
||||
{
|
||||
comboIndexText.Text = (combo.NewValue + 1).ToString();
|
||||
}, true);
|
||||
}
|
||||
|
||||
if (drawableHitObject != null)
|
||||
{
|
||||
comboColour = drawableHitObject.AccentColour.GetBoundCopy();
|
||||
comboColour.BindValueChanged(colour =>
|
||||
{
|
||||
if (HitObject is IHasDuration)
|
||||
mainComponents.Colour = ColourInfo.GradientHorizontal(drawableHitObject.AccentColour.Value, Color4.White);
|
||||
else
|
||||
mainComponents.Colour = drawableHitObject.AccentColour.Value;
|
||||
|
||||
var col = mainComponents.Colour.TopLeft.Linear;
|
||||
float brightness = col.R + col.G + col.B;
|
||||
|
||||
// decide the combo index colour based on brightness?
|
||||
comboIndexText.Colour = brightness > 0.5f ? Color4.Black : Color4.White;
|
||||
}, true);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
@ -1,8 +1,12 @@
|
||||
// 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.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
using osu.Game.Skinning;
|
||||
@ -18,11 +22,23 @@ namespace osu.Game.Screens.Edit.Compose
|
||||
{
|
||||
}
|
||||
|
||||
protected override Drawable CreateMainContent()
|
||||
private Ruleset ruleset;
|
||||
|
||||
protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent)
|
||||
{
|
||||
var ruleset = Beatmap.Value.BeatmapInfo.Ruleset?.CreateInstance();
|
||||
var dependencies = new DependencyContainer(base.CreateChildDependencies(parent));
|
||||
|
||||
ruleset = parent.Get<IBindable<WorkingBeatmap>>().Value.BeatmapInfo.Ruleset?.CreateInstance();
|
||||
composer = ruleset?.CreateHitObjectComposer();
|
||||
|
||||
// make the composer available to the timeline and other components in this screen.
|
||||
dependencies.CacheAs(composer);
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
protected override Drawable CreateMainContent()
|
||||
{
|
||||
if (ruleset == null || composer == null)
|
||||
return new ScreenWhiteBox.UnderConstructionMessage(ruleset == null ? "This beatmap" : $"{ruleset.Description}'s composer");
|
||||
|
||||
|
@ -115,10 +115,18 @@ namespace osu.Game.Screens.Edit
|
||||
new TimelineTickDisplay(),
|
||||
CreateTimelineContent(),
|
||||
}
|
||||
}, timelineContainer.Add);
|
||||
}, t =>
|
||||
{
|
||||
timelineContainer.Add(t);
|
||||
OnTimelineLoaded(t);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
protected virtual void OnTimelineLoaded(TimelineArea timelineArea)
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract Drawable CreateMainContent();
|
||||
|
||||
protected virtual Drawable CreateTimelineContent() => new Container();
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Extensions;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
@ -89,7 +90,7 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
},
|
||||
new OsuSpriteText
|
||||
{
|
||||
Text = $"{group.Time:n0}ms",
|
||||
Text = group.Time.ToEditorFormattedString(),
|
||||
Font = OsuFont.GetFont(size: text_size, weight: FontWeight.Bold)
|
||||
},
|
||||
new ControlGroupAttributes(group),
|
||||
|
@ -12,6 +12,8 @@ using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Timing
|
||||
@ -29,6 +31,11 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
{
|
||||
}
|
||||
|
||||
protected override Drawable CreateTimelineContent() => new ControlPointPart
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
};
|
||||
|
||||
protected override Drawable CreateMainContent() => new GridContainer
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
@ -58,6 +65,12 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
});
|
||||
}
|
||||
|
||||
protected override void OnTimelineLoaded(TimelineArea timelineArea)
|
||||
{
|
||||
base.OnTimelineLoaded(timelineArea);
|
||||
timelineArea.Timeline.Zoom = timelineArea.Timeline.MinZoom;
|
||||
}
|
||||
|
||||
public class ControlPointList : CompositeDrawable
|
||||
{
|
||||
private OsuButton deleteButton;
|
||||
@ -129,11 +142,12 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
selectedGroup.BindValueChanged(selected => { deleteButton.Enabled.Value = selected.NewValue != null; }, true);
|
||||
|
||||
controlGroups = Beatmap.Value.Beatmap.ControlPointInfo.Groups.GetBoundCopy();
|
||||
controlGroups.CollectionChanged += (sender, args) => createContent();
|
||||
createContent();
|
||||
}
|
||||
|
||||
private void createContent() => table.ControlGroups = controlGroups;
|
||||
controlGroups.BindCollectionChanged((sender, args) =>
|
||||
{
|
||||
table.ControlGroups = controlGroups;
|
||||
}, true);
|
||||
}
|
||||
|
||||
private void delete()
|
||||
{
|
||||
|
@ -103,12 +103,17 @@ namespace osu.Game.Screens.Edit.Timing
|
||||
private const double sane_maximum = 240;
|
||||
|
||||
private readonly BindableNumber<double> beatLengthBindable = new TimingControlPoint().BeatLengthBindable;
|
||||
private readonly BindableDouble bpmBindable = new BindableDouble();
|
||||
|
||||
private readonly BindableDouble bpmBindable = new BindableDouble(60000 / TimingControlPoint.DEFAULT_BEAT_LENGTH)
|
||||
{
|
||||
MinValue = sane_minimum,
|
||||
MaxValue = sane_maximum,
|
||||
};
|
||||
|
||||
public BPMSlider()
|
||||
{
|
||||
beatLengthBindable.BindValueChanged(beatLength => updateCurrent(beatLengthToBpm(beatLength.NewValue)), true);
|
||||
bpmBindable.BindValueChanged(bpm => bpmBindable.Default = beatLengthBindable.Value = beatLengthToBpm(bpm.NewValue));
|
||||
bpmBindable.BindValueChanged(bpm => beatLengthBindable.Value = beatLengthToBpm(bpm.NewValue));
|
||||
|
||||
base.Bindable = bpmBindable;
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Timing;
|
||||
|
||||
@ -20,6 +22,11 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public readonly BindableBool IsPaused = new BindableBool();
|
||||
|
||||
/// <summary>
|
||||
/// All adjustments applied to this clock which don't come from gameplay or mods.
|
||||
/// </summary>
|
||||
public virtual IEnumerable<Bindable<double>> NonGameplayAdjustments => Enumerable.Empty<Bindable<double>>();
|
||||
|
||||
public GameplayClock(IFrameBasedClock underlyingClock)
|
||||
{
|
||||
this.underlyingClock = underlyingClock;
|
||||
@ -29,6 +36,23 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
public double Rate => underlyingClock.Rate;
|
||||
|
||||
/// <summary>
|
||||
/// The rate of gameplay when playback is at 100%.
|
||||
/// This excludes any seeking / user adjustments.
|
||||
/// </summary>
|
||||
public double TrueGameplayRate
|
||||
{
|
||||
get
|
||||
{
|
||||
double baseRate = Rate;
|
||||
|
||||
foreach (var adjustment in NonGameplayAdjustments)
|
||||
baseRate /= adjustment.Value;
|
||||
|
||||
return baseRate;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsRunning => underlyingClock.IsRunning;
|
||||
|
||||
/// <summary>
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
@ -50,9 +51,11 @@ namespace osu.Game.Screens.Play
|
||||
/// <summary>
|
||||
/// The final clock which is exposed to underlying components.
|
||||
/// </summary>
|
||||
[Cached]
|
||||
public GameplayClock GameplayClock => localGameplayClock;
|
||||
|
||||
[Cached(typeof(GameplayClock))]
|
||||
[Cached(typeof(ISamplePlaybackDisabler))]
|
||||
public readonly GameplayClock GameplayClock;
|
||||
private readonly LocalGameplayClock localGameplayClock;
|
||||
|
||||
private Bindable<double> userAudioOffset;
|
||||
|
||||
@ -80,7 +83,7 @@ namespace osu.Game.Screens.Play
|
||||
userOffsetClock = new HardwareCorrectionOffsetClock(platformOffsetClock);
|
||||
|
||||
// the clock to be exposed via DI to children.
|
||||
GameplayClock = new GameplayClock(userOffsetClock);
|
||||
localGameplayClock = new LocalGameplayClock(userOffsetClock);
|
||||
|
||||
GameplayClock.IsPaused.BindTo(IsPaused);
|
||||
}
|
||||
@ -201,7 +204,9 @@ namespace osu.Game.Screens.Play
|
||||
protected override void Update()
|
||||
{
|
||||
if (!IsPaused.Value)
|
||||
{
|
||||
userOffsetClock.ProcessFrame();
|
||||
}
|
||||
|
||||
base.Update();
|
||||
}
|
||||
@ -216,6 +221,9 @@ namespace osu.Game.Screens.Play
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
||||
track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||
|
||||
localGameplayClock.MutableNonGameplayAdjustments.Add(pauseFreqAdjust);
|
||||
localGameplayClock.MutableNonGameplayAdjustments.Add(UserPlaybackRate);
|
||||
|
||||
speedAdjustmentsApplied = true;
|
||||
}
|
||||
|
||||
@ -232,9 +240,24 @@ namespace osu.Game.Screens.Play
|
||||
track.RemoveAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
||||
track.RemoveAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||
|
||||
localGameplayClock.MutableNonGameplayAdjustments.Remove(pauseFreqAdjust);
|
||||
localGameplayClock.MutableNonGameplayAdjustments.Remove(UserPlaybackRate);
|
||||
|
||||
speedAdjustmentsApplied = false;
|
||||
}
|
||||
|
||||
private class LocalGameplayClock : GameplayClock
|
||||
{
|
||||
public readonly List<Bindable<double>> MutableNonGameplayAdjustments = new List<Bindable<double>>();
|
||||
|
||||
public override IEnumerable<Bindable<double>> NonGameplayAdjustments => MutableNonGameplayAdjustments;
|
||||
|
||||
public LocalGameplayClock(FramedOffsetClock underlyingClock)
|
||||
: base(underlyingClock)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class HardwareCorrectionOffsetClock : FramedOffsetClock
|
||||
{
|
||||
// we always want to apply the same real-time offset, so it should be adjusted by the difference in playback rate (from realtime) to achieve this.
|
||||
|
@ -8,7 +8,7 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows a component to disable sample playback dynamically as required.
|
||||
/// Handled by <see cref="SkinnableSound"/>.
|
||||
/// Handled by <see cref="PausableSkinnableSound"/>.
|
||||
/// </summary>
|
||||
public interface ISamplePlaybackDisabler
|
||||
{
|
||||
|
@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play
|
||||
AddButton("Retry", colours.YellowDark, () => OnRetry?.Invoke());
|
||||
AddButton("Quit", new Color4(170, 27, 39, 255), () => OnQuit?.Invoke());
|
||||
|
||||
AddInternal(pauseLoop = new UnpausableSkinnableSound(new SampleInfo("pause-loop"))
|
||||
AddInternal(pauseLoop = new SkinnableSound(new SampleInfo("pause-loop"))
|
||||
{
|
||||
Looping = true,
|
||||
Volume = { Value = 0 }
|
||||
@ -54,15 +54,5 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
pauseLoop.VolumeTo(0, TRANSITION_DURATION, Easing.OutQuad).Finally(_ => pauseLoop.Stop());
|
||||
}
|
||||
|
||||
private class UnpausableSkinnableSound : SkinnableSound
|
||||
{
|
||||
protected override bool PlayWhenPaused => true;
|
||||
|
||||
public UnpausableSkinnableSound(SampleInfo sampleInfo)
|
||||
: base(sampleInfo)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
66
osu.Game/Skinning/PausableSkinnableSound.cs
Normal file
66
osu.Game/Skinning/PausableSkinnableSound.cs
Normal file
@ -0,0 +1,66 @@
|
||||
// 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 System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
public class PausableSkinnableSound : SkinnableSound
|
||||
{
|
||||
protected bool RequestedPlaying { get; private set; }
|
||||
|
||||
public PausableSkinnableSound(ISampleInfo hitSamples)
|
||||
: base(hitSamples)
|
||||
{
|
||||
}
|
||||
|
||||
public PausableSkinnableSound(IEnumerable<ISampleInfo> hitSamples)
|
||||
: base(hitSamples)
|
||||
{
|
||||
}
|
||||
|
||||
private readonly IBindable<bool> samplePlaybackDisabled = new Bindable<bool>();
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(ISamplePlaybackDisabler samplePlaybackDisabler)
|
||||
{
|
||||
// if in a gameplay context, pause sample playback when gameplay is paused.
|
||||
if (samplePlaybackDisabler != null)
|
||||
{
|
||||
samplePlaybackDisabled.BindTo(samplePlaybackDisabler.SamplePlaybackDisabled);
|
||||
samplePlaybackDisabled.BindValueChanged(disabled =>
|
||||
{
|
||||
if (RequestedPlaying)
|
||||
{
|
||||
if (disabled.NewValue)
|
||||
base.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)
|
||||
base.Play();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public override void Play()
|
||||
{
|
||||
RequestedPlaying = true;
|
||||
|
||||
if (samplePlaybackDisabled.Value)
|
||||
return;
|
||||
|
||||
base.Play();
|
||||
}
|
||||
|
||||
public override void Stop()
|
||||
{
|
||||
RequestedPlaying = false;
|
||||
base.Stop();
|
||||
}
|
||||
}
|
||||
}
|
@ -11,7 +11,6 @@ using osu.Framework.Extensions.IEnumerableExtensions;
|
||||
using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Skinning
|
||||
{
|
||||
@ -22,11 +21,6 @@ namespace osu.Game.Skinning
|
||||
[Resolved]
|
||||
private ISampleStore samples { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether playback of this sound has been requested, regardless of whether it could be played or not (due to being paused, for instance).
|
||||
/// </summary>
|
||||
protected bool PlaybackRequested;
|
||||
|
||||
public override bool RemoveWhenNotAlive => false;
|
||||
public override bool RemoveCompletedTransforms => false;
|
||||
|
||||
@ -40,8 +34,6 @@ namespace osu.Game.Skinning
|
||||
/// </remarks>
|
||||
protected bool PlayWhenZeroVolume => Looping;
|
||||
|
||||
protected virtual bool PlayWhenPaused => false;
|
||||
|
||||
protected readonly AudioContainer<DrawableSample> SamplesContainer;
|
||||
|
||||
public SkinnableSound(ISampleInfo hitSamples)
|
||||
@ -55,30 +47,6 @@ namespace osu.Game.Skinning
|
||||
InternalChild = SamplesContainer = new AudioContainer<DrawableSample>();
|
||||
}
|
||||
|
||||
private readonly IBindable<bool> samplePlaybackDisabled = new Bindable<bool>();
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(ISamplePlaybackDisabler samplePlaybackDisabler)
|
||||
{
|
||||
// if in a gameplay context, pause sample playback when gameplay is paused.
|
||||
if (samplePlaybackDisabler != null)
|
||||
{
|
||||
samplePlaybackDisabled.BindTo(samplePlaybackDisabler.SamplePlaybackDisabled);
|
||||
samplePlaybackDisabled.BindValueChanged(disabled =>
|
||||
{
|
||||
if (PlaybackRequested)
|
||||
{
|
||||
if (disabled.NewValue && !PlayWhenPaused)
|
||||
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;
|
||||
|
||||
public bool Looping
|
||||
@ -94,17 +62,8 @@ namespace osu.Game.Skinning
|
||||
}
|
||||
}
|
||||
|
||||
public void Play()
|
||||
public virtual void Play()
|
||||
{
|
||||
PlaybackRequested = true;
|
||||
play();
|
||||
}
|
||||
|
||||
private void play()
|
||||
{
|
||||
if (samplePlaybackDisabled.Value && !PlayWhenPaused)
|
||||
return;
|
||||
|
||||
SamplesContainer.ForEach(c =>
|
||||
{
|
||||
if (PlayWhenZeroVolume || c.AggregateVolume.Value > 0)
|
||||
@ -112,13 +71,7 @@ namespace osu.Game.Skinning
|
||||
});
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
PlaybackRequested = false;
|
||||
stop();
|
||||
}
|
||||
|
||||
private void stop()
|
||||
public virtual void Stop()
|
||||
{
|
||||
SamplesContainer.ForEach(c => c.Stop());
|
||||
}
|
||||
@ -153,7 +106,7 @@ namespace osu.Game.Skinning
|
||||
|
||||
// Start playback internally for the new samples if the previous ones were playing beforehand.
|
||||
if (wasPlaying)
|
||||
play();
|
||||
Play();
|
||||
}
|
||||
|
||||
#region Re-expose AudioContainer
|
||||
|
@ -10,7 +10,7 @@ using osu.Game.Skinning;
|
||||
|
||||
namespace osu.Game.Storyboards.Drawables
|
||||
{
|
||||
public class DrawableStoryboardSample : SkinnableSound
|
||||
public class DrawableStoryboardSample : PausableSkinnableSound
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount of time allowable beyond the start time of the sample, for the sample to start.
|
||||
@ -60,7 +60,7 @@ namespace osu.Game.Storyboards.Drawables
|
||||
{
|
||||
// We've passed the start time of the sample. We only play the sample if we're within an allowable range
|
||||
// from the sample's start, to reduce layering if we've been fast-forwarded far into the future
|
||||
if (!PlaybackRequested && Time.Current - sampleInfo.StartTime < allowable_late_start)
|
||||
if (!RequestedPlaying && Time.Current - sampleInfo.StartTime < allowable_late_start)
|
||||
Play();
|
||||
|
||||
// In the case that the user rewinds to a point far behind the start time of the sample,
|
||||
|
@ -20,6 +20,8 @@ namespace osu.Game.Tests.Visual
|
||||
protected readonly BindableBeatDivisor BeatDivisor = new BindableBeatDivisor();
|
||||
protected new readonly EditorClock Clock;
|
||||
|
||||
protected virtual bool ScrollUsingMouseWheel => true;
|
||||
|
||||
protected EditorClockTestScene()
|
||||
{
|
||||
Clock = new EditorClock(new ControlPointInfo(), 5000, BeatDivisor) { IsCoupled = false };
|
||||
@ -57,6 +59,9 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
protected override bool OnScroll(ScrollEvent e)
|
||||
{
|
||||
if (!ScrollUsingMouseWheel)
|
||||
return false;
|
||||
|
||||
if (e.ScrollDelta.Y > 0)
|
||||
Clock.SeekBackward(true);
|
||||
else
|
||||
|
@ -24,7 +24,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.925.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.930.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
||||
<PackageReference Include="Sentry" Version="2.1.6" />
|
||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||
|
@ -70,7 +70,7 @@
|
||||
<Reference Include="System.Net.Http" />
|
||||
</ItemGroup>
|
||||
<ItemGroup Label="Package References">
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.925.0" />
|
||||
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.930.0" />
|
||||
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.904.0" />
|
||||
</ItemGroup>
|
||||
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
|
||||
@ -80,7 +80,7 @@
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.925.0" />
|
||||
<PackageReference Include="ppy.osu.Framework" Version="2020.930.0" />
|
||||
<PackageReference Include="SharpCompress" Version="0.26.0" />
|
||||
<PackageReference Include="NUnit" Version="3.12.0" />
|
||||
<PackageReference Include="SharpRaven" Version="2.4.0" />
|
||||
|
Loading…
Reference in New Issue
Block a user