Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom playlist music order was ignored. Queue music by filter and collection. #30343

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open
8 changes: 8 additions & 0 deletions osu.Game.Tests/Visual/Menus/TestSceneMusicActionHandling.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public void TestMusicNavigationActions()
});
});

AddAssert("empty playlist", () => Game.MusicController.Playlist.Count > 0);

AddStep("bind to track change", () =>
{
trackChangeQueue = new Queue<(IWorkingBeatmap, TrackChangeDirection)>();
Expand Down Expand Up @@ -98,6 +100,8 @@ public void TestShuffleBackwards()
beatmap.Length = 60_000;
}));

AddAssert("empty playlist", () => Game.MusicController.Playlist.Count > 0);

AddStep("bind to track change", () =>
{
trackChangeQueue = new Queue<(IWorkingBeatmap, TrackChangeDirection)>();
Expand Down Expand Up @@ -140,6 +144,8 @@ public void TestShuffleForwards()
beatmap.Length = 60_000;
}));

AddAssert("empty playlist", () => Game.MusicController.Playlist.Count > 0);

AddStep("bind to track change", () =>
{
trackChangeQueue = new Queue<(IWorkingBeatmap, TrackChangeDirection)>();
Expand Down Expand Up @@ -175,6 +181,8 @@ public void TestShuffleBackAndForth()
beatmap.Length = 60_000;
}));

AddAssert("empty playlist", () => Game.MusicController.Playlist.Count > 0);

