@@ -9,12 +9,16 @@ namespace osu.Framework.Timing
99 /// <summary>
1010 /// A clock which uses an internal stopwatch to interpolate (smooth out) a source.
1111 /// </summary>
12- public class InterpolatingFramedClock : IFrameBasedClock , ISourceChangeableClock // TODO: seal when DecoupleableInterpolatingFramedClock is gone.
12+ public sealed class InterpolatingFramedClock : IFrameBasedClock , ISourceChangeableClock
1313 {
1414 /// <summary>
1515 /// The amount of error that is allowed between the source and interpolated time before the interpolated time is ignored and the source time is used.
16+ /// Defaults to two 60 fps frames (~33.3 ms).
1617 /// </summary>
17- public virtual double AllowableErrorMilliseconds => 1000.0 / 60 * 2 * Rate ;
18+ /// <remarks>
19+ /// This is internally adjusted for the current playback rate (so that the actual precision is constant regardless of the rate applied).
20+ /// </remarks>
21+ public double AllowableErrorMilliseconds { get ; set ; } = 1000.0 / 60 * 2 ;
1822
1923 /// <summary>
2024 /// Whether interpolation was applied at the last processed frame.
@@ -28,19 +32,19 @@ public class InterpolatingFramedClock : IFrameBasedClock, ISourceChangeableClock
2832 /// <summary>
2933 /// The drift in milliseconds between the source and interpolation at the last processed frame.
3034 /// </summary>
31- public double Drift => CurrentTime - FramedSourceClock . CurrentTime ;
35+ public double Drift => CurrentTime - framedSourceClock . CurrentTime ;
3236
33- public virtual double Rate => FramedSourceClock . Rate ;
37+ public double Rate => framedSourceClock . Rate ;
3438
35- public virtual bool IsRunning { get ; private set ; }
39+ public bool IsRunning { get ; private set ; }
3640
37- public virtual double ElapsedFrameTime { get ; private set ; }
41+ public double ElapsedFrameTime { get ; private set ; }
3842
3943 public IClock Source { get ; private set ; }
4044
41- protected IFrameBasedClock FramedSourceClock ;
45+ private IFrameBasedClock framedSourceClock ;
4246
43- public virtual double CurrentTime { get ; private set ; }
47+ public double CurrentTime { get ; private set ; }
4448
4549 private readonly FramedClock realtimeClock = new FramedClock ( new StopwatchClock ( true ) ) ;
4650
@@ -50,10 +54,10 @@ public InterpolatingFramedClock(IFrameBasedClock? source = null)
5054 {
5155 ChangeSource ( source ) ;
5256 Debug . Assert ( Source != null ) ;
53- Debug . Assert ( FramedSourceClock != null ) ;
57+ Debug . Assert ( framedSourceClock != null ) ;
5458 }
5559
56- public virtual void ChangeSource ( IClock ? source )
60+ public void ChangeSource ( IClock ? source )
5761 {
5862 if ( source != null && source == Source )
5963 return ;
@@ -62,22 +66,22 @@ public virtual void ChangeSource(IClock? source)
6266
6367 // We need a frame-based source to correctly process interpolation.
6468 // If the provided source is not already a framed clock, encapsulate it in one.
65- FramedSourceClock = Source as IFrameBasedClock ?? new FramedClock ( source ) ;
69+ framedSourceClock = Source as IFrameBasedClock ?? new FramedClock ( source ) ;
6670
6771 IsInterpolating = false ;
68- currentTime = FramedSourceClock . CurrentTime ;
72+ currentTime = framedSourceClock . CurrentTime ;
6973 }
7074
71- public virtual void ProcessFrame ( )
75+ public void ProcessFrame ( )
7276 {
7377 double lastTime = currentTime ;
7478
7579 realtimeClock . ProcessFrame ( ) ;
76- FramedSourceClock . ProcessFrame ( ) ;
80+ framedSourceClock . ProcessFrame ( ) ;
7781
78- bool sourceIsRunning = FramedSourceClock . IsRunning ;
82+ bool sourceIsRunning = framedSourceClock . IsRunning ;
7983
80- bool sourceHasElapsed = FramedSourceClock . ElapsedFrameTime != 0 ;
84+ bool sourceHasElapsed = framedSourceClock . ElapsedFrameTime != 0 ;
8185
8286 try
8387 {
@@ -88,7 +92,7 @@ public virtual void ProcessFrame()
8892 if ( sourceHasElapsed )
8993 {
9094 IsInterpolating = false ;
91- currentTime = FramedSourceClock . CurrentTime ;
95+ currentTime = framedSourceClock . CurrentTime ;
9296 }
9397
9498 return ;
@@ -100,20 +104,20 @@ public virtual void ProcessFrame()
100104 currentTime += realtimeClock . ElapsedFrameTime * Rate ;
101105 // if we differ from the elapsed time of the source, let's adjust for the difference.
102106 // TODO: this is frame rate depending, and can result in unexpected results.
103- currentTime += ( FramedSourceClock . CurrentTime - currentTime ) / 8 ;
107+ currentTime += ( framedSourceClock . CurrentTime - currentTime ) / 8 ;
104108
105- bool withinAllowableError = Math . Abs ( FramedSourceClock . CurrentTime - currentTime ) <= AllowableErrorMilliseconds ;
109+ bool withinAllowableError = Math . Abs ( framedSourceClock . CurrentTime - currentTime ) <= AllowableErrorMilliseconds * Rate ;
106110
107111 if ( ! withinAllowableError )
108112 {
109113 // if we've exceeded the allowable error, we should use the source clock's time value.
110114 IsInterpolating = false ;
111- currentTime = FramedSourceClock . CurrentTime ;
115+ currentTime = framedSourceClock . CurrentTime ;
112116 }
113117 }
114118 else
115119 {
116- currentTime = FramedSourceClock . CurrentTime ;
120+ currentTime = framedSourceClock . CurrentTime ;
117121
118122 // Of importance, only start interpolating from the next frame.
119123 // The first frame after a clock starts may give very incorrect results, ie. due to a seek in the frame before.
@@ -122,7 +126,7 @@ public virtual void ProcessFrame()
122126 }
123127
124128 // seeking backwards should only be allowed if the source is explicitly doing that.
125- bool elapsedInOpposingDirection = FramedSourceClock . ElapsedFrameTime != 0 && Math . Sign ( FramedSourceClock . ElapsedFrameTime ) != Math . Sign ( Rate ) ;
129+ bool elapsedInOpposingDirection = framedSourceClock . ElapsedFrameTime != 0 && Math . Sign ( framedSourceClock . ElapsedFrameTime ) != Math . Sign ( Rate ) ;
126130 if ( ! elapsedInOpposingDirection )
127131 currentTime = Rate >= 0 ? Math . Max ( lastTime , currentTime ) : Math . Min ( lastTime , currentTime ) ;
128132 }
0 commit comments