1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-28 02:02:53 +08:00

Expose track from MusicController

This commit is contained in:
smoogipoo 2020-08-05 21:10:38 +09:00
parent 6e42b8219c
commit 5c05fe3988
39 changed files with 204 additions and 283 deletions

View File

@ -343,7 +343,7 @@ namespace osu.Game.Rulesets.Mania.Tests
judgementResults = new List<JudgementResult>(); judgementResults = new List<JudgementResult>();
}); });
AddUntilStep("Beatmap at 0", () => MusicController.CurrentTrackTime == 0); AddUntilStep("Beatmap at 0", () => MusicController.CurrentTrack?.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
} }

View File

@ -385,7 +385,7 @@ namespace osu.Game.Rulesets.Osu.Tests
judgementResults = new List<JudgementResult>(); judgementResults = new List<JudgementResult>();
}); });
AddUntilStep("Beatmap at 0", () => MusicController.CurrentTrackTime == 0); AddUntilStep("Beatmap at 0", () => MusicController.CurrentTrack?.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
} }

View File

@ -366,7 +366,7 @@ namespace osu.Game.Rulesets.Osu.Tests
judgementResults = new List<JudgementResult>(); judgementResults = new List<JudgementResult>();
}); });
AddUntilStep("Beatmap at 0", () => MusicController.CurrentTrackTime == 0); AddUntilStep("Beatmap at 0", () => MusicController.CurrentTrack?.CurrentTime == 0);
AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen());
AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value);
} }

View File

@ -31,6 +31,6 @@ namespace osu.Game.Tests.Skins
public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo("sample")) != null); public void TestRetrieveOggSample() => AddAssert("sample is non-null", () => beatmap.Skin.GetSample(new SampleInfo("sample")) != null);
[Test] [Test]
public void TestRetrieveOggTrack() => AddAssert("track is non-null", () => !MusicController.IsDummyDevice); public void TestRetrieveOggTrack() => AddAssert("track is non-null", () => MusicController.CurrentTrack?.IsDummyDevice == false);
} }
} }

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -94,7 +95,10 @@ namespace osu.Game.Tests.Visual.Editing
base.Update(); base.Update();
if (musicController.TrackLoaded) if (musicController.TrackLoaded)
marker.X = (float)(editorClock.CurrentTime / musicController.TrackLength); {
Debug.Assert(musicController.CurrentTrack != null);
marker.X = (float)(editorClock.CurrentTime / musicController.CurrentTrack.Length);
}
} }
} }

View File

@ -288,7 +288,7 @@ namespace osu.Game.Tests.Visual.Gameplay
private void confirmNoTrackAdjustments() private void confirmNoTrackAdjustments()
{ {
AddAssert("track has no adjustments", () => MusicController.AggregateFrequency.Value == 1); AddAssert("track has no adjustments", () => MusicController.CurrentTrack?.AggregateFrequency.Value == 1);
} }
private void restart() => AddStep("restart", () => Player.Restart()); 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); Beatmap.Value = CreateWorkingBeatmap(new OsuRuleset().RulesetInfo);
foreach (var mod in SelectedMods.Value.OfType<IApplicableToTrack>()) foreach (var mod in SelectedMods.Value.OfType<IApplicableToTrack>())
mod.ApplyToTrack(MusicController); mod.ApplyToTrack(MusicController.CurrentTrack);
InputManager.Child = container = new TestPlayerLoaderContainer( InputManager.Child = container = new TestPlayerLoaderContainer(
loader = new TestPlayerLoader(() => loader = new TestPlayerLoader(() =>
@ -77,12 +77,12 @@ namespace osu.Game.Tests.Visual.Gameplay
{ {
AddStep("load dummy beatmap", () => ResetPlayer(false, () => SelectedMods.Value = new[] { new OsuModNightcore() })); AddStep("load dummy beatmap", () => ResetPlayer(false, () => SelectedMods.Value = new[] { new OsuModNightcore() }));
AddUntilStep("wait for current", () => loader.IsCurrentScreen()); AddUntilStep("wait for current", () => loader.IsCurrentScreen());
AddAssert("mod rate applied", () => MusicController.Rate != 1); AddAssert("mod rate applied", () => MusicController.CurrentTrack?.Rate != 1);
AddStep("exit loader", () => loader.Exit()); AddStep("exit loader", () => loader.Exit());
AddUntilStep("wait for not current", () => !loader.IsCurrentScreen()); AddUntilStep("wait for not current", () => !loader.IsCurrentScreen());
AddAssert("player did not load", () => !player.IsLoaded); AddAssert("player did not load", () => !player.IsLoaded);
AddUntilStep("player disposed", () => loader.DisposalTask?.IsCompleted == true); AddUntilStep("player disposed", () => loader.DisposalTask?.IsCompleted == true);
AddAssert("mod rate still applied", () => MusicController.Rate != 1); AddAssert("mod rate still applied", () => MusicController.CurrentTrack?.Rate != 1);
} }
[Test] [Test]

View File

@ -87,9 +87,9 @@ namespace osu.Game.Tests.Visual.Gameplay
private void restart() private void restart()
{ {
MusicController.Reset(); MusicController.CurrentTrack?.Reset();
loadStoryboard(Beatmap.Value); loadStoryboard(Beatmap.Value);
MusicController.Play(true); MusicController.CurrentTrack?.Start();
} }
private void loadStoryboard(WorkingBeatmap working) private void loadStoryboard(WorkingBeatmap working)
@ -104,7 +104,7 @@ namespace osu.Game.Tests.Visual.Gameplay
storyboard.Passing = false; storyboard.Passing = false;
storyboardContainer.Add(storyboard); storyboardContainer.Add(storyboard);
decoupledClock.ChangeSource(musicController.GetTrackClock()); decoupledClock.ChangeSource(musicController.CurrentTrack);
} }
private void loadStoryboardNoVideo() private void loadStoryboardNoVideo()
@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.Gameplay
storyboard = sb.CreateDrawable(Beatmap.Value); storyboard = sb.CreateDrawable(Beatmap.Value);
storyboardContainer.Add(storyboard); storyboardContainer.Add(storyboard);
decoupledClock.ChangeSource(musicController.GetTrackClock()); decoupledClock.ChangeSource(musicController.CurrentTrack);
} }
} }
} }

