Skip to content

Fix 32-bit overflow in AudioStreamInfo::frames_to_microseconds#58

Merged
kahrendt merged 4 commits into
mainfrom
fix-frames-to-microseconds-overflow
May 21, 2026
Merged

Fix 32-bit overflow in AudioStreamInfo::frames_to_microseconds#58
kahrendt merged 4 commits into
mainfrom
fix-frames-to-microseconds-overflow

Conversation

@kahrendt
Copy link
Copy Markdown
Contributor

@kahrendt kahrendt commented May 21, 2026

Summary

frames_to_microseconds computed frames * US_PER_SECOND as a uint32 * uint32 product, which overflows above ~4295 frames (~97 ms at 44.1 kHz). Most callers dodged this by pre-reducing the frame count, but the hard-sync drop path in SyncTask passes a frame count bounded only by the decode buffer size, so a large drop could silently corrupt decoded_timestamp precisely during resync.

Changes

  • Widen the intermediate multiply to 64-bit and return int64_t, making the conversion exact for any frame count.
  • This removes the need for the two-step remainder decomposition that callers used purely to dodge the overflow:
    • Delete frames_to_milliseconds_with_remainder (and its gcd helper and the ms_sample_rate_gcd_ member).
    • Collapse the two call sites in SyncTask (track_sent_audio and the unplayed-frames calc in process_playback_progress) to a single frames_to_microseconds() call with identical numeric results: the decomposition was a pure overflow-dodge, not a precision trick.
    • The hard-sync drop site is now correct for large drops with no further change.

Performance note (32-bit targets)

The only added cost is a 64-bit/32-bit software divide (__udivdi3) in place of the previous all-32-bit arithmetic. This runs at audio-chunk granularity (~tens of Hz), not per-sample, so the absolute cost is negligible (well under 0.01% CPU). The genuinely hot hand-optimized path (the client/time formatter) is unaffected.

Testing

Verified the library and both host examples build cleanly. Behavioral test coverage, a large-frame case pinning this fix, will be a follow-up unit-test-infrastructure PR.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Fixes an overflow bug in AudioStreamInfo::frames_to_microseconds by widening the intermediate arithmetic, ensuring correct timestamp/playtime math when handling larger frame counts (notably in SyncTask’s hard-sync drop/resync paths).

Changes:

  • Change frames_to_microseconds to use a 64-bit intermediate multiply and return int64_t.
  • Remove the milliseconds + remainder decomposition helper and simplify SyncTask playtime computations to a single frames_to_microseconds() call.
  • Remove now-unused gcd helper and related state.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/sync_task.cpp Simplifies playtime accumulation to rely on the widened frames_to_microseconds() conversion.
src/audio_stream_info.h Updates API signature/docs for frames_to_microseconds() and removes the milliseconds-with-remainder helper + state.
src/audio_stream_info.cpp Implements the 64-bit-safe conversion and removes the old gcd-based decomposition logic.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/audio_stream_info.h Outdated
Comment thread src/audio_stream_info.cpp Outdated
kahrendt added 4 commits May 21, 2026 12:46
frames * US_PER_SECOND was computed as a uint32 product, which overflows
above ~4295 frames (~97 ms at 44.1 kHz). The hard-sync drop path in
SyncTask passes a frame count bounded only by the decode buffer size, so
a large drop could silently corrupt decoded_timestamp precisely during
resync.

Widen the multiply to 64-bit and return int64_t, making the conversion
exact for any frame count. This removes the need for the two-step
remainder decomposition that callers used purely to dodge the overflow:
frames_to_milliseconds_with_remainder (along with its gcd helper and the
ms_sample_rate_gcd_ member) is deleted, and the two call sites in SyncTask
collapse to a single frames_to_microseconds() call with identical results.
@kahrendt kahrendt force-pushed the fix-frames-to-microseconds-overflow branch from 5f1ac4e to e769ffa Compare May 21, 2026 16:46
@kahrendt kahrendt enabled auto-merge (squash) May 21, 2026 16:49
@kahrendt kahrendt merged commit b0d524f into main May 21, 2026
4 checks passed
@kahrendt kahrendt deleted the fix-frames-to-microseconds-overflow branch May 21, 2026 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants