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:
@ -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:
|
||||
|
||||
@ -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():
|
||||
|
||||
Reference in New Issue
Block a user