1
0
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:
smoogipoo 2020-08-04 21:53:00 +09:00
parent 641279ec3e
commit 6e42b8219c
57 changed files with 438 additions and 346 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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(() =>
{

View File

@ -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())

View File

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

View File

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

View File

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

View File

@ -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));

View File

@ -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());

View File

@ -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());

View File

@ -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]

View File

@ -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());
}
}
}

View File

@ -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;
}
}

View File

@ -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() =>

View File

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

View File

@ -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);

View File

@ -255,7 +255,7 @@ namespace osu.Game.Beatmaps
new LargeTextureStore(host?.CreateTextureLoaderStore(Files.Store)), beatmapInfo, audioManager));
}
previous?.TransferTo(working);
// previous?.TransferTo(working);
return working;
}
}

View File

@ -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

View File

@ -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>

View File

@ -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--;

View File

@ -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;

View File

@ -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 =>
{

View File

@ -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);

View File

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

View File

@ -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

View File

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

View File

@ -10,6 +10,6 @@ namespace osu.Game.Rulesets.Mods
/// </summary>
public interface IApplicableToTrack : IApplicableMod
{
void ApplyToTrack(Track track);
void ApplyToTrack(ITrack track);
}
}

View File

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

View File

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

View File

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

View File

@ -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();

View File

@ -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;

View File

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

View File

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

View File

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

View File

@ -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();

View File

@ -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;

View File

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

View File

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

View File

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

View File

@ -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 =>
{

View File

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

View File

@ -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);

View File

@ -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();
}
}

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

@ -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;
}
}

View File

@ -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()

View File

@ -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;

View File

@ -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();
}

View File

@ -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();

View File

@ -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;
}
}
}