Repository navigation
Keep a segment's time and position math from overflowing in a run of months #94
Description
Problem
Within one segment of an anchor, offset_us() computes samples * 10^6 and time_position() computes (unshifted_us - u_j) * rate in int64. Both overflow (undefined behavior) once one segment has run about 2.4e7 s at 384 kHz (about 278 days), or about 6 years at 48 kHz. A live input that plays one format on one anchor that long then maps positions and cells to wrong times. #24 split the boundary time (Segment::elapsed_s and elapsed_ps), which holds at any length, but these two products remain.
Where
sendspin-server-cpp/src/sched/timeline.cpp
Lines 36 to 39 in 83b821d
int64_t offset_us(const Segment& segment, uint64_t position) { const int64_t samples = static_cast<int64_t>(position - segment.position); return floor_div(samples * US_PER_SECOND, segment.rate); } sendspin-server-cpp/src/sched/timeline.cpp
Lines 126 to 129 in 83b821d
// first segment extrapolates it const int64_t position = static_cast<int64_t>(segment.position) + ceil_div((unshifted_us - segment.unshifted_us) * int64_t{segment.rate}, US_PER_SECOND);
Evidence
Confirmed by arithmetic: int64 holds 9.22e18; at 384 kHz (MAX_INPUT_RATE) a segment reaches 9.22e12 samples, so samples * 10^6 overflows, after 2.4e7 s, and (unshifted_us - u_j) * 384000 overflows at the same elapsed time. Not reproduced; reaching it needs one anchor playing for months.
Direction
Owner's choice, because of the cost. unshifted_time() and cell_position() run per frame and per cell (group_audio.cpp, HostView::time_of(), admission), and 64-bit multiply and divide are slow on the ESP32-P4. Options:
- Split each product the way
add_segment()does (q = samples / rate,r = samples % rate), which adds 64-bit work to the per-frame paths. - Start a new segment (same rate and channels) when one grows past a bound, in
add_segment()'s own path, so each segment's products stay small and the per-frame math is unchanged.segment_output_index()is exact across segments, so the extra boundaries do not move any output index. - Document the bound and leave it.
Done when
- A pure-function table row in
tests/test_sched_timeline.cpppast the bound (for example one segment of 300 days at 384 kHz) maps positions and cells as an ordinary row does, and trips UBSan before the change; plus a Control row at ordinary durations. - No 64-bit multiply or divide added per sample, cell or frame, unless the owner chooses that option.
Activity
Keep a segment's boundary time from overflowing and resync a source a…
kahrendt commentedon Oct 16, 2026
Claimed 0c55a139; branch issue-94-segment-math-split
kahrendt commentedon Oct 16, 2026
PR: #96