View File

@ -16,7 +16,7 @@ namespace osu.Game.Tests.Visual.Menus
{ {
AddUntilStep("wait for load", () => MusicController.TrackLoaded); AddUntilStep("wait for load", () => MusicController.TrackLoaded);
AddAssert("check if menu music loops", () => MusicController.Looping); AddAssert("check if menu music loops", () => MusicController.CurrentTrack?.Looping == true);
} }
} }
} }

View File

@ -4,6 +4,7 @@
using System.Linq; using System.Linq;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -61,12 +62,12 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("wait for fail", () => player.HasFailed); AddUntilStep("wait for fail", () => player.HasFailed);
AddUntilStep("wait for track stop", () => !MusicController.IsPlaying); AddUntilStep("wait for track stop", () => !MusicController.IsPlaying);
AddAssert("Ensure time before preview point", () => MusicController.CurrentTrackTime < beatmap().Metadata.PreviewTime); AddAssert("Ensure time before preview point", () => MusicController.CurrentTrack?.CurrentTime < beatmap().Metadata.PreviewTime);
pushEscape(); pushEscape();
AddUntilStep("wait for track playing", () => MusicController.IsPlaying); AddUntilStep("wait for track playing", () => MusicController.IsPlaying);
AddAssert("Ensure time wasn't reset to preview point", () => MusicController.CurrentTrackTime < beatmap().Metadata.PreviewTime); AddAssert("Ensure time wasn't reset to preview point", () => MusicController.CurrentTrack?.CurrentTime < beatmap().Metadata.PreviewTime);
} }
[Test] [Test]
@ -76,11 +77,11 @@ namespace osu.Game.Tests.Visual.Navigation
PushAndConfirm(() => songSelect = new TestSongSelect()); PushAndConfirm(() => songSelect = new TestSongSelect());
AddUntilStep("wait for no track", () => MusicController.IsDummyDevice); AddUntilStep("wait for no track", () => MusicController.CurrentTrack?.IsDummyDevice == true);
AddStep("return to menu", () => songSelect.Exit()); AddStep("return to menu", () => songSelect.Exit());
AddUntilStep("wait for track", () => !MusicController.IsDummyDevice && MusicController.IsPlaying); AddUntilStep("wait for track", () => MusicController.CurrentTrack?.IsDummyDevice == false && MusicController.IsPlaying);
} }
[Test] [Test]
@ -135,8 +136,8 @@ namespace osu.Game.Tests.Visual.Navigation
AddUntilStep("Wait for music controller", () => Game.MusicController.IsLoaded); AddUntilStep("Wait for music controller", () => Game.MusicController.IsLoaded);
AddStep("Seek close to end", () => AddStep("Seek close to end", () =>
{ {
Game.MusicController.SeekTo(MusicController.TrackLength - 1000); Game.MusicController.SeekTo(MusicController.CurrentTrack.AsNonNull().Length - 1000);
// MusicController.Completed += () => trackCompleted = true; MusicController.CurrentTrack.AsNonNull().Completed += () => trackCompleted = true;
}); });
AddUntilStep("Track was completed", () => trackCompleted); AddUntilStep("Track was completed", () => trackCompleted);

View File

