BPM tap button averages the whole session, not the last 8 taps

TapTempo kept a rolling window of 8 taps, so a long tapping session
chased the most recent taps instead of converging on the overall tempo
(trav noticed the estimate never settled). Every tap since the session
started now counts; a >2.5s gap still begins a fresh session.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-03 11:30:37 -04:00
parent 405250dc4e
commit 407c5669c5
2 changed files with 23 additions and 14 deletions

View File

@ -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:

View File

@ -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():