Files
led-sync-studio/.planning/phases/02-app-core-audio/02-05-SUMMARY.md
Claude d04990f6c5 docs(02-05): complete BeatDetector plan — AUD-03 and AUD-04 fulfilled
- SUMMARY.md: TDD execution documented, deviation noted (module-level time import)
- STATE.md: advanced to plan 3/6, 90% progress, decision logged
- ROADMAP.md: 5/6 summaries complete for phase 02
- REQUIREMENTS.md: AUD-03 and AUD-04 marked complete
2026-04-03 14:52:26 +02:00

4.3 KiB

phase, plan, subsystem, tags, dependency_graph, tech_stack, key_files, decisions, metrics
phase plan subsystem tags dependency_graph tech_stack key_files decisions metrics
02-app-core-audio 05 audio
beat-detection
sounddevice
aubio
asyncio-bridge
threading
requires provides affects
02-02 (UDP transport)
02-03 (AudioPlayer)
BeatDetector class (AUD-03, AUD-04)
02-06 (CLI integration — will wire BeatDetector to REPL)
Phase 5 (Live Reactive mode — reuses BeatDetector directly)
added patterns
sounddevice 0.5.x (system audio capture via PortAudio/PipeWire)
aubio 0.4.9 (real-time beat detection, C-backed)
call_soon_threadsafe bridge
audio thread -> asyncio event loop (D-05)
threading.Lock for shared state (beat_count, bpm, last_beat_time)
Double-trigger suppression via monotonic hold timer (200ms)
created modified
src/led_sync/audio/beat_detector.py
tests/test_beat_detector.py
src/led_sync/audio/__init__.py
D-04 enforced: sounddevice thread is fully independent from asyncio and miniaudio
D-05 enforced: call_soon_threadsafe used in _audio_callback — no direct asyncio calls
D-06 noted: device=None for PipeWire default; monitor source for loopback capture
import time in _audio_callback removed — time import moved to module level for cleanliness
duration completed tasks_completed files_created files_modified
~2 minutes 2026-04-03 1 2 1

Phase 2 Plan 5: BeatDetector — Real-Time Beat Detection Summary

One-liner: sounddevice InputStream + aubio.tempo with call_soon_threadsafe bridge into asyncio — AUD-03 and AUD-04 fulfilled.

What Was Built

BeatDetector class in src/led_sync/audio/beat_detector.py — a real-time beat detection module that:

  1. Opens a sounddevice.InputStream at 44100Hz with 512-sample blocks (~11.6ms latency per hop)
  2. Runs aubio.tempo in the OS audio thread callback to detect beats from the audio stream
  3. Bridges beat events into the asyncio event loop via loop.call_soon_threadsafe(loop.create_task, on_beat(beat_time))
  4. Uses threading.Lock for thread-safe beat_count and bpm properties
  5. Implements 200ms double-trigger suppression to avoid false positives on strong transients

Public API

detector = BeatDetector(loop=asyncio.get_running_loop(), on_beat=my_async_fn)
detector.start(device=None)   # None = PipeWire default; pass monitor source for loopback
detector.stop()               # idempotent, no-op if already stopped
detector.bpm: float           # current aubio BPM estimate
detector.beat_count: int      # total beats since start

Key Design Decisions

  • D-04 boundary: _audio_callback is a pure OS thread function — zero asyncio dependencies
  • D-05 bridge: call_soon_threadsafe(loop.create_task, on_beat(beat_time)) is the only crossing point
  • D-06 PipeWire: device=None selects the PipeWire default input; for system audio loopback, pass the monitor source name (documented in module docstring)
  • Constants SAMPLE_RATE=44100, HOP_SIZE=512, BUF_SIZE=1024 lock in ~11.6ms latency

TDD Execution

  • RED commit: 3ad5e91 — 14 failing tests covering import, constants, thread safety, asyncio bridge pattern, and ImportError handling
  • GREEN commit: f1af7ba — Implementation passes all 14 tests + 57 total test suite passes

Deviations from Plan

1. [Rule 1 - Bug] Removed import time as _time inline import inside callback

  • Found during: implementation review
  • Issue: Plan code sample had import time as _time inside _audio_callback — importing inside a hot audio callback runs at ~11ms intervals and is poor practice
  • Fix: Moved import time to module-level imports
  • Files modified: src/led_sync/audio/beat_detector.py
  • Commit: f1af7ba (included in implementation)

Otherwise, plan executed exactly as specified.

Verification Results

BeatDetector class OK: latency=11.6ms, all properties accessible
57 passed in 2.80s

Known Stubs

None — BeatDetector is fully implemented. start() requires a real audio device to test beat detection live; this is documented in the module as expected behavior (Pi hardware required).

Self-Check: PASSED

Item Status
src/led_sync/audio/beat_detector.py FOUND
tests/test_beat_detector.py FOUND
Commit 3ad5e91 (TDD RED) FOUND
Commit f1af7ba (TDD GREEN) FOUND