@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -168,7 +169,7 @@ namespace osu.Game.Tests.Visual.UserInterface
if (timingPoints.Count == 0) return 0; if (timingPoints.Count == 0) return 0;
if (timingPoints[^1] == current) if (timingPoints[^1] == current)
return (int)Math.Ceiling((musicController.TrackLength - current.Time) / current.BeatLength); return (int)Math.Ceiling((musicController.CurrentTrack.AsNonNull().Length - current.Time) / current.BeatLength);
return (int)Math.Ceiling((getNextTimingPoint(current).Time - 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("Store track", () => currentBeatmap = Beatmap.Value);
AddStep(@"Seek track to 6 second", () => musicController.SeekTo(6000)); AddStep(@"Seek track to 6 second", () => musicController.SeekTo(6000));
AddUntilStep(@"Wait for current time to update", () => musicController.CurrentTrackTime > 5000); AddUntilStep(@"Wait for current time to update", () => musicController.CurrentTrack?.CurrentTime > 5000);
AddStep(@"Set previous", () => musicController.PreviousTrack()); AddStep(@"Set previous", () => musicController.PreviousTrack());
AddAssert(@"Check beatmap didn't change", () => currentBeatmap == Beatmap.Value); AddAssert(@"Check beatmap didn't change", () => currentBeatmap == Beatmap.Value);
AddUntilStep("Wait for current time to update", () => musicController.CurrentTrackTime < 5000); AddUntilStep("Wait for current time to update", () => musicController.CurrentTrack?.CurrentTime < 5000);
AddStep(@"Set previous", () => musicController.PreviousTrack()); AddStep(@"Set previous", () => musicController.PreviousTrack());
AddAssert(@"Check beatmap did change", () => currentBeatmap != Beatmap.Value); AddAssert(@"Check beatmap did change", () => currentBeatmap != Beatmap.Value);

View File

@ -51,6 +51,7 @@ namespace osu.Game.Graphics.Containers
protected override void Update() protected override void Update()
{ {
ITrack track = null;
IBeatmap beatmap = null; IBeatmap beatmap = null;
double currentTrackTime = 0; double currentTrackTime = 0;
@ -58,11 +59,14 @@ namespace osu.Game.Graphics.Containers
EffectControlPoint effectPoint = null; EffectControlPoint effectPoint = null;
if (musicController.TrackLoaded && Beatmap.Value.BeatmapLoaded) if (musicController.TrackLoaded && Beatmap.Value.BeatmapLoaded)
beatmap = Beatmap.Value.Beatmap;
if (beatmap != null && musicController.IsPlaying && musicController.TrackLength > 0)
{ {
currentTrackTime = musicController.CurrentTrackTime + EarlyActivationMilliseconds; track = musicController.CurrentTrack;
beatmap = Beatmap.Value.Beatmap;
}
if (track != null && beatmap != null && musicController.IsPlaying && track.Length > 0)
{
currentTrackTime = track.CurrentTime + EarlyActivationMilliseconds;
timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime); timingPoint = beatmap.ControlPointInfo.TimingPointAt(currentTrackTime);
effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime); effectPoint = beatmap.ControlPointInfo.EffectPointAt(currentTrackTime);
@ -98,7 +102,7 @@ namespace osu.Game.Graphics.Containers
return; return;
using (BeginDelayedSequence(-TimeSinceLastBeat, true)) using (BeginDelayedSequence(-TimeSinceLastBeat, true))
OnNewBeat(beatIndex, timingPoint, effectPoint, musicController.CurrentAmplitudes); OnNewBeat(beatIndex, timingPoint, effectPoint, track?.CurrentAmplitudes ?? ChannelAmplitudes.Empty);
lastBeat = beatIndex; lastBeat = beatIndex;
lastTimingPoint = timingPoint; lastTimingPoint = timingPoint;

View File

@ -6,7 +6,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using JetBrains.Annotations; using JetBrains.Annotations;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -15,7 +14,6 @@ using osu.Framework.Graphics.Containers;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Input.Bindings; using osu.Game.Input.Bindings;
using osu.Game.Overlays.OSD; using osu.Game.Overlays.OSD;
@ -26,7 +24,7 @@ namespace osu.Game.Overlays
/// <summary> /// <summary>
/// Handles playback of the global music track. /// Handles playback of the global music track.
/// </summary> /// </summary>
public class MusicController : CompositeDrawable, IKeyBindingHandler<GlobalAction>, ITrack public class MusicController : CompositeDrawable, IKeyBindingHandler<GlobalAction>
{ {
[Resolved] [Resolved]
private BeatmapManager beatmaps { get; set; } private BeatmapManager beatmaps { get; set; }
@ -70,10 +68,7 @@ namespace osu.Game.Overlays
private readonly TrackContainer trackContainer; private readonly TrackContainer trackContainer;
[CanBeNull] [CanBeNull]
private DrawableTrack drawableTrack; public DrawableTrack CurrentTrack { get; private set; }
[CanBeNull]
private Track track;
private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated; private IBindable<WeakReference<BeatmapSetInfo>> managerUpdated;
private IBindable<WeakReference<BeatmapSetInfo>> managerRemoved; private IBindable<WeakReference<BeatmapSetInfo>> managerRemoved;
@ -116,33 +111,12 @@ namespace osu.Game.Overlays
/// <summary> /// <summary>
/// Returns whether the beatmap track is playing. /// Returns whether the beatmap track is playing.
/// </summary> /// </summary>
public bool IsPlaying => drawableTrack?.IsRunning ?? false; public bool IsPlaying => CurrentTrack?.IsRunning ?? false;
/// <summary> /// <summary>
/// Returns whether the beatmap track is loaded. /// Returns whether the beatmap track is loaded.
/// </summary> /// </summary>
public bool TrackLoaded => drawableTrack?.IsLoaded == true; public bool TrackLoaded => CurrentTrack?.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) private void beatmapUpdated(ValueChangedEvent<WeakReference<BeatmapSetInfo>> weakSet)
{ {
@ -175,7 +149,7 @@ namespace osu.Game.Overlays
seekDelegate = Schedule(() => seekDelegate = Schedule(() =>
{ {
if (!beatmap.Disabled) if (!beatmap.Disabled)
drawableTrack?.Seek(position); CurrentTrack?.Seek(position);
}); });
} }
@ -187,7 +161,7 @@ namespace osu.Game.Overlays
{ {
if (IsUserPaused) return; if (IsUserPaused) return;
if (drawableTrack == null || drawableTrack.IsDummyDevice) if (CurrentTrack == null || CurrentTrack.IsDummyDevice)
{ {
if (beatmap.Disabled) if (beatmap.Disabled)
return; return;
@ -208,13 +182,13 @@ namespace osu.Game.Overlays
{ {
IsUserPaused = false; IsUserPaused = false;
if (drawableTrack == null) if (CurrentTrack == null)
return false; return false;
if (restart) if (restart)
drawableTrack.Restart(); CurrentTrack.Restart();
else if (!IsPlaying) else if (!IsPlaying)
drawableTrack.Start(); CurrentTrack.Start();
return true; return true;
} }
@ -225,8 +199,8 @@ namespace osu.Game.Overlays
public void Stop() public void Stop()
{ {
IsUserPaused = true; IsUserPaused = true;
if (drawableTrack?.IsRunning == true) if (CurrentTrack?.IsRunning == true)
drawableTrack.Stop(); CurrentTrack.Stop();
} }
/// <summary> /// <summary>
@ -235,7 +209,7 @@ namespace osu.Game.Overlays
/// <returns>Whether the operation was successful.</returns> /// <returns>Whether the operation was successful.</returns>
public bool TogglePause() public bool TogglePause()
{ {
if (drawableTrack?.IsRunning == true) if (CurrentTrack?.IsRunning == true)
Stop(); Stop();
else else
Play(); Play();
@ -257,7 +231,7 @@ namespace osu.Game.Overlays
if (beatmap.Disabled) if (beatmap.Disabled)
return PreviousTrackResult.None; return PreviousTrackResult.None;
var currentTrackPosition = drawableTrack?.CurrentTime; var currentTrackPosition = CurrentTrack?.CurrentTime;
if (currentTrackPosition >= restart_cutoff_point) if (currentTrackPosition >= restart_cutoff_point)
{ {
@ -311,7 +285,7 @@ namespace osu.Game.Overlays
{ {
// if not scheduled, the previously track will be stopped one frame later (see ScheduleAfterChildren logic in GameBase). // 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. // we probably want to move this to a central method for switching to a new working beatmap in the future.
Schedule(() => drawableTrack?.Restart()); Schedule(() => CurrentTrack?.Restart());
} }
private WorkingBeatmap current; private WorkingBeatmap current;
@ -345,12 +319,11 @@ namespace osu.Game.Overlays
current = beatmap.NewValue; current = beatmap.NewValue;
drawableTrack?.Expire(); CurrentTrack?.Expire();
drawableTrack = null; CurrentTrack = null;
track = null;
if (current != null) if (current != null)
trackContainer.Add(drawableTrack = new DrawableTrack(track = current.GetRealTrack())); trackContainer.Add(CurrentTrack = new DrawableTrack(current.GetRealTrack()));
TrackChanged?.Invoke(current, direction); TrackChanged?.Invoke(current, direction);
@ -379,15 +352,15 @@ namespace osu.Game.Overlays
public void ResetTrackAdjustments() public void ResetTrackAdjustments()
{ {
if (drawableTrack == null) if (CurrentTrack == null)
return; return;
drawableTrack.ResetSpeedAdjustments(); CurrentTrack.ResetSpeedAdjustments();
if (allowRateAdjustments) if (allowRateAdjustments)
{ {
foreach (var mod in mods.Value.OfType<IApplicableToTrack>()) foreach (var mod in mods.Value.OfType<IApplicableToTrack>())
mod.ApplyToTrack(drawableTrack); mod.ApplyToTrack(CurrentTrack);
} }
} }
@ -442,129 +415,6 @@ namespace osu.Game.Overlays
private class TrackContainer : AudioContainer<DrawableTrack> 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 public enum TrackChangeDirection

View File

@ -234,12 +234,14 @@ namespace osu.Game.Overlays
pendingBeatmapSwitch = null; pendingBeatmapSwitch = null;
} }
if (musicController.IsDummyDevice == false) var track = musicController.TrackLoaded ? musicController.CurrentTrack : null;
{
progressBar.EndTime = musicController.TrackLength;
progressBar.CurrentTime = musicController.CurrentTrackTime;
playButton.Icon = musicController.IsPlaying ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle; if (track?.IsDummyDevice == false)
{
progressBar.EndTime = track.Length;
progressBar.CurrentTime = track.CurrentTime;
playButton.Icon = track.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle;
} }
else else
{ {

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Audio;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
namespace osu.Game.Rulesets.Mods namespace osu.Game.Rulesets.Mods
@ -10,6 +11,6 @@ namespace osu.Game.Rulesets.Mods
/// </summary> /// </summary>
public interface IApplicableToTrack : IApplicableMod public interface IApplicableToTrack : IApplicableMod
{ {
void ApplyToTrack(ITrack track); void ApplyToTrack<T>(T track) where T : ITrack, IAdjustableAudioComponent;
} }
} }

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
@ -27,11 +26,11 @@ namespace osu.Game.Rulesets.Mods
}, true); }, true);
} }
public override void ApplyToTrack(ITrack track) public override void ApplyToTrack<T>(T track)
{ {
// base.ApplyToTrack() intentionally not called (different tempo adjustment is applied) // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied)
(track as Track)?.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
(track as Track)?.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust); track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
} }
} }
} }

View File

@ -38,11 +38,11 @@ namespace osu.Game.Rulesets.Mods
}, true); }, true);
} }
public override void ApplyToTrack(ITrack track) public override void ApplyToTrack<T>(T track)
{ {
// base.ApplyToTrack() intentionally not called (different tempo adjustment is applied) // base.ApplyToTrack() intentionally not called (different tempo adjustment is applied)
(track as Track)?.AddAdjustment(AdjustableProperty.Frequency, freqAdjust); track.AddAdjustment(AdjustableProperty.Frequency, freqAdjust);
(track as Track)?.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust); track.AddAdjustment(AdjustableProperty.Tempo, tempoAdjust);
} }
public void ApplyToDrawableRuleset(DrawableRuleset<TObject> drawableRuleset) public void ApplyToDrawableRuleset(DrawableRuleset<TObject> drawableRuleset)

View File

@ -12,9 +12,9 @@ namespace osu.Game.Rulesets.Mods
{ {
public abstract BindableNumber<double> SpeedChange { get; } public abstract BindableNumber<double> SpeedChange { get; }
public virtual void ApplyToTrack(ITrack track) public virtual void ApplyToTrack<T>(T track) where T : ITrack, IAdjustableAudioComponent
{ {
(track as Track)?.AddAdjustment(AdjustableProperty.Tempo, SpeedChange); track.AddAdjustment(AdjustableProperty.Tempo, SpeedChange);
} }
public virtual void ApplyToSample(SampleChannel sample) public virtual void ApplyToSample(SampleChannel sample)

View File

@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Mods
Precision = 0.01, Precision = 0.01,
}; };
private Track track; private ITrack track;
protected ModTimeRamp() protected ModTimeRamp()
{ {
@ -51,9 +51,9 @@ namespace osu.Game.Rulesets.Mods
AdjustPitch.BindValueChanged(applyPitchAdjustment); AdjustPitch.BindValueChanged(applyPitchAdjustment);
} }
public void ApplyToTrack(ITrack track) public void ApplyToTrack<T>(T track) where T : ITrack, IAdjustableAudioComponent
{ {
this.track = track as Track; this.track = track;
FinalRate.TriggerChange(); FinalRate.TriggerChange();
AdjustPitch.TriggerChange(); AdjustPitch.TriggerChange();
@ -89,9 +89,9 @@ namespace osu.Game.Rulesets.Mods
private void applyPitchAdjustment(ValueChangedEvent<bool> adjustPitchSetting) private void applyPitchAdjustment(ValueChangedEvent<bool> adjustPitchSetting)
{ {
// remove existing old adjustment // remove existing old adjustment
track?.RemoveAdjustment(adjustmentForPitchSetting(adjustPitchSetting.OldValue), SpeedChange); (track as IAdjustableAudioComponent)?.RemoveAdjustment(adjustmentForPitchSetting(adjustPitchSetting.OldValue), SpeedChange);
track?.AddAdjustment(adjustmentForPitchSetting(adjustPitchSetting.NewValue), SpeedChange); (track as IAdjustableAudioComponent)?.AddAdjustment(adjustmentForPitchSetting(adjustPitchSetting.NewValue), SpeedChange);
} }
private AdjustableProperty adjustmentForPitchSetting(bool adjustPitchSettingValue) private AdjustableProperty adjustmentForPitchSetting(bool adjustPitchSettingValue)

View File

@ -66,12 +66,12 @@ namespace osu.Game.Screens.Edit.Components
} }
}; };
musicController.AddAdjustment(AdjustableProperty.Tempo, tempo); musicController.CurrentTrack?.AddAdjustment(AdjustableProperty.Tempo, tempo);
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
musicController?.RemoveAdjustment(AdjustableProperty.Tempo, tempo); musicController?.CurrentTrack?.RemoveAdjustment(AdjustableProperty.Tempo, tempo);
base.Dispose(isDisposing); base.Dispose(isDisposing);
} }

