// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; using osu.Framework.Audio; using osu.Framework.Bindables; namespace osu.Game.Rulesets.Mods { /// /// Provides common functionality shared across various rate adjust mods. /// public class RateAdjustModHelper : IApplicableToTrack { public readonly IBindableNumber SpeedChange; private IAdjustableAudioComponent? track; private BindableBool? adjustPitch; /// /// The score multiplier for the current . /// public double ScoreMultiplier { get { // Round to the nearest multiple of 0.1. double value = (int)(SpeedChange.Value * 10) / 10.0; // Offset back to 0. value -= 1; if (SpeedChange.Value >= 1) return 1 + value / 5; else return 0.6 + value; } } /// /// Construct a new . /// /// The main speed adjust parameter which is exposed to the user. public RateAdjustModHelper(IBindableNumber speedChange) { SpeedChange = speedChange; } /// /// Setup audio track adjustments for a rate adjust mod. /// Importantly, must be called when a track is obtained/changed for this to work. /// /// The "adjust pitch" setting as exposed to the user. public void HandleAudioAdjustments(BindableBool adjustPitch) { this.adjustPitch = adjustPitch; // When switching between pitch adjust, we need to update adjustments to time-shift or frequency-scale. adjustPitch.BindValueChanged(adjustPitchSetting => { track?.RemoveAdjustment(adjustmentForPitchSetting(adjustPitchSetting.OldValue), SpeedChange); track?.AddAdjustment(adjustmentForPitchSetting(adjustPitchSetting.NewValue), SpeedChange); AdjustableProperty adjustmentForPitchSetting(bool adjustPitchSettingValue) => adjustPitchSettingValue ? AdjustableProperty.Frequency : AdjustableProperty.Tempo; }); } /// /// Should be invoked when a track is obtained / changed. /// /// The new track. /// If this method is called before . public void ApplyToTrack(IAdjustableAudioComponent track) { if (adjustPitch == null) throw new InvalidOperationException($"Must call {nameof(HandleAudioAdjustments)} first"); this.track = track; adjustPitch.TriggerChange(); } } }