From 554740af1010855598b3f2c40e9dee369438b11b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jul 2024 14:41:50 +0900 Subject: [PATCH] Adjust ducking API to use a parameters `record` --- .../Collections/ManageCollectionsDialog.cs | 13 ++- osu.Game/Overlays/DialogOverlay.cs | 13 ++- osu.Game/Overlays/MusicController.cs | 104 ++++++++++++------ 3 files changed, 86 insertions(+), 44 deletions(-) diff --git a/osu.Game/Collections/ManageCollectionsDialog.cs b/osu.Game/Collections/ManageCollectionsDialog.cs index 0396fd531c..11d50f3ce4 100644 --- a/osu.Game/Collections/ManageCollectionsDialog.cs +++ b/osu.Game/Collections/ManageCollectionsDialog.cs @@ -24,7 +24,7 @@ namespace osu.Game.Collections protected override string PopInSampleName => @"UI/overlay-big-pop-in"; protected override string PopOutSampleName => @"UI/overlay-big-pop-out"; - private IDisposable? audioDucker; + private IDisposable? duckOperation; [Resolved] private MusicController? musicController { get; set; } @@ -120,12 +120,17 @@ namespace osu.Game.Collections protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - audioDucker?.Dispose(); + duckOperation?.Dispose(); } protected override void PopIn() { - audioDucker = musicController?.Duck(100, 1f, unduckDuration: 100); + duckOperation = musicController?.Duck(new DuckParameters + { + DuckDuration = 100, + DuckVolumeTo = 1, + RestoreDuration = 100, + }); this.FadeIn(enter_duration, Easing.OutQuint); this.ScaleTo(0.9f).Then().ScaleTo(1f, enter_duration, Easing.OutQuint); @@ -135,7 +140,7 @@ namespace osu.Game.Collections { base.PopOut(); - audioDucker?.Dispose(); + duckOperation?.Dispose(); this.FadeOut(exit_duration, Easing.OutQuint); this.ScaleTo(0.9f, exit_duration); diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 7c52081053..97d77ae2d3 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays || dialogContainer.Children.Count > 0; [CanBeNull] - private IDisposable audioDucker; + private IDisposable duckOperation; public DialogOverlay() { @@ -53,7 +53,7 @@ namespace osu.Game.Overlays protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - audioDucker?.Dispose(); + duckOperation?.Dispose(); } public void Push(PopupDialog dialog) @@ -106,13 +106,18 @@ namespace osu.Game.Overlays protected override void PopIn() { - audioDucker = musicController.Duck(100, 1f, unduckDuration: 100); + duckOperation = musicController?.Duck(new DuckParameters + { + DuckDuration = 100, + DuckVolumeTo = 1, + RestoreDuration = 100, + }); } protected override void PopOut() { base.PopOut(); - audioDucker?.Dispose(); + duckOperation?.Dispose(); // PopOut gets called initially, but we only want to hide dialog when we have been loaded and are present. if (IsLoaded && CurrentDialog?.State.Value == Visibility.Visible) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 94078a3dec..cd770bef28 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -8,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; -using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; @@ -263,59 +262,53 @@ namespace osu.Game.Overlays /// /// Attenuates the volume and/or filters the currently playing track. /// - /// Duration of the ducking transition, in ms. - /// Level to drop volume to (1.0 = 100%). - /// Cutoff frequency to drop `AudioFilter` to. Use `null` to skip filter effect. - /// Easing for the ducking transition. - /// Duration of the unducking transition, in ms. - /// Easing for the unducking transition. - public IDisposable? Duck(int duration = 0, float duckVolumeTo = 0.25f, int? duckCutoffTo = 300, Easing easing = Easing.Out, int unduckDuration = 500, Easing unduckEasing = Easing.In) + public IDisposable? Duck(DuckParameters? parameters = null) { + parameters ??= new DuckParameters(); + if (audioDuckActive) return null; audioDuckActive = true; Schedule(() => { - if (duckCutoffTo.IsNotNull()) - audioDuckFilter?.CutoffTo((int)duckCutoffTo, duration, easing); + if (parameters.DuckCutoffTo != null) + audioDuckFilter?.CutoffTo(parameters.DuckCutoffTo.Value, parameters.DuckDuration, parameters.DuckEasing); - this.TransformBindableTo(audioDuckVolume, duckVolumeTo, duration, easing); + this.TransformBindableTo(audioDuckVolume, parameters.DuckVolumeTo, parameters.DuckDuration, parameters.DuckEasing); }); - return new InvokeOnDisposal(() => unduck(unduckDuration, unduckEasing)); + return new InvokeOnDisposal(restoreDucking); + + void restoreDucking() + { + if (!audioDuckActive) return; + + audioDuckActive = false; + + Schedule(() => + { + audioDuckFilter?.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, parameters.RestoreDuration, parameters.RestoreEasing); + this.TransformBindableTo(audioDuckVolume, 1, parameters.RestoreDuration, parameters.RestoreEasing); + }); + } } /// - /// A convenience method that ducks the currently playing track, then after a delay, unducks it. + /// A convenience method that ducks the currently playing track, then after a delay, restores automatically. /// - /// Delay after audio is ducked before unducking begins, in ms. - /// Duration of the unducking transition, in ms. - /// Easing for the unducking transition. - /// Level to drop volume to (1.0 = 100%). - /// Cutoff frequency to drop `AudioFilter` to. Use `null` to skip filter effect. - /// Duration of the ducking transition, in ms. - /// Easing for the ducking transition. - public void DuckMomentarily(int delay, int unduckDuration = 500, Easing unduckEasing = Easing.In, float duckVolumeTo = 0.25f, int? duckCutoffTo = 300, int duckDuration = 0, - Easing duckEasing = Easing.Out) + /// A delay in milliseconds which defines how long to delay restoration after ducking completes. + /// Parameters defining the ducking operation. + public void DuckMomentarily(double delayUntilRestore, DuckParameters? parameters = null) { - if (audioDuckActive) return; + parameters ??= new DuckParameters(); - Duck(duckDuration, duckVolumeTo, duckCutoffTo, duckEasing); - Scheduler.AddDelayed(() => unduck(unduckDuration, unduckEasing), delay); - } + IDisposable? duckOperation = Duck(parameters); - private void unduck(int duration, Easing easing) - { - if (!audioDuckActive) return; + if (duckOperation == null) + return; - audioDuckActive = false; - - Schedule(() => - { - audioDuckFilter?.CutoffTo(AudioFilter.MAX_LOWPASS_CUTOFF, duration, easing); - this.TransformBindableTo(audioDuckVolume, 1, duration, easing); - }); + Scheduler.AddDelayed(() => duckOperation.Dispose(), delayUntilRestore); } private bool next() @@ -491,6 +484,45 @@ namespace osu.Game.Overlays } } + public record DuckParameters + { + /// + /// The duration of the ducking transition in milliseconds. + /// Defaults to no duration (immediate ducking). + /// + public double DuckDuration = 0; + + /// + /// The final volume which should be reached during ducking, when 0 is silent and 1 is original volume. + /// Defaults to 25%. + /// + public float DuckVolumeTo = 0.25f; + + /// + /// The low-pass cutoff frequency which should be reached during ducking. Use `null` to skip filter effect. + /// Defaults to 300 Hz. + /// + public int? DuckCutoffTo = 300; + + /// + /// The easing curve to be applied during ducking. + /// Defaults to . + /// + public Easing DuckEasing = Easing.Out; + + /// + /// The duration of the restoration transition in milliseconds. + /// Defaults to 500 ms. + /// + public double RestoreDuration = 500; + + /// + /// The easing curve to be applied during restoration. + /// Defaults to . + /// + public Easing RestoreEasing = Easing.In; + } + public enum TrackChangeDirection { None,