View File

@ -2,6 +2,7 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osuTK; using osuTK;
@ -57,7 +58,8 @@ namespace osu.Game.Screens.Edit.Components.Timelines.Summary.Parts
return; return;
} }
content.RelativeChildSize = new Vector2((float)Math.Max(1, musicController.TrackLength), 1); Debug.Assert(musicController.CurrentTrack != null);
content.RelativeChildSize = new Vector2((float)Math.Max(1, musicController.CurrentTrack.Length), 1);
} }
protected virtual void LoadBeatmap(WorkingBeatmap beatmap) protected virtual void LoadBeatmap(WorkingBeatmap beatmap)

View File

@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System; using System;
using System.Diagnostics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -60,21 +62,20 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
Beatmap.BindValueChanged(b => Beatmap.BindValueChanged(b =>
{ {
waveform.Waveform = b.NewValue.Waveform; waveform.Waveform = b.NewValue.Waveform;
track = musicController.CurrentTrack;
// Todo: Wrong. Debug.Assert(track != null);
Schedule(() =>
if (track.Length > 0)
{ {
if (musicController.TrackLength > 0) MaxZoom = getZoomLevelForVisibleMilliseconds(500);
{ MinZoom = getZoomLevelForVisibleMilliseconds(10000);
MaxZoom = getZoomLevelForVisibleMilliseconds(500); Zoom = getZoomLevelForVisibleMilliseconds(2000);
MinZoom = getZoomLevelForVisibleMilliseconds(10000); }
Zoom = getZoomLevelForVisibleMilliseconds(2000);
}
});
}, true); }, true);
} }
private float getZoomLevelForVisibleMilliseconds(double milliseconds) => (float)(musicController.TrackLength / milliseconds); private float getZoomLevelForVisibleMilliseconds(double milliseconds) => (float)(track.Length / milliseconds);
/// <summary> /// <summary>
/// The timeline's scroll position in the last frame. /// The timeline's scroll position in the last frame.
@ -96,6 +97,8 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
/// </summary> /// </summary>
private bool trackWasPlaying; private bool trackWasPlaying;
private ITrack track;
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
@ -136,15 +139,15 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
if (!musicController.TrackLoaded) if (!musicController.TrackLoaded)
return; return;
editorClock.Seek(Current / Content.DrawWidth * musicController.TrackLength); editorClock.Seek(Current / Content.DrawWidth * track.Length);
} }
private void scrollToTrackTime() private void scrollToTrackTime()
{ {
if (!musicController.TrackLoaded || musicController.TrackLength == 0) if (!musicController.TrackLoaded || track.Length == 0)
return; return;
ScrollTo((float)(editorClock.CurrentTime / musicController.TrackLength) * Content.DrawWidth, false); ScrollTo((float)(editorClock.CurrentTime / track.Length) * Content.DrawWidth, false);
} }
protected override bool OnMouseDown(MouseDownEvent e) protected override bool OnMouseDown(MouseDownEvent e)
@ -188,7 +191,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition)))); new SnapResult(screenSpacePosition, beatSnapProvider.SnapTime(getTimeFromPosition(Content.ToLocalSpace(screenSpacePosition))));
private double getTimeFromPosition(Vector2 localPosition) => private double getTimeFromPosition(Vector2 localPosition) =>
(localPosition.X / Content.DrawWidth) * musicController.TrackLength; (localPosition.X / Content.DrawWidth) * track.Length;
public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException(); public float GetBeatSnapDistanceAt(double referenceTime) => throw new NotImplementedException();

