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:
|
class TapTempo:
|
||||||
RESET_GAP_SECONDS = 2.5
|
RESET_GAP_SECONDS = 2.5
|
||||||
MAX_TAPS = 8
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._taps: list[float] = []
|
self._taps: list[float] = []
|
||||||
|
|
||||||
def tap(self, now: float) -> int | None:
|
def tap(self, now: float) -> int | None:
|
||||||
"""Record a tap at monotonic time `now`; returns the current bpm
|
"""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:
|
if self._taps and now - self._taps[-1] > self.RESET_GAP_SECONDS:
|
||||||
self._taps = []
|
self._taps = []
|
||||||
self._taps.append(now)
|
self._taps.append(now)
|
||||||
self._taps = self._taps[-self.MAX_TAPS:]
|
|
||||||
return self.bpm()
|
return self.bpm()
|
||||||
|
|
||||||
def bpm(self) -> int | None:
|
def bpm(self) -> int | None:
|
||||||
|
|||||||
@ -26,18 +26,27 @@ def test_long_gap_resets_series():
|
|||||||
assert tempo.tap(6.1) == 100 # 0.6s interval
|
assert tempo.tap(6.1) == 100 # 0.6s interval
|
||||||
|
|
||||||
|
|
||||||
def test_window_uses_recent_taps_only():
|
def test_long_session_converges_exactly():
|
||||||
tempo = TapTempo()
|
tempo = TapTempo()
|
||||||
# 4 slow taps (1s = 60bpm) then 8 fast taps (0.25s = 240bpm);
|
bpm = None
|
||||||
# only the last 8 taps count, so the slow ones age out entirely
|
for i in range(30):
|
||||||
t = 0.0
|
bpm = tempo.tap(i * 0.5) # 30 steady taps at 120 bpm
|
||||||
for _ in range(4):
|
assert bpm == 120
|
||||||
tempo.tap(t)
|
|
||||||
t += 1.0
|
|
||||||
for _ in range(8):
|
def test_whole_session_is_averaged():
|
||||||
tempo.tap(t)
|
tempo = TapTempo()
|
||||||
t += 0.25
|
# One sloppy 1.0s interval up front, then 20 taps at 0.5s. All 21 taps
|
||||||
assert tempo.bpm() == 240
|
# 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():
|
def test_reset():
|
||||||
|
|||||||
Reference in New Issue
Block a user