// 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.Extensions.TypeExtensions; using System; using System.Diagnostics; namespace osu.Framework.Timing { public class StopwatchClock : Stopwatch, IAdjustableClock { private double seekOffset; /// /// Keep track of how much stopwatch time we have used at previous rates. /// private double rateChangeUsed; /// /// Keep track of the resultant time that was accumulated at previous rates. /// private double rateChangeAccumulated; public StopwatchClock(bool start = false) { if (start) Start(); } public double CurrentTime => stopwatchCurrentTime + seekOffset; /// /// The current time, represented solely by the accumulated time. /// private double stopwatchCurrentTime => (stopwatchMilliseconds - rateChangeUsed) * rate + rateChangeAccumulated; private double stopwatchMilliseconds => (double)ElapsedTicks / Frequency * 1000; private double rate = 1; public double Rate { get => rate; set { if (rate == value) return; rateChangeAccumulated += (stopwatchMilliseconds - rateChangeUsed) * rate; rateChangeUsed = stopwatchMilliseconds; rate = value; } } public new void Reset() { resetAccumulatedRate(); base.Reset(); } public new void Restart() { resetAccumulatedRate(); base.Restart(); } public void ResetSpeedAdjustments() => Rate = 1; public bool Seek(double position) { // Determine the offset that when added to stopwatchCurrentTime; results in the requested time value seekOffset = position - stopwatchCurrentTime; return true; } public override string ToString() => $@"{GetType().ReadableName()} ({Math.Truncate(CurrentTime)}ms)"; private void resetAccumulatedRate() { rateChangeAccumulated = 0; rateChangeUsed = 0; } } }