View File

@ -3,6 +3,7 @@
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -43,7 +44,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++) for (var i = 0; i < beatmap.ControlPointInfo.TimingPoints.Count; i++)
{ {
var point = beatmap.ControlPointInfo.TimingPoints[i]; var point = beatmap.ControlPointInfo.TimingPoints[i];
var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : musicController.TrackLength; var until = i + 1 < beatmap.ControlPointInfo.TimingPoints.Count ? beatmap.ControlPointInfo.TimingPoints[i + 1].Time : musicController.CurrentTrack.AsNonNull().Length;
int beat = 0; int beat = 0;

View File

@ -83,7 +83,7 @@ namespace osu.Game.Screens.Edit
beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue); beatDivisor.BindValueChanged(divisor => Beatmap.Value.BeatmapInfo.BeatDivisor = divisor.NewValue);
// Todo: should probably be done at a DrawableRuleset level to share logic with Player. // Todo: should probably be done at a DrawableRuleset level to share logic with Player.
var sourceClock = musicController.GetTrackClock() ?? new StopwatchClock(); var sourceClock = (IAdjustableClock)musicController.CurrentTrack ?? new StopwatchClock();
clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false }; clock = new EditorClock(Beatmap.Value, beatDivisor) { IsCoupled = false };
clock.ChangeSource(sourceClock); clock.ChangeSource(sourceClock);

View File

@ -55,7 +55,7 @@ namespace osu.Game.Screens.Edit
private void load() private void load()
{ {
// Todo: What. // Todo: What.
TrackLength ??= musicController.TrackLength; TrackLength ??= musicController.CurrentTrack?.Length ?? 0;
} }
/// <summary> /// <summary>

View File

@ -113,7 +113,9 @@ namespace osu.Game.Screens.Menu
if (setInfo != null) if (setInfo != null)
{ {
initialBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]); initialBeatmap = beatmaps.GetWorkingBeatmap(setInfo.Beatmaps[0]);
UsingThemedIntro = !MusicController.IsDummyDevice;
// Todo: Wrong.
UsingThemedIntro = MusicController.CurrentTrack?.IsDummyDevice == false;
} }
return UsingThemedIntro; return UsingThemedIntro;

View File

@ -59,7 +59,7 @@ namespace osu.Game.Screens.Menu
LoadComponentAsync(new TrianglesIntroSequence(logo, background) LoadComponentAsync(new TrianglesIntroSequence(logo, background)
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Clock = new FramedClock(UsingThemedIntro ? MusicController.GetTrackClock() : null), Clock = new FramedClock(UsingThemedIntro ? MusicController.CurrentTrack : null),
LoadMenu = LoadMenu LoadMenu = LoadMenu
}, t => }, t =>
{ {

View File

@ -44,7 +44,8 @@ namespace osu.Game.Screens.Menu
pianoReverb = audio.Samples.Get(@"Intro/Welcome/welcome_piano"); pianoReverb = audio.Samples.Get(@"Intro/Welcome/welcome_piano");
musicController.Looping = true; if (musicController.CurrentTrack != null)
musicController.CurrentTrack.Looping = true;
} }
protected override void LogoArriving(OsuLogo logo, bool resuming) protected override void LogoArriving(OsuLogo logo, bool resuming)

View File