AddStep("bind to track change", () =>
{
trackChangeQueue = new Queue<(IWorkingBeatmap, TrackChangeDirection)>();
Expand Down
24 changes: 24 additions & 0 deletions osu.Game/Overlays/Music/Playlist.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text.

using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
Expand All @@ -21,6 +23,9 @@ public partial class Playlist : OsuRearrangeableListContainer<Live<BeatmapSetInf

private FilterCriteria currentCriteria = new FilterCriteria();

[Resolved]
private MusicController musicController { get; set; } = null!;

public new MarginPadding Padding
{
get => base.Padding;
Expand All @@ -46,9 +51,28 @@ public void Filter(FilterCriteria criteria)

items.SearchTerm = criteria.SearchText;
currentCriteria = criteria;

if (currentCriteria == criteria)
updateMusicControllerPlaylist();

items.FilterCompleted += updateMusicControllerPlaylist;

void updateMusicControllerPlaylist() => Scheduler.AddOnce(() =>
{
musicController.PlaylistHookedByOverlay.Value = true;
musicController.Playlist.Clear();
musicController.Playlist.AddRange(AllVisibleSets);
});
}

public Live<BeatmapSetInfo>? FirstVisibleSet => Items.FirstOrDefault(i => ((PlaylistItem)ItemMap[i]).MatchingFilter);
public IEnumerable<Live<BeatmapSetInfo>> AllVisibleSets => Items.Where(i => ((PlaylistItem)ItemMap[i]).MatchingFilter);

protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
musicController.PlaylistHookedByOverlay.Value = false;
}

protected override OsuRearrangeableListItem<Live<BeatmapSetInfo>> CreateOsuDrawable(Live<BeatmapSetInfo> item) =>
new PlaylistItem(item)
Expand Down
56 changes: 48 additions & 8 deletions osu.Game/Overlays/MusicController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
using osu.Game.Configuration;
using osu.Game.Database;
using osu.Game.Rulesets.Mods;
using Realms;

namespace osu.Game.Overlays
{
Expand Down Expand Up @@ -60,11 +61,16 @@ public partial class MusicController : CompositeDrawable
[Resolved]
private IBindable<IReadOnlyList<Mod>> mods { get; set; } = null!;

public readonly BindableList<Live<BeatmapSetInfo>> Playlist = new BindableList<Live<BeatmapSetInfo>>();
public readonly BindableBool PlaylistHookedByOverlay = new BindableBool(false);
wezwery marked this conversation as resolved.
Show resolved Hide resolved

public DrawableTrack CurrentTrack { get; private set; } = new DrawableTrack(new TrackVirtual(1000));

[Resolved]
private RealmAccess realm { get; set; } = null!;

private IDisposable? beatmapSubscription;

private BindableNumber<double> sampleVolume = null!;

private readonly BindableDouble audioDuckVolume = new BindableDouble(1);
Expand All @@ -90,6 +96,16 @@ protected override void LoadComplete()
{
base.LoadComplete();

beatmapSubscription = realm.RegisterForNotifications(r => r.All<BeatmapSetInfo>().Where(s => !s.DeletePending && !s.Protected), beatmapsChanged);

PlaylistHookedByOverlay.ValueChanged += b =>
{
if (!b.NewValue)
{
Playlist.Clear();
Playlist.AddRange(getBeatmapSets());
}
};
beatmap.BindValueChanged(b =>
{
if (b.NewValue != null)
Expand All @@ -98,6 +114,25 @@ protected override void LoadComplete()
mods.BindValueChanged(_ => ResetTrackAdjustments(), true);
}

private void beatmapsChanged(IRealmCollection<BeatmapSetInfo> sender, ChangeSet? changes)
{
if (PlaylistHookedByOverlay.Value) return;

if (changes == null)
{
Playlist.Clear();
// must use AddRange to avoid RearrangeableList sort overhead per add op.
Playlist.AddRange(sender.Select(b => b.ToLive(realm)));
return;
}

foreach (int i in changes.InsertedIndices)
Playlist.Insert(i, sender[i].ToLive(realm));

foreach (int i in changes.DeletedIndices.OrderDescending())
Playlist.RemoveAt(i);
}

/// <summary>
/// Forcefully reload the current <see cref="WorkingBeatmap"/>'s track from disk.
/// </summary>
Expand Down Expand Up @@ -256,8 +291,8 @@ private PreviousTrackResult prev(bool allowProtectedTracks)
playableSet = getNextRandom(-1, allowProtectedTracks);
else
{
playableSet = getBeatmapSets().TakeWhile(i => !i.Value.Equals(current?.BeatmapSetInfo)).LastOrDefault(s => !s.Value.Protected || allowProtectedTracks)
?? getBeatmapSets().LastOrDefault(s => !s.Value.Protected || allowProtectedTracks);
playableSet = Playlist.TakeWhile(i => !i.Value.Equals(current?.BeatmapSetInfo)).LastOrDefault(s => !s.Value.Protected || allowProtectedTracks)
?? Playlist.LastOrDefault(s => !s.Value.Protected || allowProtectedTracks);
}

if (playableSet != null)
Expand All @@ -275,7 +310,6 @@ private PreviousTrackResult prev(bool allowProtectedTracks)
/// </summary>
/// <param name="onSuccess">Invoked when the operation has been performed successfully.</param>
/// <param name="allowProtectedTracks">Whether to include <see cref="BeatmapSetInfo.Protected"/> beatmap sets when navigating.</param>
/// <returns>A <see cref="ScheduledDelegate"/> of the operation.</returns>
public void NextTrack(Action? onSuccess = null, bool allowProtectedTracks = false) => Schedule(() =>
{
bool res = next(allowProtectedTracks);
Expand Down Expand Up @@ -352,10 +386,10 @@ private bool next(bool allowProtectedTracks)
playableSet = getNextRandom(1, allowProtectedTracks);
else
{
playableSet = getBeatmapSets().SkipWhile(i => !i.Value.Equals(current?.BeatmapSetInfo))
.Where(i => !i.Value.Protected || allowProtectedTracks)
.ElementAtOrDefault(1)
?? getBeatmapSets().FirstOrDefault(i => !i.Value.Protected || allowProtectedTracks);
playableSet = Playlist.SkipWhile(i => !i.Value.Equals(current?.BeatmapSetInfo))
.Where(i => !i.Value.Protected || allowProtectedTracks)
.ElementAtOrDefault(1)
?? Playlist.FirstOrDefault(i => !i.Value.Protected || allowProtectedTracks);
}

var playableBeatmap = playableSet?.Value.Beatmaps.FirstOrDefault();
Expand All @@ -376,7 +410,7 @@ private bool next(bool allowProtectedTracks)
{
Live<BeatmapSetInfo> result;

var possibleSets = getBeatmapSets().Where(s => !s.Value.Protected || allowProtectedTracks).ToList();
var possibleSets = Playlist.Where(s => !s.Value.Protected || allowProtectedTracks).ToList();

if (possibleSets.Count == 0)
return null;
Expand Down Expand Up @@ -599,6 +633,12 @@ public void ResetTrackAdjustments()
mod.ApplyToTrack(modTrackAdjustments);
}
}

protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
beatmapSubscription?.Dispose();
}
}

public class DuckParameters
Expand Down
Loading