// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.IO.Stores; using osu.Framework.Logging; using osu.Game.Beatmaps; namespace osu.Game.Audio { public partial class PreviewTrackManager : Component { private readonly IAdjustableAudioComponent mainTrackAdjustments; private readonly BindableDouble muteBindable = new BindableDouble(); private ITrackStore trackStore = null!; protected TrackManagerPreviewTrack? CurrentTrack; public PreviewTrackManager(IAdjustableAudioComponent mainTrackAdjustments) { this.mainTrackAdjustments = mainTrackAdjustments; } [BackgroundDependencyLoader] private void load(AudioManager audioManager) { trackStore = audioManager.GetTrackStore(new OnlineStore()); } /// /// Retrieves a for a . /// /// The to retrieve the preview track for. /// The playable . public PreviewTrack Get(IBeatmapSetInfo beatmapSetInfo) { var track = CreatePreviewTrack(beatmapSetInfo, trackStore); track.Started += () => Schedule(() => { CurrentTrack?.Stop(); CurrentTrack = track; mainTrackAdjustments.AddAdjustment(AdjustableProperty.Volume, muteBindable); }); track.Stopped += () => Schedule(() => { if (CurrentTrack != track) return; CurrentTrack = null; mainTrackAdjustments.RemoveAdjustment(AdjustableProperty.Volume, muteBindable); }); return track; } /// /// Stops any currently playing . /// /// /// Only the immediate owner (an object that implements ) of the playing /// can globally stop the currently playing . The object holding a reference to the /// can always stop the themselves through . /// /// The which may be the owner of the . public void StopAnyPlaying(IPreviewTrackOwner source) { if (CurrentTrack == null || (CurrentTrack.Owner != null && CurrentTrack.Owner != source)) return; CurrentTrack.Stop(); // CurrentTrack should not be set to null here as it will result in incorrect handling in the track.Stopped callback above. } /// /// Creates the . /// protected virtual TrackManagerPreviewTrack CreatePreviewTrack(IBeatmapSetInfo beatmapSetInfo, ITrackStore trackStore) => new TrackManagerPreviewTrack(beatmapSetInfo, trackStore); public partial class TrackManagerPreviewTrack : PreviewTrack { [Resolved] public IPreviewTrackOwner? Owner { get; private set; } private readonly IBeatmapSetInfo beatmapSetInfo; private readonly ITrackStore trackManager; public TrackManagerPreviewTrack(IBeatmapSetInfo beatmapSetInfo, ITrackStore trackManager) { this.beatmapSetInfo = beatmapSetInfo; this.trackManager = trackManager; } protected override void LoadComplete() { base.LoadComplete(); if (Owner == null) Logger.Log($"A {nameof(PreviewTrack)} was created without a containing {nameof(IPreviewTrackOwner)}. An owner should be added for correct behaviour."); } protected override Track GetTrack() => trackManager.Get($"https://b.ppy.sh/preview/{beatmapSetInfo.OnlineID}.mp3"); } } }