@ -19,6 +19,7 @@ using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -108,14 +109,14 @@ namespace osu.Game.Screens.Menu
private void updateAmplitudes() private void updateAmplitudes()
{ {
var effect = beatmap.Value.BeatmapLoaded && musicController.TrackLoaded var effect = beatmap.Value.BeatmapLoaded && musicController.TrackLoaded
? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(musicController.CurrentTrackTime) ? beatmap.Value.Beatmap?.ControlPointInfo.EffectPointAt(musicController.CurrentTrack.AsNonNull().CurrentTime)
: null; : null;
for (int i = 0; i < temporalAmplitudes.Length; i++) for (int i = 0; i < temporalAmplitudes.Length; i++)
temporalAmplitudes[i] = 0; temporalAmplitudes[i] = 0;
if (musicController.TrackLoaded) if (musicController.TrackLoaded)
addAmplitudesFromSource(musicController); addAmplitudesFromSource(musicController.CurrentTrack.AsNonNull());
foreach (var source in amplitudeSources) foreach (var source in amplitudeSources)
addAmplitudesFromSource(source); addAmplitudesFromSource(source);

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System.Diagnostics;
using System.Linq; using System.Linq;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -181,11 +182,12 @@ namespace osu.Game.Screens.Menu
if (last is IntroScreen && musicController.TrackLoaded) if (last is IntroScreen && musicController.TrackLoaded)
{ {
// Todo: Wrong. Debug.Assert(musicController.CurrentTrack != null);
if (!musicController.IsPlaying)
if (!musicController.CurrentTrack.IsRunning)
{ {
musicController.SeekTo(metadata.PreviewTime != -1 ? metadata.PreviewTime : 0.4f * musicController.TrackLength); musicController.CurrentTrack.Seek(metadata.PreviewTime != -1 ? metadata.PreviewTime : 0.4f * musicController.CurrentTrack.Length);
musicController.Play(); musicController.CurrentTrack.Start();
} }
} }

View File

@ -330,9 +330,9 @@ namespace osu.Game.Screens.Menu
const float velocity_adjust_cutoff = 0.98f; const float velocity_adjust_cutoff = 0.98f;
const float paused_velocity = 0.5f; const float paused_velocity = 0.5f;
if (musicController.IsPlaying) if (musicController.CurrentTrack?.IsRunning == true)
{ {
var maxAmplitude = lastBeatIndex >= 0 ? musicController.CurrentAmplitudes.Maximum : 0; var maxAmplitude = lastBeatIndex >= 0 ? musicController.CurrentTrack.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)); 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) if (maxAmplitude > velocity_adjust_cutoff)

View File

@ -104,7 +104,7 @@ namespace osu.Game.Screens.Multi.Match.Components
return; return;
} }
bool hasEnoughTime = DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(musicController.TrackLength) < endDate.Value; bool hasEnoughTime = musicController.CurrentTrack != null && DateTimeOffset.UtcNow.AddSeconds(30).AddMilliseconds(musicController.CurrentTrack.Length) < endDate.Value;
Enabled.Value = hasBeatmap && hasEnoughTime; Enabled.Value = hasBeatmap && hasEnoughTime;
} }

View File

@ -343,9 +343,13 @@ namespace osu.Game.Screens.Multi
{ {
if (screenStack.CurrentScreen is MatchSubScreen) if (screenStack.CurrentScreen is MatchSubScreen)
{ {
musicController.RestartPoint = Beatmap.Value.Metadata.PreviewTime; if (musicController.CurrentTrack != null)
musicController.Looping = true; {
musicController.EnsurePlayingSomething(); musicController.CurrentTrack.RestartPoint = Beatmap.Value.Metadata.PreviewTime;
musicController.CurrentTrack.Looping = true;
musicController.EnsurePlayingSomething();
}
} }
else else
{ {
@ -355,8 +359,11 @@ namespace osu.Game.Screens.Multi
private void cancelLooping() private void cancelLooping()
{ {
musicController.Looping = false; if (musicController.CurrentTrack != null)
musicController.RestartPoint = 0; {
musicController.CurrentTrack.Looping = false;
musicController.CurrentTrack.RestartPoint = 0;
}
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)

View File

@ -9,6 +9,7 @@ using System.Collections.Generic;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Overlays; using osu.Game.Overlays;
@ -30,21 +31,21 @@ namespace osu.Game.Screens.Play
private readonly BindableDouble trackFreq = new BindableDouble(1); private readonly BindableDouble trackFreq = new BindableDouble(1);
private DrawableTrack track;
private const float duration = 2500; private const float duration = 2500;
private SampleChannel failSample; private SampleChannel failSample;
[Resolved]
private MusicController musicController { get; set; }
public FailAnimation(DrawableRuleset drawableRuleset) public FailAnimation(DrawableRuleset drawableRuleset)
{ {
this.drawableRuleset = drawableRuleset; this.drawableRuleset = drawableRuleset;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(AudioManager audio, IBindable<WorkingBeatmap> beatmap) private void load(AudioManager audio, IBindable<WorkingBeatmap> beatmap, MusicController musicController)
{ {
track = musicController.CurrentTrack;
failSample = audio.Samples.Get(@"Gameplay/failsound"); failSample = audio.Samples.Get(@"Gameplay/failsound");
} }
@ -68,7 +69,7 @@ namespace osu.Game.Screens.Play
Expire(); Expire();
}); });
musicController.AddAdjustment(AdjustableProperty.Frequency, trackFreq); track.AddAdjustment(AdjustableProperty.Frequency, trackFreq);
applyToPlayfield(drawableRuleset.Playfield); applyToPlayfield(drawableRuleset.Playfield);
drawableRuleset.Playfield.HitObjectContainer.FlashColour(Color4.Red, 500); drawableRuleset.Playfield.HitObjectContainer.FlashColour(Color4.Red, 500);
@ -107,7 +108,7 @@ namespace osu.Game.Screens.Play
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
musicController?.RemoveAdjustment(AdjustableProperty.Frequency, trackFreq); track?.RemoveAdjustment(AdjustableProperty.Frequency, trackFreq);
} }
} }
} }

View File

@ -8,8 +8,10 @@ using System.Threading.Tasks;
using osu.Framework; using osu.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Audio;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
@ -27,8 +29,7 @@ namespace osu.Game.Screens.Play
private readonly WorkingBeatmap beatmap; private readonly WorkingBeatmap beatmap;
private readonly IReadOnlyList<Mod> mods; private readonly IReadOnlyList<Mod> mods;
[Resolved] private DrawableTrack track;
private MusicController musicController { get; set; }
public readonly BindableBool IsPaused = new BindableBool(); public readonly BindableBool IsPaused = new BindableBool();
@ -95,8 +96,10 @@ namespace osu.Game.Screens.Play
private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1); private readonly BindableDouble pauseFreqAdjust = new BindableDouble(1);
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config, MusicController musicController)
{ {
track = musicController.CurrentTrack;
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset); userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
userAudioOffset.BindValueChanged(offset => userOffsetClock.Offset = offset.NewValue, true); userAudioOffset.BindValueChanged(offset => userOffsetClock.Offset = offset.NewValue, true);
@ -121,15 +124,15 @@ namespace osu.Game.Screens.Play
{ {
// The Reset() call below causes speed adjustments to be reset in an async context, leading to deadlocks. // 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. // The deadlock can be prevented by resetting the track synchronously before entering the async context.
musicController.Reset(); track.ResetSpeedAdjustments();
Task.Run(() => Task.Run(() =>
{ {
musicController.Reset(); track.Reset();
Schedule(() => Schedule(() =>
{ {
adjustableClock.ChangeSource(musicController.GetTrackClock()); adjustableClock.ChangeSource(track);
updateRate(); updateRate();
if (!IsPaused.Value) if (!IsPaused.Value)
@ -190,6 +193,20 @@ namespace osu.Game.Screens.Play
IsPaused.Value = true; IsPaused.Value = true;
} }
/// <summary>
/// Changes the backing clock to avoid using the originally provided track.
/// </summary>
public void StopUsingBeatmapClock()
{
if (track == null)
return;
removeSourceClockAdjustments();
track = new DrawableTrack(new TrackVirtual(track.Length));
adjustableClock.ChangeSource(track);
}
protected override void Update() protected override void Update()
{ {
if (!IsPaused.Value) if (!IsPaused.Value)
@ -202,23 +219,30 @@ namespace osu.Game.Screens.Play
private void updateRate() private void updateRate()
{ {
if (track == null) return;
speedAdjustmentsApplied = true; speedAdjustmentsApplied = true;
musicController.ResetTrackAdjustments(); track.ResetSpeedAdjustments();
musicController.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
musicController.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate); track.AddAdjustment(AdjustableProperty.Frequency, pauseFreqAdjust);
track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate);
foreach (var mod in mods.OfType<IApplicableToTrack>())
mod.ApplyToTrack(track);
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);
removeSourceClockAdjustments(); removeSourceClockAdjustments();
track = null;
} }
private void removeSourceClockAdjustments() private void removeSourceClockAdjustments()
{ {
if (speedAdjustmentsApplied) if (speedAdjustmentsApplied)
{ {
musicController.ResetTrackAdjustments(); track.ResetSpeedAdjustments();
speedAdjustmentsApplied = false; speedAdjustmentsApplied = false;
} }
} }

View File

@ -31,6 +31,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -560,9 +561,9 @@ namespace osu.Game.Screens.Select
BeatmapDetails.Refresh(); BeatmapDetails.Refresh();
if (music != null) if (music?.CurrentTrack != null)
{ {
music.Looping = true; music.CurrentTrack.Looping = true;
music.ResetTrackAdjustments(); music.ResetTrackAdjustments();
} }
@ -588,8 +589,8 @@ namespace osu.Game.Screens.Select
BeatmapOptions.Hide(); BeatmapOptions.Hide();
if (music != null) if (music?.CurrentTrack != null)
music.Looping = false; music.CurrentTrack.Looping = false;
this.ScaleTo(1.1f, 250, Easing.InSine); this.ScaleTo(1.1f, 250, Easing.InSine);
@ -610,8 +611,8 @@ namespace osu.Game.Screens.Select
FilterControl.Deactivate(); FilterControl.Deactivate();
if (music != null) if (music?.CurrentTrack != null)
music.Looping = false; music.CurrentTrack.Looping = false;
return false; return false;
} }
@ -652,18 +653,30 @@ namespace osu.Game.Screens.Select
BeatmapDetails.Beatmap = beatmap; BeatmapDetails.Beatmap = beatmap;
if (music != null) if (music?.CurrentTrack != null)
music.Looping = false; music.CurrentTrack.Looping = false;
} }
private readonly WeakReference<ITrack> lastTrack = new WeakReference<ITrack>(null);
/// <summary> /// <summary>
/// Ensures some music is playing for the current track. /// Ensures some music is playing for the current track.
/// Will resume playback from a manual user pause if the track has changed. /// Will resume playback from a manual user pause if the track has changed.
/// </summary> /// </summary>
private void ensurePlayingSelected() private void ensurePlayingSelected()
{ {
music.RestartPoint = Beatmap.Value.Metadata.PreviewTime; ITrack track = music?.CurrentTrack;
music.EnsurePlayingSomething(); if (track == null)
return;
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);
} }
private void carouselBeatmapsLoaded() private void carouselBeatmapsLoaded()

View File

@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual
private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> e) private void beatmapChanged(ValueChangedEvent<WorkingBeatmap> e)
{ {
Clock.ControlPointInfo = e.NewValue.Beatmap.ControlPointInfo; Clock.ControlPointInfo = e.NewValue.Beatmap.ControlPointInfo;
Clock.ChangeSource(MusicController.GetTrackClock() ?? new StopwatchClock()); Clock.ChangeSource((IAdjustableClock)MusicController.CurrentTrack ?? new StopwatchClock());
Clock.ProcessFrame(); Clock.ProcessFrame();
} }

View File

@ -1,6 +1,8 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Extensions.ObjectExtensions;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
/// <summary> /// <summary>
@ -13,7 +15,7 @@ namespace osu.Game.Tests.Visual
base.Update(); base.Update();
// note that this will override any mod rate application // note that this will override any mod rate application
MusicController.Tempo.Value = Clock.Rate; MusicController.CurrentTrack.AsNonNull().Tempo.Value = Clock.Rate;
} }
} }
} }