mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 09:27:29 +08:00
Move track to MusicController, compiles
This commit is contained in:
parent
641279ec3e
commit
6e42b8219c
@ -343,7 +343,7 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
judgementResults = new List<JudgementResult>();
|
||||
});
|
||||
|
||||
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||
AddUntilStep("Beatmap at 0", () => MusicController.CurrentTrackTime == 0);
|
||||
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
|
||||
}
|
||||
|
@ -385,7 +385,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
judgementResults = new List<JudgementResult>();
|
||||
});
|
||||
|
||||
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||
AddUntilStep("Beatmap at 0", () => MusicController.CurrentTrackTime == 0);
|
||||
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
|
||||
}
|
||||
|
@ -366,7 +366,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
judgementResults = new List<JudgementResult>();
|
||||
});
|
||||
|
||||
AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0);
|
||||
AddUntilStep("Beatmap at 0", () => MusicController.CurrentTrackTime == 0);
|
||||
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
|
||||
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
|
||||
}
|
||||
|
@ -22,7 +22,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables;
|
||||
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
|
||||
using osu.Game.Storyboards;
|
||||
using osuTK;
|
||||
using static osu.Game.Tests.Visual.OsuTestScene.ClockBackedTestWorkingBeatmap;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
@ -32,8 +31,6 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Resolved]
|
||||
private AudioManager audioManager { get; set; }
|
||||
|
||||
private TrackVirtualManual track;
|
||||
|
||||
protected override bool Autoplay => autoplay;
|
||||
private bool autoplay;
|
||||
|
||||
@ -44,11 +41,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
private const double fade_in_modifier = -1200;
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
{
|
||||
var working = new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||
track = (TrackVirtualManual)working.Track;
|
||||
return working;
|
||||
}
|
||||
=> new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(RulesetConfigCache configCache)
|
||||
@ -72,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
AddStep("enable autoplay", () => autoplay = true);
|
||||
base.SetUpSteps();
|
||||
AddUntilStep("wait for track to start running", () => track.IsRunning);
|
||||
AddUntilStep("wait for track to start running", () => MusicController.IsPlaying);
|
||||
|
||||
double startTime = hitObjects[sliderIndex].StartTime;
|
||||
retrieveDrawableSlider(sliderIndex);
|
||||
@ -97,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
AddStep("have autoplay", () => autoplay = true);
|
||||
base.SetUpSteps();
|
||||
AddUntilStep("wait for track to start running", () => track.IsRunning);
|
||||
AddUntilStep("wait for track to start running", () => MusicController.IsPlaying);
|
||||
|
||||
double startTime = hitObjects[sliderIndex].StartTime;
|
||||
retrieveDrawableSlider(sliderIndex);
|
||||
@ -201,7 +194,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private void addSeekStep(double time)
|
||||
{
|
||||
AddStep($"seek to {time}", () => track.Seek(time));
|
||||
AddStep($"seek to {time}", () => MusicController.SeekTo(time));
|
||||
|
||||
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||
}
|
||||
|
@ -24,7 +24,6 @@ using osu.Game.Scoring;
|
||||
using osu.Game.Storyboards;
|
||||
using osu.Game.Tests.Visual;
|
||||
using osuTK;
|
||||
using static osu.Game.Tests.Visual.OsuTestScene.ClockBackedTestWorkingBeatmap;
|
||||
|
||||
namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
@ -33,18 +32,12 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
[Resolved]
|
||||
private AudioManager audioManager { get; set; }
|
||||
|
||||
private TrackVirtualManual track;
|
||||
|
||||
protected override bool Autoplay => true;
|
||||
|
||||
protected override TestPlayer CreatePlayer(Ruleset ruleset) => new ScoreExposedPlayer();
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
{
|
||||
var working = new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||
track = (TrackVirtualManual)working.Track;
|
||||
return working;
|
||||
}
|
||||
=> new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||
|
||||
private DrawableSpinner drawableSpinner;
|
||||
private SpriteIcon spinnerSymbol => drawableSpinner.ChildrenOfType<SpriteIcon>().Single();
|
||||
@ -54,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
{
|
||||
base.SetUpSteps();
|
||||
|
||||
AddUntilStep("wait for track to start running", () => track.IsRunning);
|
||||
AddUntilStep("wait for track to start running", () => MusicController.IsPlaying);
|
||||
AddStep("retrieve spinner", () => drawableSpinner = (DrawableSpinner)Player.DrawableRuleset.Playfield.AllHitObjects.First());
|
||||
}
|
||||
|
||||
@ -198,7 +191,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
|
||||
private void addSeekStep(double time)
|
||||
{
|
||||
AddStep($"seek to {time}", () => track.Seek(time));
|
||||
AddStep($"seek to {time}", () => MusicController.SeekTo(time));
|
||||
|
||||
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||
}
|
||||
|
@ -175,11 +175,11 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
|
||||
private void createDrawableRuleset()
|
||||
{
|
||||
AddUntilStep("wait for beatmap to be loaded", () => Beatmap.Value.Track.IsLoaded);
|
||||
AddUntilStep("wait for beatmap to be loaded", () => MusicController.TrackLoaded);
|
||||
|
||||
AddStep("create drawable ruleset", () =>
|
||||
{
|
||||
Beatmap.Value.Track.Start();
|
||||
MusicController.Play(true);
|
||||
|
||||
SetContents(() =>
|
||||
{
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Skinning
|
||||
|
||||
beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 });
|
||||
|
||||
Beatmap.Value.Track.Start();
|
||||
MusicController.Play(true);
|
||||
});
|
||||
|
||||
AddStep("Load playfield", () => SetContents(() => new TaikoPlayfield(new ControlPointInfo())
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -32,6 +31,6 @@ namespace osu.Game.Tests.Skins
|
||||
public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo("sample")) != null);
|
||||
|
||||
[Test]
|
||||
public void TestRetrieveOggTrack() => AddAssert("track is non-null", () => !(beatmap.Track is TrackVirtual));
|
||||
public void TestRetrieveOggTrack() => AddAssert("track is non-null", () => !MusicController.IsDummyDevice);
|
||||
}
|
||||
}
|
||||
|
@ -3,12 +3,11 @@
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Screens.Edit;
|
||||
using osu.Game.Screens.Edit.Compose.Components.Timeline;
|
||||
@ -65,10 +64,10 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
private readonly Drawable marker;
|
||||
|
||||
[Resolved]
|
||||
private IBindable<WorkingBeatmap> beatmap { get; set; }
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private EditorClock editorClock { get; set; }
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
public AudioVisualiser()
|
||||
{
|
||||
@ -94,8 +93,8 @@ namespace osu.Game.Tests.Visual.Editing
|
||||
{
|
||||
base.Update();
|
||||
|
||||
if (beatmap.Value.Track.IsLoaded)
|
||||
marker.X = (float)(editorClock.CurrentTime / beatmap.Value.Track.Length);
|
||||
if (musicController.TrackLoaded)
|
||||
marker.X = (float)(editorClock.CurrentTime / musicController.TrackLength);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Framework.Testing;
|
||||
using osu.Framework.Timing;
|
||||
@ -18,8 +17,6 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
public class TestSceneCompletionCancellation : OsuPlayerTestScene
|
||||
{
|
||||
private Track track;
|
||||
|
||||
[Resolved]
|
||||
private AudioManager audio { get; set; }
|
||||
|
||||
@ -34,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
base.SetUpSteps();
|
||||
|
||||
// Ensure track has actually running before attempting to seek
|
||||
AddUntilStep("wait for track to start running", () => track.IsRunning);
|
||||
AddUntilStep("wait for track to start running", () => MusicController.IsPlaying);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -73,13 +70,13 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void complete()
|
||||
{
|
||||
AddStep("seek to completion", () => track.Seek(5000));
|
||||
AddStep("seek to completion", () => MusicController.SeekTo(5000));
|
||||
AddUntilStep("completion set by processor", () => Player.ScoreProcessor.HasCompleted.Value);
|
||||
}
|
||||
|
||||
private void cancel()
|
||||
{
|
||||
AddStep("rewind to cancel", () => track.Seek(4000));
|
||||
AddStep("rewind to cancel", () => MusicController.SeekTo(4000));
|
||||
AddUntilStep("completion cleared by processor", () => !Player.ScoreProcessor.HasCompleted.Value);
|
||||
}
|
||||
|
||||
@ -91,11 +88,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
}
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
{
|
||||
var working = new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audio);
|
||||
track = working.Track;
|
||||
return working;
|
||||
}
|
||||
=> new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audio);
|
||||
|
||||
protected override IBeatmap CreateBeatmap(RulesetInfo ruleset)
|
||||
{
|
||||
|
@ -5,10 +5,10 @@ using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Osu.Objects;
|
||||
using osu.Game.Storyboards;
|
||||
@ -21,19 +21,16 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
[Resolved]
|
||||
private AudioManager audioManager { get; set; }
|
||||
|
||||
private Track track;
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||
{
|
||||
var working = new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||
track = working.Track;
|
||||
return working;
|
||||
}
|
||||
=> new ClockBackedTestWorkingBeatmap(beatmap, storyboard, new FramedClock(new ManualClock { Rate = 1 }), audioManager);
|
||||
|
||||
[Test]
|
||||
public void TestNoJudgementsOnRewind()
|
||||
{
|
||||
AddUntilStep("wait for track to start running", () => track.IsRunning);
|
||||
AddUntilStep("wait for track to start running", () => MusicController.IsPlaying);
|
||||
addSeekStep(3000);
|
||||
AddAssert("all judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.Judged));
|
||||
AddUntilStep("key counter counted keys", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses >= 7));
|
||||
@ -46,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void addSeekStep(double time)
|
||||
{
|
||||
AddStep($"seek to {time}", () => track.Seek(time));
|
||||
AddStep($"seek to {time}", () => MusicController.SeekTo(time));
|
||||
|
||||
// Allow a few frames of lenience
|
||||
AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100));
|
||||
|
@ -19,8 +19,8 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
Beatmap.Value.Track.Start();
|
||||
Beatmap.Value.Track.Seek(Beatmap.Value.Beatmap.HitObjects.First().StartTime - 1000);
|
||||
MusicController.Play(true);
|
||||
MusicController.SeekTo(Beatmap.Value.Beatmap.HitObjects.First().StartTime - 1000);
|
||||
|
||||
Add(new ModNightcore<HitObject>.NightcoreBeatContainer());
|
||||
|
||||
|
@ -288,7 +288,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void confirmNoTrackAdjustments()
|
||||
{
|
||||
AddAssert("track has no adjustments", () => Beatmap.Value.Track.AggregateFrequency.Value == 1);
|
||||
AddAssert("track has no adjustments", () => MusicController.AggregateFrequency.Value == 1);
|
||||
}
|
||||
|
||||
private void restart() => AddStep("restart", () => Player.Restart());
|
||||
|
@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
|
||||
|
||||
foreach (var mod in SelectedMods.Value.OfType<IApplicableToTrack>())
|
||||
mod.ApplyToTrack(Beatmap.Value.Track);
|
||||
mod.ApplyToTrack(MusicController);
|
||||
|
||||
InputManager.Child = container = new TestPlayerLoaderContainer(
|
||||
loader = new TestPlayerLoader(() =>
|
||||
@ -77,12 +77,12 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
{
|
||||
AddStep("load dummy beatmap", () => ResetPlayer(false, () => SelectedMods.Value = new[] { new OsuModNightcore() }));
|
||||
AddUntilStep("wait for current", () => loader.IsCurrentScreen());
|
||||
AddAssert("mod rate applied", () => Beatmap.Value.Track.Rate != 1);
|
||||
AddAssert("mod rate applied", () => MusicController.Rate != 1);
|
||||
AddStep("exit loader", () => loader.Exit());
|
||||
AddUntilStep("wait for not current", () => !loader.IsCurrentScreen());
|
||||
AddAssert("player did not load", () => !player.IsLoaded);
|
||||
AddUntilStep("player disposed", () => loader.DisposalTask?.IsCompleted == true);
|
||||
AddAssert("mod rate still applied", () => Beatmap.Value.Track.Rate != 1);
|
||||
AddAssert("mod rate still applied", () => MusicController.Rate != 1);
|
||||
}
|
||||
|
||||
[Test]
|
||||
|
@ -87,11 +87,9 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
|
||||
private void restart()
|
||||
{
|
||||
var track = Beatmap.Value.Track;
|
||||
|
||||
track.Reset();
|
||||
MusicController.Reset();
|
||||
loadStoryboard(Beatmap.Value);
|
||||
track.Start();
|
||||
MusicController.Play(true);
|
||||
}
|
||||
|
||||
private void loadStoryboard(WorkingBeatmap working)
|
||||
@ -106,7 +104,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
storyboard.Passing = false;
|
||||
|
||||
storyboardContainer.Add(storyboard);
|
||||
decoupledClock.ChangeSource(working.Track);
|
||||
decoupledClock.ChangeSource(musicController.GetTrackClock());
|
||||
}
|
||||
|
||||
private void loadStoryboardNoVideo()
|
||||
@ -129,7 +127,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
storyboard = sb.CreateDrawable(Beatmap.Value);
|
||||
|
||||
storyboardContainer.Add(storyboard);
|
||||
decoupledClock.ChangeSource(Beatmap.Value.Track);
|
||||
decoupledClock.ChangeSource(musicController.GetTrackClock());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Screens.Menu;
|
||||
|
||||
@ -15,11 +14,9 @@ namespace osu.Game.Tests.Visual.Menus
|
||||
|
||||
public TestSceneIntroWelcome()
|
||||
{
|
||||
AddUntilStep("wait for load", () => getTrack() != null);
|
||||
AddUntilStep("wait for load", () => MusicController.TrackLoaded);
|
||||
|
||||
AddAssert("check if menu music loops", () => getTrack().Looping);
|
||||
AddAssert("check if menu music loops", () => MusicController.Looping);
|
||||
}
|
||||
|
||||
private Track getTrack() => (IntroStack?.CurrentScreen as MainMenu)?.Track;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -46,7 +45,6 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
Player player = null;
|
||||
|
||||
WorkingBeatmap beatmap() => Game.Beatmap.Value;
|
||||
Track track() => beatmap().Track;
|
||||
|
||||
PushAndConfirm(() => new TestSongSelect());
|
||||
|
||||
@ -62,30 +60,27 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null);
|
||||
AddUntilStep("wait for fail", () => player.HasFailed);
|
||||
|
||||
AddUntilStep("wait for track stop", () => !track().IsRunning);
|
||||
AddAssert("Ensure time before preview point", () => track().CurrentTime < beatmap().Metadata.PreviewTime);
|
||||
AddUntilStep("wait for track stop", () => !MusicController.IsPlaying);
|
||||
AddAssert("Ensure time before preview point", () => MusicController.CurrentTrackTime < beatmap().Metadata.PreviewTime);
|
||||
|
||||
pushEscape();
|
||||
|
||||
AddUntilStep("wait for track playing", () => track().IsRunning);
|
||||
AddAssert("Ensure time wasn't reset to preview point", () => track().CurrentTime < beatmap().Metadata.PreviewTime);
|
||||
AddUntilStep("wait for track playing", () => MusicController.IsPlaying);
|
||||
AddAssert("Ensure time wasn't reset to preview point", () => MusicController.CurrentTrackTime < beatmap().Metadata.PreviewTime);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestMenuMakesMusic()
|
||||
{
|
||||
WorkingBeatmap beatmap() => Game.Beatmap.Value;
|
||||
Track track() => beatmap().Track;
|
||||
|
||||
TestSongSelect songSelect = null;
|
||||
|
||||
PushAndConfirm(() => songSelect = new TestSongSelect());
|
||||
|
||||
AddUntilStep("wait for no track", () => track() is TrackVirtual);
|
||||
AddUntilStep("wait for no track", () => MusicController.IsDummyDevice);
|
||||
|
||||
AddStep("return to menu", () => songSelect.Exit());
|
||||
|
||||
AddUntilStep("wait for track", () => !(track() is TrackVirtual) && track().IsRunning);
|
||||
AddUntilStep("wait for track", () => !MusicController.IsDummyDevice && MusicController.IsPlaying);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@ -140,12 +135,12 @@ namespace osu.Game.Tests.Visual.Navigation
|
||||
AddUntilStep("Wait for music controller", () => Game.MusicController.IsLoaded);
|
||||
AddStep("Seek close to end", () =>
|
||||
{
|
||||
Game.MusicController.SeekTo(Game.Beatmap.Value.Track.Length - 1000);
|
||||
Game.Beatmap.Value.Track.Completed += () => trackCompleted = true;
|
||||
Game.MusicController.SeekTo(MusicController.TrackLength - 1000);
|
||||
// MusicController.Completed += () => trackCompleted = true;
|
||||
});
|
||||
|
||||
AddUntilStep("Track was completed", () => trackCompleted);
|
||||
AddUntilStep("Track was restarted", () => Game.Beatmap.Value.Track.IsRunning);
|
||||
AddUntilStep("Track was restarted", () => MusicController.IsPlaying);
|
||||
}
|
||||
|
||||
private void pushEscape() =>
|
||||
|
@ -71,6 +71,9 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
|
||||
private readonly Box flashLayer;
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
public BeatContainer()
|
||||
{
|
||||
RelativeSizeAxes = Axes.X;
|
||||
@ -165,7 +168,7 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
if (timingPoints.Count == 0) return 0;
|
||||
|
||||
if (timingPoints[^1] == current)
|
||||
return (int)Math.Ceiling((Beatmap.Value.Track.Length - current.Time) / current.BeatLength);
|
||||
return (int)Math.Ceiling((musicController.TrackLength - current.Time) / current.BeatLength);
|
||||
|
||||
return (int)Math.Ceiling((getNextTimingPoint(current).Time - current.Time) / current.BeatLength);
|
||||
}
|
||||
|
@ -80,12 +80,12 @@ namespace osu.Game.Tests.Visual.UserInterface
|
||||
AddStep("Store track", () => currentBeatmap = Beatmap.Value);
|
||||
|
||||
AddStep(@"Seek track to 6 second", () => musicController.SeekTo(6000));
|
||||
AddUntilStep(@"Wait for current time to update", () => currentBeatmap.Track.CurrentTime > 5000);
|
||||
AddUntilStep(@"Wait for current time to update", () => musicController.CurrentTrackTime > 5000);
|
||||
|
||||
AddStep(@"Set previous", () => musicController.PreviousTrack());
|
||||
|
||||
AddAssert(@"Check beatmap didn't change", () => currentBeatmap == Beatmap.Value);
|
||||
AddUntilStep("Wait for current time to update", () => currentBeatmap.Track.CurrentTime < 5000);
|
||||
AddUntilStep("Wait for current time to update", () => musicController.CurrentTrackTime < 5000);
|
||||
|
||||
AddStep(@"Set previous", () => musicController.PreviousTrack());
|
||||
AddAssert(@"Check beatmap did change", () => currentBeatmap != Beatmap.Value);
|
||||
|
@ -255,7 +255,7 @@ namespace osu.Game.Beatmaps
|
||||
new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager));
|
||||
}
|
||||
|
||||
previous?.TransferTo(working);
|
||||
// previous?.TransferTo(working);
|
||||
return working;
|
||||
}
|
||||
}
|
||||
|
@ -79,22 +79,6 @@ namespace osu.Game.Beatmaps
|
||||
}
|
||||
}
|
||||
|
||||
public override void RecycleTrack()
|
||||
{
|
||||
base.RecycleTrack();
|
||||
|
||||
trackStore?.Dispose();
|
||||
trackStore = null;
|
||||
}
|
||||
|
||||
public override void TransferTo(WorkingBeatmap other)
|
||||
{
|
||||
base.TransferTo(other);
|
||||
|
||||
if (other is BeatmapManagerWorkingBeatmap owb && textureStore != null && BeatmapInfo.BackgroundEquals(other.BeatmapInfo))
|
||||
owb.textureStore = textureStore;
|
||||
}
|
||||
|
||||
protected override Waveform GetWaveform()
|
||||
{
|
||||
try
|
||||
|
@ -26,11 +26,6 @@ namespace osu.Game.Beatmaps
|
||||
/// </summary>
|
||||
Texture Background { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the audio track for this <see cref="WorkingBeatmap"/>.
|
||||
/// </summary>
|
||||
Track Track { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves the <see cref="Waveform"/> for the <see cref="Track"/> of this <see cref="WorkingBeatmap"/>.
|
||||
/// </summary>
|
||||
|
@ -40,7 +40,6 @@ namespace osu.Game.Beatmaps
|
||||
BeatmapSetInfo = beatmapInfo.BeatmapSet;
|
||||
Metadata = beatmapInfo.Metadata ?? BeatmapSetInfo?.Metadata ?? new BeatmapMetadata();
|
||||
|
||||
track = new RecyclableLazy<Track>(() => GetTrack() ?? GetVirtualTrack(1000));
|
||||
background = new RecyclableLazy<Texture>(GetBackground, BackgroundStillValid);
|
||||
waveform = new RecyclableLazy<Waveform>(GetWaveform);
|
||||
storyboard = new RecyclableLazy<Storyboard>(GetStoryboard);
|
||||
@ -250,10 +249,9 @@ namespace osu.Game.Beatmaps
|
||||
protected abstract Texture GetBackground();
|
||||
private readonly RecyclableLazy<Texture> background;
|
||||
|
||||
public virtual bool TrackLoaded => track.IsResultAvailable;
|
||||
public Track Track => track.Value;
|
||||
public Track GetRealTrack() => GetTrack() ?? GetVirtualTrack(1000);
|
||||
|
||||
protected abstract Track GetTrack();
|
||||
private RecyclableLazy<Track> track;
|
||||
|
||||
public bool WaveformLoaded => waveform.IsResultAvailable;
|
||||
public Waveform Waveform => waveform.Value;
|
||||
@ -271,22 +269,6 @@ namespace osu.Game.Beatmaps
|
||||
protected virtual ISkin GetSkin() => new DefaultSkin();
|
||||
private readonly RecyclableLazy<ISkin> skin;
|
||||
|
||||
/// <summary>
|
||||
/// Transfer pieces of a beatmap to a new one, where possible, to save on loading.
|
||||
/// </summary>
|
||||
/// <param name="other">The new beatmap which is being switched to.</param>
|
||||
public virtual void TransferTo(WorkingBeatmap other)
|
||||
{
|
||||
if (track.IsResultAvailable && Track != null && BeatmapInfo.AudioEquals(other.BeatmapInfo))
|
||||
other.track = track;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Eagerly dispose of the audio track associated with this <see cref="WorkingBeatmap"/> (if any).
|
||||
/// Accessing track again will load a fresh instance.
|
||||
/// </summary>
|
||||
public virtual void RecycleTrack() => track.Recycle();
|
||||
|
||||
~WorkingBeatmap()
|
||||
{
|
||||
total_count.Value--;
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
@ -14,6 +15,9 @@ namespace osu.Game.Graphics.Containers
|
||||
{
|
||||
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
private int lastBeat;
|
||||
private TimingControlPoint lastTimingPoint;
|
||||
|
||||
@ -47,22 +51,18 @@ namespace osu.Game.Graphics.Containers
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
Track track = null;
|
||||
IBeatmap beatmap = null;
|
||||
|
||||
double currentTrackTime = 0;
|
||||
TimingControlPoint timingPoint = null;
|
||||
EffectControlPoint effectPoint = null;
|
||||
|
||||
if (Beatmap.Value.TrackLoaded && Beatmap.Value.BeatmapLoaded)
|
||||
{
|
||||
track = Beatmap.Value.Track;
|
||||
if (musicController.TrackLoaded && Beatmap.Value.BeatmapLoaded)
|
||||
beatmap = Beatmap.Value.Beatmap;
|
||||
}
|
||||
|
||||
if (track != null && beatmap != null && track.IsRunning && track.Length > 0)
|
||||
if (beatmap != null && musicController.IsPlaying && musicController.TrackLength > 0)
|
||||
{
|
||||
currentTrackTime = track.CurrentTime + EarlyActivationMilliseconds;
|
||||
currentTrackTime = musicController.CurrentTrackTime + EarlyActivationMilliseconds;
|
||||
|
||||
timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
|
||||
effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
|
||||
@ -98,7 +98,7 @@ namespace osu.Game.Graphics.Containers
|
||||
return;
|
||||
|
||||
using (BeginDelayedSequence(-TimeSinceLastBeat, true))
|
||||
OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty);
|
||||
OnNewBeat(beatIndex, timingPoint, effectPoint, musicController.CurrentAmplitudes);
|
||||
|
||||
lastBeat = beatIndex;
|
||||
lastTimingPoint = timingPoint;
|
||||
|
@ -431,19 +431,19 @@ namespace osu.Game
|
||||
|
||||
if (newBeatmap != null)
|
||||
{
|
||||
newBeatmap.Track.Completed += () => Scheduler.AddOnce(() => trackCompleted(newBeatmap));
|
||||
// MusicController.Completed += () => Scheduler.AddOnce(() => trackCompleted(newBeatmap));
|
||||
newBeatmap.BeginAsyncLoad();
|
||||
}
|
||||
|
||||
void trackCompleted(WorkingBeatmap b)
|
||||
{
|
||||
// the source of track completion is the audio thread, so the beatmap may have changed before firing.
|
||||
if (Beatmap.Value != b)
|
||||
return;
|
||||
|
||||
if (!Beatmap.Value.Track.Looping && !Beatmap.Disabled)
|
||||
MusicController.NextTrack();
|
||||
}
|
||||
// void trackCompleted(WorkingBeatmap b)
|
||||
// {
|
||||
// // the source of track completion is the audio thread, so the beatmap may have changed before firing.
|
||||
// if (Beatmap.Value != b)
|
||||
// return;
|
||||
//
|
||||
// if (!MusicController.Looping && !Beatmap.Disabled)
|
||||
// MusicController.NextTrack();
|
||||
// }
|
||||
}
|
||||
|
||||
private void modsChanged(ValueChangedEvent<IReadOnlyList<Mod>> mods)
|
||||
@ -555,6 +555,7 @@ namespace osu.Game
|
||||
BackButton.Receptor receptor;
|
||||
|
||||
dependencies.CacheAs(idleTracker = new GameIdleTracker(6000));
|
||||
dependencies.CacheAs(MusicController = new MusicController());
|
||||
|
||||
AddRange(new Drawable[]
|
||||
{
|
||||
@ -617,7 +618,7 @@ namespace osu.Game
|
||||
|
||||
loadComponentSingleFile(new OnScreenDisplay(), Add, true);
|
||||
|
||||
loadComponentSingleFile(MusicController = new MusicController(), Add, true);
|
||||
loadComponentSingleFile(MusicController, Add);
|
||||
|
||||
loadComponentSingleFile(notifications.With(d =>
|
||||
{
|
||||
|
@ -238,16 +238,6 @@ namespace osu.Game
|
||||
|
||||
Beatmap = new NonNullableBindable<WorkingBeatmap>(defaultBeatmap);
|
||||
|
||||
// ScheduleAfterChildren is safety against something in the current frame accessing the previous beatmap's track
|
||||
// and potentially causing a reload of it after just unloading.
|
||||
// Note that the reason for this being added *has* been resolved, so it may be feasible to removed this if required.
|
||||
Beatmap.BindValueChanged(b => ScheduleAfterChildren(() =>
|
||||
{
|
||||
// compare to last beatmap as sometimes the two may share a track representation (optimisation, see WorkingBeatmap.TransferTo)
|
||||
if (b.OldValue?.TrackLoaded == true && b.OldValue?.Track != b.NewValue?.Track)
|
||||
b.OldValue.RecycleTrack();
|
||||
}));
|
||||
|
||||
dependencies.CacheAs<IBindable<WorkingBeatmap>>(Beatmap);
|
||||
dependencies.CacheAs(Beatmap);
|
||||
|
||||
|
@ -30,6 +30,9 @@ namespace osu.Game.Overlays.Music
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
private FilterControl filter;
|
||||
private Playlist list;
|
||||
|
||||
@ -80,10 +83,7 @@ namespace osu.Game.Overlays.Music
|
||||
BeatmapInfo toSelect = list.FirstVisibleSet?.Beatmaps?.FirstOrDefault();
|
||||
|
||||
if (toSelect != null)
|
||||
{
|
||||
beatmap.Value = beatmaps.GetWorkingBeatmap(toSelect);
|
||||
beatmap.Value.Track.Restart();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -116,12 +116,12 @@ namespace osu.Game.Overlays.Music
|
||||
{
|
||||
if (set.ID == (beatmap.Value?.BeatmapSetInfo?.ID ?? -1))
|
||||
{
|
||||
beatmap.Value?.Track?.Seek(0);
|
||||
musicController.SeekTo(0);
|
||||
return;
|
||||
}
|
||||
|
||||
beatmap.Value = beatmaps.GetWorkingBeatmap(set.Beatmaps.First());
|
||||
beatmap.Value.Track.Restart();
|
||||
musicController.Play(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,13 +4,18 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using JetBrains.Annotations;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Input.Bindings;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Framework.Threading;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays.OSD;
|
||||
@ -21,7 +26,7 @@ namespace osu.Game.Overlays
|
||||
/// <summary>
|
||||
/// Handles playback of the global music track.
|
||||
/// </summary>
|
||||
public class MusicController : Component, IKeyBindingHandler<GlobalAction>
|
||||
public class MusicController : CompositeDrawable, IKeyBindingHandler<GlobalAction>, ITrack
|
||||
{
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
@ -61,9 +66,23 @@ namespace osu.Game.Overlays
|
||||
[Resolved(canBeNull: true)]
|
||||
private OnScreenDisplay onScreenDisplay { get; set; }
|
||||
|
||||
[NotNull]
|
||||
private readonly TrackContainer trackContainer;
|
||||
|
||||
[CanBeNull]
|
||||
private DrawableTrack drawableTrack;
|
||||
|
||||
[CanBeNull]
|
||||
private Track track;
|
||||
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
|
||||
private IBindable<WeakReference<BeatmapSetInfo>> managerRemoved;
|
||||
|
||||
public MusicController()
|
||||
{
|
||||
InternalChild = trackContainer = new TrackContainer { RelativeSizeAxes = Axes.Both };
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
@ -95,9 +114,35 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the current beatmap track is playing.
|
||||
/// Returns whether the beatmap track is playing.
|
||||
/// </summary>
|
||||
public bool IsPlaying => current?.Track.IsRunning ?? false;
|
||||
public bool IsPlaying => drawableTrack?.IsRunning ?? false;
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the beatmap track is loaded.
|
||||
/// </summary>
|
||||
public bool TrackLoaded => drawableTrack?.IsLoaded == true;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the current time of the beatmap track.
|
||||
/// </summary>
|
||||
public double CurrentTrackTime => drawableTrack?.CurrentTime ?? 0;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the length of the beatmap track.
|
||||
/// </summary>
|
||||
public double TrackLength => drawableTrack?.Length ?? 0;
|
||||
|
||||
public void AddAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable)
|
||||
=> trackContainer.AddAdjustment(type, adjustBindable);
|
||||
|
||||
public void RemoveAdjustment(AdjustableProperty type, BindableNumber<double> adjustBindable)
|
||||
=> trackContainer.RemoveAdjustment(type, adjustBindable);
|
||||
|
||||
public void Reset() => drawableTrack?.Reset();
|
||||
|
||||
[CanBeNull]
|
||||
public IAdjustableClock GetTrackClock() => track;
|
||||
|
||||
private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
|
||||
{
|
||||
@ -130,7 +175,7 @@ namespace osu.Game.Overlays
|
||||
seekDelegate = Schedule(() =>
|
||||
{
|
||||
if (!beatmap.Disabled)
|
||||
current?.Track.Seek(position);
|
||||
drawableTrack?.Seek(position);
|
||||
});
|
||||
}
|
||||
|
||||
@ -142,9 +187,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
if (IsUserPaused) return;
|
||||
|
||||
var track = current?.Track;
|
||||
|
||||
if (track == null || track is TrackVirtual)
|
||||
if (drawableTrack == null || drawableTrack.IsDummyDevice)
|
||||
{
|
||||
if (beatmap.Disabled)
|
||||
return;
|
||||
@ -163,17 +206,15 @@ namespace osu.Game.Overlays
|
||||
/// <returns>Whether the operation was successful.</returns>
|
||||
public bool Play(bool restart = false)
|
||||
{
|
||||
var track = current?.Track;
|
||||
|
||||
IsUserPaused = false;
|
||||
|
||||
if (track == null)
|
||||
if (drawableTrack == null)
|
||||
return false;
|
||||
|
||||
if (restart)
|
||||
track.Restart();
|
||||
drawableTrack.Restart();
|
||||
else if (!IsPlaying)
|
||||
track.Start();
|
||||
drawableTrack.Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -183,11 +224,9 @@ namespace osu.Game.Overlays
|
||||
/// </summary>
|
||||
public void Stop()
|
||||
{
|
||||
var track = current?.Track;
|
||||
|
||||
IsUserPaused = true;
|
||||
if (track?.IsRunning == true)
|
||||
track.Stop();
|
||||
if (drawableTrack?.IsRunning == true)
|
||||
drawableTrack.Stop();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -196,9 +235,7 @@ namespace osu.Game.Overlays
|
||||
/// <returns>Whether the operation was successful.</returns>
|
||||
public bool TogglePause()
|
||||
{
|
||||
var track = current?.Track;
|
||||
|
||||
if (track?.IsRunning == true)
|
||||
if (drawableTrack?.IsRunning == true)
|
||||
Stop();
|
||||
else
|
||||
Play();
|
||||
@ -220,7 +257,7 @@ namespace osu.Game.Overlays
|
||||
if (beatmap.Disabled)
|
||||
return PreviousTrackResult.None;
|
||||
|
||||
var currentTrackPosition = current?.Track.CurrentTime;
|
||||
var currentTrackPosition = drawableTrack?.CurrentTime;
|
||||
|
||||
if (currentTrackPosition >= restart_cutoff_point)
|
||||
{
|
||||
@ -274,7 +311,7 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
// if not scheduled, the previously track will be stopped one frame later (see ScheduleAfterChildren logic in GameBase).
|
||||
// we probably want to move this to a central method for switching to a new working beatmap in the future.
|
||||
Schedule(() => beatmap.Value.Track.Restart());
|
||||
Schedule(() => drawableTrack?.Restart());
|
||||
}
|
||||
|
||||
private WorkingBeatmap current;
|
||||
@ -307,6 +344,14 @@ namespace osu.Game.Overlays
|
||||
}
|
||||
|
||||
current = beatmap.NewValue;
|
||||
|
||||
drawableTrack?.Expire();
|
||||
drawableTrack = null;
|
||||
track = null;
|
||||
|
||||
if (current != null)
|
||||
trackContainer.Add(drawableTrack = new DrawableTrack(track = current.GetRealTrack()));
|
||||
|
||||
TrackChanged?.Invoke(current, direction);
|
||||
|
||||
ResetTrackAdjustments();
|
||||
@ -334,16 +379,15 @@ namespace osu.Game.Overlays
|
||||
|
||||
public void ResetTrackAdjustments()
|
||||
{
|
||||
var track = current?.Track;
|
||||
if (track == null)
|
||||
if (drawableTrack == null)
|
||||
return;
|
||||
|
||||
track.ResetSpeedAdjustments();
|
||||
drawableTrack.ResetSpeedAdjustments();
|
||||
|
||||
if (allowRateAdjustments)
|
||||
{
|
||||
foreach (var mod in mods.Value.OfType<IApplicableToTrack>())
|
||||
mod.ApplyToTrack(track);
|
||||
mod.ApplyToTrack(drawableTrack);
|
||||
}
|
||||
}
|
||||
|
||||
@ -394,6 +438,133 @@ namespace osu.Game.Overlays
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private class TrackContainer : AudioContainer<DrawableTrack>
|
||||
{
|
||||
}
|
||||
|
||||
#region ITrack
|
||||
|
||||
/// <summary>
|
||||
/// The volume of this component.
|
||||
/// </summary>
|
||||
public BindableNumber<double> Volume => drawableTrack?.Volume; // Todo: Bad
|
||||
|
||||
/// <summary>
|
||||
/// The playback balance of this sample (-1 .. 1 where 0 is centered)
|
||||
/// </summary>
|
||||
public BindableNumber<double> Balance => drawableTrack?.Balance; // Todo: Bad
|
||||
|
||||
/// <summary>
|
||||
/// Rate at which the component is played back (affects pitch). 1 is 100% playback speed, or default frequency.
|
||||
/// </summary>
|
||||
public BindableNumber<double> Frequency => drawableTrack?.Frequency; // Todo: Bad
|
||||
|
||||
/// <summary>
|
||||
/// Rate at which the component is played back (does not affect pitch). 1 is 100% playback speed.
|
||||
/// </summary>
|
||||
public BindableNumber<double> Tempo => drawableTrack?.Tempo; // Todo: Bad
|
||||
|
||||
public IBindable<double> AggregateVolume => drawableTrack?.AggregateVolume; // Todo: Bad
|
||||
|
||||
public IBindable<double> AggregateBalance => drawableTrack?.AggregateBalance; // Todo: Bad
|
||||
|
||||
public IBindable<double> AggregateFrequency => drawableTrack?.AggregateFrequency; // Todo: Bad
|
||||
|
||||
public IBindable<double> AggregateTempo => drawableTrack?.AggregateTempo; // Todo: Bad
|
||||
|
||||
/// <summary>
|
||||
/// Overall playback rate (1 is 100%, -1 is reversed at 100%).
|
||||
/// </summary>
|
||||
public double Rate => AggregateFrequency.Value * AggregateTempo.Value;
|
||||
|
||||
event Action ITrack.Completed
|
||||
{
|
||||
add
|
||||
{
|
||||
if (drawableTrack != null)
|
||||
drawableTrack.Completed += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (drawableTrack != null)
|
||||
drawableTrack.Completed -= value;
|
||||
}
|
||||
}
|
||||
|
||||
event Action ITrack.Failed
|
||||
{
|
||||
add
|
||||
{
|
||||
if (drawableTrack != null)
|
||||
drawableTrack.Failed += value;
|
||||
}
|
||||
remove
|
||||
{
|
||||
if (drawableTrack != null)
|
||||
drawableTrack.Failed -= value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Looping
|
||||
{
|
||||
get => drawableTrack?.Looping ?? false;
|
||||
set
|
||||
{
|
||||
if (drawableTrack != null)
|
||||
drawableTrack.Looping = value;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsDummyDevice => drawableTrack?.IsDummyDevice ?? true;
|
||||
|
||||
public double RestartPoint
|
||||
{
|
||||
get => drawableTrack?.RestartPoint ?? 0;
|
||||
set
|
||||
{
|
||||
if (drawableTrack != null)
|
||||
drawableTrack.RestartPoint = value;
|
||||
}
|
||||
}
|
||||
|
||||
double ITrack.CurrentTime => CurrentTrackTime;
|
||||
|
||||
double ITrack.Length
|
||||
{
|
||||
get => TrackLength;
|
||||
set
|
||||
{
|
||||
if (drawableTrack != null)
|
||||
drawableTrack.Length = value;
|
||||
}
|
||||
}
|
||||
|
||||
public int? Bitrate => drawableTrack?.Bitrate;
|
||||
|
||||
bool ITrack.IsRunning => IsPlaying;
|
||||
|
||||
public bool IsReversed => drawableTrack?.IsReversed ?? false;
|
||||
|
||||
public bool HasCompleted => drawableTrack?.HasCompleted ?? false;
|
||||
|
||||
void ITrack.Reset() => drawableTrack?.Reset();
|
||||
|
||||
void ITrack.Restart() => Play(true);
|
||||
|
||||
void ITrack.ResetSpeedAdjustments() => ResetTrackAdjustments();
|
||||
|
||||
bool ITrack.Seek(double seek)
|
||||
{
|
||||
SeekTo(seek);
|
||||
return true;
|
||||
}
|
||||
|
||||
void ITrack.Start() => Play();
|
||||
|
||||
public ChannelAmplitudes CurrentAmplitudes => drawableTrack?.CurrentAmplitudes ?? ChannelAmplitudes.Empty;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public enum TrackChangeDirection
|
||||
|
@ -234,14 +234,12 @@ namespace osu.Game.Overlays
|
||||
pendingBeatmapSwitch = null;
|
||||
}
|
||||
|
||||
var track = beatmap.Value?.TrackLoaded ?? false ? beatmap.Value.Track : null;
|
||||
|
||||
if (track?.IsDummyDevice == false)
|
||||
if (musicController.IsDummyDevice == false)
|
||||
{
|
||||
progressBar.EndTime = track.Length;
|
||||
progressBar.CurrentTime = track.CurrentTime;
|
||||
progressBar.EndTime = musicController.TrackLength;
|
||||
progressBar.CurrentTime = musicController.CurrentTrackTime;
|
||||
|
||||
playButton.Icon = track.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
|
||||
playButton.Icon = musicController.IsPlaying ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
/// </summary>
|
||||
public interface IApplicableToTrack : IApplicableMod
|
||||
{
|
||||
void ApplyToTrack(Track track);
|
||||
void ApplyToTrack(ITrack track);
|
||||
}
|
||||
}
|
||||
|
@ -27,11 +27,11 @@ namespace osu.Game.Rulesets.Mods
|
||||
}, true);
|
||||
}
|
||||
|
||||
public override void ApplyToTrack(Track track)
|
||||
public override void ApplyToTrack(ITrack track)
|
||||
{
|
||||
// base.ApplyToTrack() intentionally not called (different tempo adjustment is applied)
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
|
||||
track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
|
||||
(track as Track)?.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
|
||||
(track as Track)?.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -38,11 +38,11 @@ namespace osu.Game.Rulesets.Mods
|
||||
}, true);
|
||||
}
|
||||
|
||||
public override void ApplyToTrack(Track track)
|
||||
public override void ApplyToTrack(ITrack track)
|
||||
{
|
||||
// base.ApplyToTrack() intentionally not called (different tempo adjustment is applied)
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
|
||||
track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
|
||||
(track as Track)?.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
|
||||
(track as Track)?.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
|
||||
}
|
||||
|
||||
public void ApplyToDrawableRuleset(DrawableRuleset<TObject> drawableRuleset)
|
||||
|
@ -12,9 +12,9 @@ namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract BindableNumber<double> SpeedChange { get; }
|
||||
|
||||
public virtual void ApplyToTrack(Track track)
|
||||
public virtual void ApplyToTrack(ITrack track)
|
||||
{
|
||||
track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange);
|
||||
(track as Track)?.AddAdjustment(AdjustableProperty.Tempo, SpeedChange);
|
||||
}
|
||||
|
||||
public virtual void ApplyToSample(SampleChannel sample)
|
||||
|
@ -51,9 +51,9 @@ namespace osu.Game.Rulesets.Mods
|
||||
AdjustPitch.BindValueChanged(applyPitchAdjustment);
|
||||
}
|
||||
|
||||
public void ApplyToTrack(Track track)
|
||||
public void ApplyToTrack(ITrack track)
|
||||
{
|
||||
this.track = track;
|
||||
this.track = track as Track;
|
||||
|
||||
FinalRate.TriggerChange();
|
||||
AdjustPitch.TriggerChange();
|
||||
|
@ -2,7 +2,6 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -18,7 +17,6 @@ namespace osu.Game.Screens.Edit.Components
|
||||
private const float contents_padding = 15;
|
||||
|
||||
protected readonly IBindable<WorkingBeatmap> Beatmap = new Bindable<WorkingBeatmap>();
|
||||
protected Track Track => Beatmap.Value.Track;
|
||||
|
||||
private readonly Drawable background;
|
||||
private readonly Container content;
|
||||
|
@ -16,6 +16,7 @@ using osu.Framework.Input.Events;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Sprites;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK.Input;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components
|
||||
@ -27,6 +28,9 @@ namespace osu.Game.Screens.Edit.Components
|
||||
[Resolved]
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
private readonly BindableNumber<double> tempo = new BindableDouble(1);
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -62,12 +66,12 @@ namespace osu.Game.Screens.Edit.Components
|
||||
}
|
||||
};
|
||||
|
||||
Track?.AddAdjustment(AdjustableProperty.Tempo, tempo);
|
||||
musicController.AddAdjustment(AdjustableProperty.Tempo, tempo);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
Track?.RemoveAdjustment(AdjustableProperty.Tempo, tempo);
|
||||
musicController?.RemoveAdjustment(AdjustableProperty.Tempo, tempo);
|
||||
|
||||
base.Dispose(isDisposing);
|
||||
}
|
||||
|
@ -2,6 +2,7 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using osuTK;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
@ -58,7 +59,9 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
return;
|
||||
|
||||
float markerPos = Math.Clamp(ToLocalSpace(screenPosition).X, 0, DrawWidth);
|
||||
editorClock.SeekTo(markerPos / DrawWidth * editorClock.TrackLength);
|
||||
|
||||
Debug.Assert(editorClock.TrackLength != null);
|
||||
editorClock.SeekTo(markerPos / DrawWidth * editorClock.TrackLength.Value);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ using osuTK;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
{
|
||||
@ -26,6 +27,9 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
|
||||
protected override Container<T> Content => content;
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
public TimelinePart(Container<T> content = null)
|
||||
{
|
||||
AddInternal(this.content = content ?? new Container<T> { RelativeSizeAxes = Axes.Both });
|
||||
@ -46,14 +50,14 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
|
||||
private void updateRelativeChildSize()
|
||||
{
|
||||
// the track may not be loaded completely (only has a length once it is).
|
||||
if (!Beatmap.Value.Track.IsLoaded)
|
||||
if (!musicController.TrackLoaded)
|
||||
{
|
||||
content.RelativeChildSize = Vector2.One;
|
||||
Schedule(updateRelativeChildSize);
|
||||
return;
|
||||
}
|
||||
|
||||
content.RelativeChildSize = new Vector2((float)Math.Max(1, Beatmap.Value.Track.Length), 1);
|
||||
content.RelativeChildSize = new Vector2((float)Math.Max(1, musicController.TrackLength), 1);
|
||||
}
|
||||
|
||||
protected virtual void LoadBeatmap(WorkingBeatmap beatmap)
|
||||
|
@ -3,7 +3,6 @@
|
||||
|
||||
using System;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Extensions.Color4Extensions;
|
||||
using osu.Framework.Graphics;
|
||||
@ -11,6 +10,7 @@ using osu.Framework.Graphics.Audio;
|
||||
using osu.Framework.Input.Events;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osuTK;
|
||||
|
||||
@ -26,6 +26,9 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
[Resolved]
|
||||
private EditorClock editorClock { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
public Timeline()
|
||||
{
|
||||
ZoomDuration = 200;
|
||||
@ -57,18 +60,21 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
Beatmap.BindValueChanged(b =>
|
||||
{
|
||||
waveform.Waveform = b.NewValue.Waveform;
|
||||
track = b.NewValue.Track;
|
||||
|
||||
if (track.Length > 0)
|
||||
// Todo: Wrong.
|
||||
Schedule(() =>
|
||||
{
|
||||
MaxZoom = getZoomLevelForVisibleMilliseconds(500);
|
||||
MinZoom = getZoomLevelForVisibleMilliseconds(10000);
|
||||
Zoom = getZoomLevelForVisibleMilliseconds(2000);
|
||||
}
|
||||
if (musicController.TrackLength > 0)
|
||||
{
|
||||
MaxZoom = getZoomLevelForVisibleMilliseconds(500);
|
||||
MinZoom = getZoomLevelForVisibleMilliseconds(10000);
|
||||
Zoom = getZoomLevelForVisibleMilliseconds(2000);
|
||||
}
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
private float getZoomLevelForVisibleMilliseconds(double milliseconds) => (float)(track.Length / milliseconds);
|
||||
private float getZoomLevelForVisibleMilliseconds(double milliseconds) => (float)(musicController.TrackLength / milliseconds);
|
||||
|
||||
/// <summary>
|
||||
/// The timeline's scroll position in the last frame.
|
||||
@ -90,8 +96,6 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
/// </summary>
|
||||
private bool trackWasPlaying;
|
||||
|
||||
private Track track;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -129,18 +133,18 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
|
||||
private void seekTrackToCurrent()
|
||||
{
|
||||
if (!track.IsLoaded)
|
||||
if (!musicController.TrackLoaded)
|
||||
return;
|
||||
|
||||
editorClock.Seek(Current / Content.DrawWidth * track.Length);
|
||||
editorClock.Seek(Current / Content.DrawWidth * musicController.TrackLength);
|
||||
}
|
||||
|
||||
private void scrollToTrackTime()
|
||||
{
|
||||
if (!track.IsLoaded || track.Length == 0)
|
||||
if (!musicController.TrackLoaded || musicController.TrackLength == 0)
|
||||
return;
|
||||
|
||||
ScrollTo((float)(editorClock.CurrentTime / track.Length) * Content.DrawWidth, false);
|
||||
ScrollTo((float)(editorClock.CurrentTime / musicController.TrackLength) * Content.DrawWidth, false);
|
||||
}
|
||||
|
||||
protected override bool OnMouseDown(MouseDownEvent e)
|
||||
@ -184,7 +188,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition))));
|
||||
|
||||
private double getTimeFromPosition(Vector2 localPosition) =>
|
||||
(localPosition.X / Content.DrawWidth) * track.Length;
|
||||
(localPosition.X / Content.DrawWidth) * musicController.TrackLength;
|
||||
|
||||
public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException();
|
||||
|
||||
|
@ -3,10 +3,9 @@
|
||||
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Parts;
|
||||
using osu.Game.Screens.Edit.Components.Timelines.Summary.Visualisations;
|
||||
|
||||
@ -18,7 +17,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
private EditorBeatmap beatmap { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private Bindable<WorkingBeatmap> working { get; set; }
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private BindableBeatDivisor beatDivisor { get; set; }
|
||||
@ -44,7 +43,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
|
||||
for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++)
|
||||
{
|
||||
var point = beatmap.ControlPointInfo.TimingPoints[i];
|
||||
var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : working.Value.Track.Length;
|
||||
var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : musicController.TrackLength;
|
||||
|
||||
int beat = 0;
|
||||
|
||||
|
@ -28,6 +28,7 @@ using osu.Framework.Logging;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics.Cursor;
|
||||
using osu.Game.Input.Bindings;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Edit;
|
||||
using osu.Game.Screens.Edit.Compose;
|
||||
using osu.Game.Screens.Edit.Setup;
|
||||
@ -53,6 +54,9 @@ namespace osu.Game.Screens.Edit
|
||||
[Resolved]
|
||||
private BeatmapManager beatmapManager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
private Box bottomBackground;
|
||||
private Container screenContainer;
|
||||
|
||||
@ -79,7 +83,7 @@ namespace osu.Game.Screens.Edit
|
||||
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
|
||||
|
||||
// Todo: should probably be done at a DrawableRuleset level to share logic with Player.
|
||||
var sourceClock = (IAdjustableClock)Beatmap.Value.Track ?? new StopwatchClock();
|
||||
var sourceClock = musicController.GetTrackClock() ?? new StopwatchClock();
|
||||
clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
|
||||
clock.ChangeSource(sourceClock);
|
||||
|
||||
@ -346,7 +350,7 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private void resetTrack(bool seekToStart = false)
|
||||
{
|
||||
Beatmap.Value.Track?.Stop();
|
||||
musicController.Stop();
|
||||
|
||||
if (seekToStart)
|
||||
{
|
||||
|
@ -2,13 +2,16 @@
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Transforms;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Edit
|
||||
{
|
||||
@ -17,7 +20,7 @@ namespace osu.Game.Screens.Edit
|
||||
/// </summary>
|
||||
public class EditorClock : Component, IFrameBasedClock, IAdjustableClock, ISourceChangeableClock
|
||||
{
|
||||
public readonly double TrackLength;
|
||||
public double? TrackLength { get; private set; }
|
||||
|
||||
public ControlPointInfo ControlPointInfo;
|
||||
|
||||
@ -25,12 +28,15 @@ namespace osu.Game.Screens.Edit
|
||||
|
||||
private readonly DecoupleableInterpolatingFramedClock underlyingClock;
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
public EditorClock(WorkingBeatmap beatmap, BindableBeatDivisor beatDivisor)
|
||||
: this(beatmap.Beatmap.ControlPointInfo, beatmap.Track.Length, beatDivisor)
|
||||
: this(beatmap.Beatmap.ControlPointInfo, null, beatDivisor)
|
||||
{
|
||||
}
|
||||
|
||||
public EditorClock(ControlPointInfo controlPointInfo, double trackLength, BindableBeatDivisor beatDivisor)
|
||||
public EditorClock(ControlPointInfo controlPointInfo, double? trackLength, BindableBeatDivisor beatDivisor)
|
||||
{
|
||||
this.beatDivisor = beatDivisor;
|
||||
|
||||
@ -45,6 +51,13 @@ namespace osu.Game.Screens.Edit
|
||||
{
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load()
|
||||
{
|
||||
// Todo: What.
|
||||
TrackLength ??= musicController.TrackLength;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Seek to the closest snappable beat from a time.
|
||||
/// </summary>
|
||||
@ -135,7 +148,8 @@ namespace osu.Game.Screens.Edit
|
||||
seekTime = timingPoint.Time;
|
||||
|
||||
// Ensure the sought point is within the boundaries
|
||||
seekTime = Math.Clamp(seekTime, 0, TrackLength);
|
||||
Debug.Assert(TrackLength != null);
|
||||
seekTime = Math.Clamp(seekTime, 0, TrackLength.Value);
|
||||
SeekTo(seekTime);
|
||||
}
|
||||
|
||||
|
@ -12,6 +12,7 @@ using osu.Framework.Screens;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.IO.Archives;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osu.Game.Skinning;
|
||||
using osuTK;
|
||||
@ -43,7 +44,8 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private WorkingBeatmap initialBeatmap;
|
||||
|
||||
protected Track Track => initialBeatmap?.Track;
|
||||
[Resolved]
|
||||
protected MusicController MusicController { get; private set; }
|
||||
|
||||
private readonly BindableDouble exitingVolumeFade = new BindableDouble(1);
|
||||
|
||||
@ -111,7 +113,7 @@ namespace osu.Game.Screens.Menu
|
||||
if (setInfo != null)
|
||||
{
|
||||
initialBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
|
||||
UsingThemedIntro = !(Track is TrackVirtual);
|
||||
UsingThemedIntro = !MusicController.IsDummyDevice;
|
||||
}
|
||||
|
||||
return UsingThemedIntro;
|
||||
@ -150,7 +152,7 @@ namespace osu.Game.Screens.Menu
|
||||
{
|
||||
// Only start the current track if it is the menu music. A beatmap's track is started when entering the Main Menu.
|
||||
if (UsingThemedIntro)
|
||||
Track.Restart();
|
||||
MusicController.Play(true);
|
||||
}
|
||||
|
||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||
|
@ -59,7 +59,7 @@ namespace osu.Game.Screens.Menu
|
||||
LoadComponentAsync(new TrianglesIntroSequence(logo, background)
|
||||
{
|
||||
RelativeSizeAxes = Axes.Both,
|
||||
Clock = new FramedClock(UsingThemedIntro ? Track : null),
|
||||
Clock = new FramedClock(UsingThemedIntro ? MusicController.GetTrackClock() : null),
|
||||
LoadMenu = LoadMenu
|
||||
}, t =>
|
||||
{
|
||||
|
@ -11,6 +11,7 @@ using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Graphics.Shapes;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Graphics.Textures;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Screens.Backgrounds;
|
||||
using osuTK.Graphics;
|
||||
|
||||
@ -30,6 +31,9 @@ namespace osu.Game.Screens.Menu
|
||||
Alpha = 0,
|
||||
};
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
private BackgroundScreenDefault background;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -40,7 +44,7 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
pianoReverb = audio.Samples.Get(@"Intro/Welcome/welcome_piano");
|
||||
|
||||
Track.Looping = true;
|
||||
musicController.Looping = true;
|
||||
}
|
||||
|
||||
protected override void LogoArriving(OsuLogo logo, bool resuming)
|
||||
|
@ -20,6 +20,7 @@ using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Menu
|
||||
{
|
||||
@ -74,6 +75,9 @@ namespace osu.Game.Screens.Menu
|
||||
/// </summary>
|
||||
public float Magnitude { get; set; } = 1;
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
private readonly float[] frequencyAmplitudes = new float[256];
|
||||
|
||||
private IShader shader;
|
||||
@ -103,15 +107,15 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
private void updateAmplitudes()
|
||||
{
|
||||
var effect = beatmap.Value.BeatmapLoaded && beatmap.Value.TrackLoaded
|
||||
? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(beatmap.Value.Track.CurrentTime)
|
||||
var effect = beatmap.Value.BeatmapLoaded && musicController.TrackLoaded
|
||||
? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(musicController.CurrentTrackTime)
|
||||
: null;
|
||||
|
||||
for (int i = 0; i < temporalAmplitudes.Length; i++)
|
||||
temporalAmplitudes[i] = 0;
|
||||
|
||||
if (beatmap.Value.TrackLoaded)
|
||||
addAmplitudesFromSource(beatmap.Value.Track);
|
||||
if (musicController.TrackLoaded)
|
||||
addAmplitudesFromSource(musicController);
|
||||
|
||||
foreach (var source in amplitudeSources)
|
||||
addAmplitudesFromSource(source);
|
||||
|
@ -5,7 +5,6 @@ using System.Linq;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Platform;
|
||||
@ -62,8 +61,6 @@ namespace osu.Game.Screens.Menu
|
||||
|
||||
protected override BackgroundScreen CreateBackground() => background;
|
||||
|
||||
internal Track Track { get; private set; }
|
||||
|
||||
private Bindable<float> holdDelay;
|
||||
private Bindable<bool> loginDisplayed;
|
||||
|
||||
@ -172,20 +169,23 @@ namespace osu.Game.Screens.Menu
|
||||
[Resolved]
|
||||
private Storage storage { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
public override void OnEntering(IScreen last)
|
||||
{
|
||||
base.OnEntering(last);
|
||||
buttons.FadeInFromZero(500);
|
||||
|
||||
Track = Beatmap.Value.Track;
|
||||
var metadata = Beatmap.Value.Metadata;
|
||||
|
||||
if (last is IntroScreen && Track != null)
|
||||
if (last is IntroScreen && musicController.TrackLoaded)
|
||||
{
|
||||
if (!Track.IsRunning)
|
||||
// Todo: Wrong.
|
||||
if (!musicController.IsPlaying)
|
||||
{
|
||||
Track.Seek(metadata.PreviewTime != -1 ? metadata.PreviewTime : 0.4f * Track.Length);
|
||||
Track.Start();
|
||||
musicController.SeekTo(metadata.PreviewTime != -1 ? metadata.PreviewTime : 0.4f * musicController.TrackLength);
|
||||
musicController.Play();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps.ControlPoints;
|
||||
using osu.Game.Graphics.Backgrounds;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
using osuTK.Input;
|
||||
@ -46,7 +47,6 @@ namespace osu.Game.Screens.Menu
|
||||
private SampleChannel sampleBeat;
|
||||
|
||||
private readonly Container colourAndTriangles;
|
||||
|
||||
private readonly Triangles triangles;
|
||||
|
||||
/// <summary>
|
||||
@ -319,6 +319,9 @@ namespace osu.Game.Screens.Menu
|
||||
intro.Delay(length + fade).FadeOut();
|
||||
}
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@ -327,9 +330,9 @@ namespace osu.Game.Screens.Menu
|
||||
const float velocity_adjust_cutoff = 0.98f;
|
||||
const float paused_velocity = 0.5f;
|
||||
|
||||
if (Beatmap.Value.Track.IsRunning)
|
||||
if (musicController.IsPlaying)
|
||||
{
|
||||
var maxAmplitude = lastBeatIndex >= 0 ? Beatmap.Value.Track.CurrentAmplitudes.Maximum : 0;
|
||||
var maxAmplitude = lastBeatIndex >= 0 ? musicController.CurrentAmplitudes.Maximum : 0;
|
||||
logoAmplitudeContainer.Scale = new Vector2((float)Interpolation.Damp(logoAmplitudeContainer.Scale.X, 1 - Math.Max(0, maxAmplitude - scale_adjust_cutoff) * 0.04f, 0.9f, Time.Elapsed));
|
||||
|
||||
if (maxAmplitude > velocity_adjust_cutoff)
|
||||
|
@ -10,6 +10,7 @@ using osu.Game.Beatmaps;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Online.Multiplayer;
|
||||
using osu.Game.Overlays;
|
||||
|
||||
namespace osu.Game.Screens.Multi.Match.Components
|
||||
{
|
||||
@ -26,6 +27,9 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
[Resolved]
|
||||
private BeatmapManager beatmaps { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
private bool hasBeatmap;
|
||||
|
||||
public ReadyButton()
|
||||
@ -100,7 +104,7 @@ namespace osu.Game.Screens.Multi.Match.Components
|
||||
return;
|
||||
}
|
||||
|
||||
bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(gameBeatmap.Value.Track.Length) < endDate.Value;
|
||||
bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(musicController.TrackLength) < endDate.Value;
|
||||
|
||||
Enabled.Value = hasBeatmap && hasEnoughTime;
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ namespace osu.Game.Screens.Multi
|
||||
private readonly Bindable<FilterCriteria> currentFilter = new Bindable<FilterCriteria>(new FilterCriteria());
|
||||
|
||||
[Resolved]
|
||||
private MusicController music { get; set; }
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
[Cached(Type = typeof(IRoomManager))]
|
||||
private RoomManager roomManager;
|
||||
@ -343,15 +343,9 @@ namespace osu.Game.Screens.Multi
|
||||
{
|
||||
if (screenStack.CurrentScreen is MatchSubScreen)
|
||||
{
|
||||
var track = Beatmap.Value?.Track;
|
||||
|
||||
if (track != null)
|
||||
{
|
||||
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
||||
track.Looping = true;
|
||||
|
||||
music.EnsurePlayingSomething();
|
||||
}
|
||||
musicController.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
||||
musicController.Looping = true;
|
||||
musicController.EnsurePlayingSomething();
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -361,13 +355,8 @@ namespace osu.Game.Screens.Multi
|
||||
|
||||
private void cancelLooping()
|
||||
{
|
||||
var track = Beatmap?.Value?.Track;
|
||||
|
||||
if (track != null)
|
||||
{
|
||||
track.Looping = false;
|
||||
track.RestartPoint = 0;
|
||||
}
|
||||
musicController.Looping = false;
|
||||
musicController.RestartPoint = 0;
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
|
@ -8,10 +8,10 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Utils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Objects.Drawables;
|
||||
using osuTK;
|
||||
using osuTK.Graphics;
|
||||
@ -30,12 +30,13 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private readonly BindableDouble trackFreq = new BindableDouble(1);
|
||||
|
||||
private Track track;
|
||||
|
||||
private const float duration = 2500;
|
||||
|
||||
private SampleChannel failSample;
|
||||
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
public FailAnimation(DrawableRuleset drawableRuleset)
|
||||
{
|
||||
this.drawableRuleset = drawableRuleset;
|
||||
@ -44,7 +45,6 @@ namespace osu.Game.Screens.Play
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, IBindable<WorkingBeatmap> beatmap)
|
||||
{
|
||||
track = beatmap.Value.Track;
|
||||
failSample = audio.Samples.Get(@"Gameplay/failsound");
|
||||
}
|
||||
|
||||
@ -68,7 +68,7 @@ namespace osu.Game.Screens.Play
|
||||
Expire();
|
||||
});
|
||||
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, trackFreq);
|
||||
musicController.AddAdjustment(AdjustableProperty.Frequency, trackFreq);
|
||||
|
||||
applyToPlayfield(drawableRuleset.Playfield);
|
||||
drawableRuleset.Playfield.HitObjectContainer.FlashColour(Color4.Red, 500);
|
||||
@ -107,7 +107,7 @@ namespace osu.Game.Screens.Play
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
track?.RemoveAdjustment(AdjustableProperty.Frequency, trackFreq);
|
||||
musicController?.RemoveAdjustment(AdjustableProperty.Frequency, trackFreq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8,13 +8,13 @@ using System.Threading.Tasks;
|
||||
using osu.Framework;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
|
||||
namespace osu.Game.Screens.Play
|
||||
@ -27,10 +27,8 @@ namespace osu.Game.Screens.Play
|
||||
private readonly WorkingBeatmap beatmap;
|
||||
private readonly IReadOnlyList<Mod> mods;
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="WorkingBeatmap"/>'s track.
|
||||
/// </summary>
|
||||
private Track track;
|
||||
[Resolved]
|
||||
private MusicController musicController { get; set; }
|
||||
|
||||
public readonly BindableBool IsPaused = new BindableBool();
|
||||
|
||||
@ -72,8 +70,6 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
track = beatmap.Track;
|
||||
|
||||
adjustableClock = new DecoupleableInterpolatingFramedClock { IsCoupled = false };
|
||||
|
||||
// Lazer's audio timings in general doesn't match stable. This is the result of user testing, albeit limited.
|
||||
@ -125,15 +121,15 @@ namespace osu.Game.Screens.Play
|
||||
{
|
||||
// The Reset() call below causes speed adjustments to be reset in an async context, leading to deadlocks.
|
||||
// The deadlock can be prevented by resetting the track synchronously before entering the async context.
|
||||
track.ResetSpeedAdjustments();
|
||||
musicController.Reset();
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
track.Reset();
|
||||
musicController.Reset();
|
||||
|
||||
Schedule(() =>
|
||||
{
|
||||
adjustableClock.ChangeSource(track);
|
||||
adjustableClock.ChangeSource(musicController.GetTrackClock());
|
||||
updateRate();
|
||||
|
||||
if (!IsPaused.Value)
|
||||
@ -194,20 +190,6 @@ namespace osu.Game.Screens.Play
|
||||
IsPaused.Value = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the backing clock to avoid using the originally provided beatmap's track.
|
||||
/// </summary>
|
||||
public void StopUsingBeatmapClock()
|
||||
{
|
||||
if (track != beatmap.Track)
|
||||
return;
|
||||
|
||||
removeSourceClockAdjustments();
|
||||
|
||||
track = new TrackVirtual(beatmap.Track.Length);
|
||||
adjustableClock.ChangeSource(track);
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (!IsPaused.Value)
|
||||
@ -220,31 +202,23 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
private void updateRate()
|
||||
{
|
||||
if (track == null) return;
|
||||
|
||||
speedAdjustmentsApplied = true;
|
||||
track.ResetSpeedAdjustments();
|
||||
|
||||
track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
||||
track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||
|
||||
foreach (var mod in mods.OfType<IApplicableToTrack>())
|
||||
mod.ApplyToTrack(track);
|
||||
musicController.ResetTrackAdjustments();
|
||||
musicController.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
|
||||
musicController.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
|
||||
}
|
||||
|
||||
protected override void Dispose(bool isDisposing)
|
||||
{
|
||||
base.Dispose(isDisposing);
|
||||
|
||||
removeSourceClockAdjustments();
|
||||
track = null;
|
||||
}
|
||||
|
||||
private void removeSourceClockAdjustments()
|
||||
{
|
||||
if (speedAdjustmentsApplied)
|
||||
{
|
||||
track.ResetSpeedAdjustments();
|
||||
musicController.ResetTrackAdjustments();
|
||||
speedAdjustmentsApplied = false;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,6 @@
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Audio;
|
||||
using osu.Framework.Audio.Sample;
|
||||
using osu.Framework.Audio.Track;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Framework.Graphics.Containers;
|
||||
@ -561,8 +560,11 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
BeatmapDetails.Refresh();
|
||||
|
||||
Beatmap.Value.Track.Looping = true;
|
||||
music?.ResetTrackAdjustments();
|
||||
if (music != null)
|
||||
{
|
||||
music.Looping = true;
|
||||
music.ResetTrackAdjustments();
|
||||
}
|
||||
|
||||
if (Beatmap != null && !Beatmap.Value.BeatmapSetInfo.DeletePending)
|
||||
{
|
||||
@ -586,8 +588,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
BeatmapOptions.Hide();
|
||||
|
||||
if (Beatmap.Value.Track != null)
|
||||
Beatmap.Value.Track.Looping = false;
|
||||
if (music != null)
|
||||
music.Looping = false;
|
||||
|
||||
this.ScaleTo(1.1f, 250, Easing.InSine);
|
||||
|
||||
@ -608,8 +610,8 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
FilterControl.Deactivate();
|
||||
|
||||
if (Beatmap.Value.Track != null)
|
||||
Beatmap.Value.Track.Looping = false;
|
||||
if (music != null)
|
||||
music.Looping = false;
|
||||
|
||||
return false;
|
||||
}
|
||||
@ -650,28 +652,18 @@ namespace osu.Game.Screens.Select
|
||||
|
||||
BeatmapDetails.Beatmap = beatmap;
|
||||
|
||||
if (beatmap.Track != null)
|
||||
beatmap.Track.Looping = true;
|
||||
if (music != null)
|
||||
music.Looping = false;
|
||||
}
|
||||
|
||||
private readonly WeakReference<Track> lastTrack = new WeakReference<Track>(null);
|
||||
|
||||
/// <summary>
|
||||
/// Ensures some music is playing for the current track.
|
||||
/// Will resume playback from a manual user pause if the track has changed.
|
||||
/// </summary>
|
||||
private void ensurePlayingSelected()
|
||||
{
|
||||
Track track = Beatmap.Value.Track;
|
||||
|
||||
bool isNewTrack = !lastTrack.TryGetTarget(out var last) || last != track;
|
||||
|
||||
track.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
||||
|
||||
if (!track.IsRunning && (music?.IsUserPaused != true || isNewTrack))
|
||||
music?.Play(true);
|
||||
|
||||
lastTrack.SetTarget(track);
|
||||
music.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
|
||||
music.EnsurePlayingSomething();
|
||||
}
|
||||
|
||||
private void carouselBeatmapsLoaded()
|
||||
|
@ -27,8 +27,6 @@ namespace osu.Game.Tests.Beatmaps
|
||||
this.storyboard = storyboard;
|
||||
}
|
||||
|
||||
public override bool TrackLoaded => true;
|
||||
|
||||
public override bool BeatmapLoaded => true;
|
||||
|
||||
protected override IBeatmap GetBeatmap() => beatmap;
|
||||
|
@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual
|
||||
private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> e)
|
||||
{
|
||||
Clock.ControlPointInfo = e.NewValue.Beatmap.ControlPointInfo;
|
||||
Clock.ChangeSource((IAdjustableClock)e.NewValue.Track ?? new StopwatchClock());
|
||||
Clock.ChangeSource(MusicController.GetTrackClock() ?? new StopwatchClock());
|
||||
Clock.ProcessFrame();
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ using osu.Framework.Timing;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Overlays;
|
||||
using osu.Game.Rulesets;
|
||||
using osu.Game.Rulesets.Mods;
|
||||
using osu.Game.Rulesets.UI;
|
||||
@ -135,6 +136,9 @@ namespace osu.Game.Tests.Visual
|
||||
[Resolved]
|
||||
protected AudioManager Audio { get; private set; }
|
||||
|
||||
[Resolved]
|
||||
protected MusicController MusicController { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates the ruleset to be used for this test scene.
|
||||
/// </summary>
|
||||
@ -164,8 +168,8 @@ namespace osu.Game.Tests.Visual
|
||||
|
||||
rulesetDependencies?.Dispose();
|
||||
|
||||
if (Beatmap?.Value.TrackLoaded == true)
|
||||
Beatmap.Value.Track.Stop();
|
||||
if (MusicController?.TrackLoaded == true)
|
||||
MusicController.Stop();
|
||||
|
||||
if (contextFactory.IsValueCreated)
|
||||
contextFactory.Value.ResetDatabase();
|
||||
|
@ -13,7 +13,7 @@ namespace osu.Game.Tests.Visual
|
||||
base.Update();
|
||||
|
||||
// note that this will override any mod rate application
|
||||
Beatmap.Value.Track.Tempo.Value = Clock.Rate;
|
||||
MusicController.Tempo.Value = Clock.Rate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user