Skip to content

Add MonotonicTimer for VM-resilient timing#8

Merged
chrisuthe merged 1 commit intomasterfrom
Monotonic
Feb 3, 2026
Merged

Add MonotonicTimer for VM-resilient timing#8
chrisuthe merged 1 commit intomasterfrom
Monotonic

Conversation

@chrisuthe
Copy link
Copy Markdown
Owner

Summary

  • Adds MonotonicTimer wrapper that filters erratic timer behavior in VMs
  • Enabled by default in AudioPipeline - all SDK consumers automatically benefit
  • No code changes required for existing SDK consumers

Problem

In VMs, the wall clock timer (Stopwatch.GetTimestamp() / QueryPerformanceCounter) can jump erratically due to hypervisor scheduling quirks. This causes:

  • Fake sync errors (timer says we're behind when we're not)
  • Unnecessary audio corrections (dropping audio that was playing fine)
  • Audio glitches from "fixing" problems that don't exist

The audio device continues playing normally - it's only the timer measurement that goes wonky.

Solution

MonotonicTimer wraps any IHighPrecisionTimer and:

  1. Never returns decreasing values (handles backward jumps)
  2. Clamps forward jumps to 50ms max (handles timer spikes)
  3. Passes through normal increments unchanged

Real DAC drift accumulates slowly (~50ppm = 3ms/minute), so clamping per-callback deltas to 50ms doesn't hide actual sync issues.

Files Changed

File Change
src/Sendspin.SDK/Synchronization/MonotonicTimer.cs NEW - Monotonic timer wrapper
src/Sendspin.SDK/Audio/AudioPipeline.cs Added useMonotonicTimer param (default: true)

Test plan

  • Build succeeds
  • Run in VM (Hyper-V/VMware), verify no erratic sync error spikes
  • Verify bare metal playback unchanged
  • Check logs show "Timer jumped" messages when filtering occurs

In VMs, wall clock timers (Stopwatch/QPC) can jump erratically due to
hypervisor scheduling, causing fake sync errors and unnecessary audio
corrections. This adds a MonotonicTimer wrapper that:

- Enforces monotonicity (never returns decreasing values)
- Clamps forward jumps to 50ms max per callback
- Filters timer noise while preserving real DAC drift detection

The wrapper is enabled by default in AudioPipeline, making all SDK
consumers automatically VM-resilient without code changes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@chrisuthe chrisuthe merged commit c6db7d0 into master Feb 3, 2026
2 checks passed
@chrisuthe chrisuthe deleted the Monotonic branch February 3, 2026 03:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant