Skip to content

spike(rust/lag): port find_best_lag to Rust (#97) - #104

Merged
natashaannn merged 3 commits into
mainfrom
spike/issue-97-lag
Jul 12, 2026
Merged

spike(rust/lag): port find_best_lag to Rust (#97)#104
natashaannn merged 3 commits into
mainfrom
spike/issue-97-lag

Conversation

@natashaannn

@natashaannn natashaannn commented Jul 12, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds PEAK_NEARNESS_THRESHOLD = 0.5 and SYNC_FRAME_RATE = 30.0 as module-level constants (must not change — baseline.json was computed with these values)
  • Implements find_best_lag(correlation: &[f64], sample_rate: u32) -> f64 replicating AudioSyncer.js L188–216, including the candidate-reset-on-new-maximum tie-break
  • Quantization uses (x + 0.5).floor() to replicate JS Math.round (Rust f64::round rounds away from zero; JS rounds toward +infinity at half-frames)
  • Strictly scoped to spike/audio-sync/src/lib.rs

Key behaviour preserved

The candidate list is cleared each time a new maximum is found, then rebuilt from scratch. Without this reset, candidates from an earlier (lower) maximum can persist and corrupt the tie-break — this mirrors the JS exactly.

Tests (8 inline #[cfg(test)], 13 total in suite)

Test Covers
find_best_lag_positive_lag peak at K ≤ N/2, quantizes to 0.0 (formula verified)
find_best_lag_positive_lag_nonzero peak at index 267 → 1/30 s (nonzero positive result)
find_best_lag_negative_lag peak at index 724 (= N-300) → -1/30 s
find_best_lag_negative_half_frame_regression -400 samples → -1.5 frames → -1/30 s, not -2/30 s
find_best_lag_tie_break_earliest_wins two equal peaks, index 50 wins over 200
find_best_lag_all_zeros_no_panic returns 0.0, no panic
find_best_lag_peak_at_n_over_2_is_positive N/2 boundary → positive lag
find_best_lag_one_frame_at_44100 1470 samples @ 44100 Hz → exactly 1/30 s

cargo test --manifest-path spike/audio-sync/Cargo.toml — 13/13 pass.

Note on negative-lag test index

The AC example used index 924 (= N-100 samples), but 100 samples at 8000 Hz quantizes to 0 frames. Index 724 (= N-300 samples → -1 frame) is used instead to produce a verifiable negative result after quantization.

Closes #97

🤖 Generated with Claude Code

Replicates findBestLag from AudioSyncer.js (L188-216). The candidate-reset
logic (clear candidates list on each new maximum) must match the JS exactly;
without it, near-equal peaks early in the array can survive as candidates
relative to a lower intermediate maximum, breaking the tie-break.

PEAK_NEARNESS_THRESHOLD (0.5) and SYNC_FRAME_RATE (30) are module-level
constants — the baseline.json fixture was computed with these values so
they must not be changed here.

The negative-lag test uses index 724 (= N-300) rather than 924 (= N-100)
because 100 samples at 8000 Hz rounds to 0 frames; 300 samples rounds to
-1 frame and survives quantization to produce a verifiable negative result.

AC: all acceptance criteria from #97
Rust f64::round() rounds away from zero (-1.5 → -2), but JavaScript
Math.round(-1.5) = -1 (rounds toward +infinity). The divergence is only
observable at exact half-frame boundaries, which AudioSyncer.js handles
with JS semantics. Fix: replace .round() with (x + 0.5).floor().

Adds regression test: lag_samples = -400 at 8000 Hz → -1.5 frames →
must return -1/30 s, not -2/30 s.

Also adds find_best_lag_positive_lag_nonzero at index 267 (→ 1/30 s) so
the positive-lag AC has a test that asserts a nonzero result rather than
relying solely on the index-100 case that quantizes to 0.0.
find_best_lag_negative_lag and find_best_lag_tie_break_earliest_wins were
computing their expected values with .round(), which agrees with the fix
for all non-half-integer inputs but diverges at half-frame boundaries.
Using the same formula as the implementation makes the tests
self-documenting and safe if either test input is later modified.
@natashaannn
natashaannn merged commit 467cfb5 into main Jul 12, 2026
1 check passed
@natashaannn
natashaannn deleted the spike/issue-97-lag branch July 12, 2026 12:33
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.

spike(rust/lag): port find_best_lag to Rust

1 participant