diff --git a/lintunes/tap_tempo.py b/lintunes/tap_tempo.py index b0163fd..e70b5a2 100644 --- a/lintunes/tap_tempo.py +++ b/lintunes/tap_tempo.py @@ -3,18 +3,18 @@ class TapTempo: RESET_GAP_SECONDS = 2.5 - MAX_TAPS = 8 def __init__(self): self._taps: list[float] = [] def tap(self, now: float) -> int | None: """Record a tap at monotonic time `now`; returns the current bpm - estimate (None until there are two taps in the series).""" + estimate (None until there are two taps in the series). Every tap + since the series started counts — the estimate converges on the + overall tempo instead of chasing the most recent few taps.""" if self._taps and now - self._taps[-1] > self.RESET_GAP_SECONDS: self._taps = [] self._taps.append(now) - self._taps = self._taps[-self.MAX_TAPS:] return self.bpm() def bpm(self) -> int | None: diff --git a/tests/test_tap_tempo.py b/tests/test_tap_tempo.py index 805e0ba..6be4eb6 100644 --- a/tests/test_tap_tempo.py +++ b/tests/test_tap_tempo.py @@ -26,18 +26,27 @@ def test_long_gap_resets_series(): assert tempo.tap(6.1) == 100 # 0.6s interval -def test_window_uses_recent_taps_only(): +def test_long_session_converges_exactly(): tempo = TapTempo() - # 4 slow taps (1s = 60bpm) then 8 fast taps (0.25s = 240bpm); - # only the last 8 taps count, so the slow ones age out entirely - t = 0.0 - for _ in range(4): - tempo.tap(t) - t += 1.0 - for _ in range(8): - tempo.tap(t) - t += 0.25 - assert tempo.bpm() == 240 + bpm = None + for i in range(30): + bpm = tempo.tap(i * 0.5) # 30 steady taps at 120 bpm + assert bpm == 120 + + +def test_whole_session_is_averaged(): + tempo = TapTempo() + # One sloppy 1.0s interval up front, then 20 taps at 0.5s. All 21 taps + # count (no rolling window), so the estimate is the overall average + # 10.5s / 20 intervals = 0.525s -> 114 bpm, not the recent-taps 120. + tempo.tap(0.0) + t = 1.0 + bpm = None + for _ in range(20): + bpm = tempo.tap(t) + t += 0.5 + assert tempo.tap_count == 21 + assert bpm == 114 def test_reset():