| 02-app-core-audio |
04 |
scheduling |
| asyncio |
| monotonic-clock |
| choreography |
| timing |
| scheduler |
|
| phase |
provides |
| 02-01 |
ChoreoEvent Pydantic model with timestamp/zone/animation/params fields |
|
| phase |
provides |
| 02-02 |
ESP32Transport.send_command() for fire-and-forget UDP dispatch |
|
|
| ChoreographyScheduler asyncio task — absolute monotonic clock event dispatch |
| pause/resume with pause_offset accumulation (D-09) |
| seek() skips events before seek position |
| on_event callback hook for TUI integration |
|
| 02-05 (beat detection integration) |
| 03-xx (TUI wraps scheduler for playback controls) |
| phase 4 (live reactive mode uses on_event hook) |
|
| added |
patterns |
|
|
| Absolute monotonic scheduling: fire_at = start_time + pause_offset + event.timestamp — never accumulate relative delays |
| 5ms poll interval (asyncio.sleep(min(remaining, 0.005))) keeps event loop responsive while maintaining <1ms drift |
| pause_offset accumulation for correct pause/resume without event skipping or double-firing |
|
|
| created |
modified |
| src/led_sync/scheduler.py |
| tests/test_scheduler.py |
|
|
|
| Absolute monotonic timestamps (D-07): fire_at recalculated fresh each 5ms poll iteration — mathematical guarantee of <1ms drift regardless of duration |
| pause_offset accumulation (D-09): resume() adds elapsed pause time to offset, keeping all future fire_at calculations correct |
| Scheduler does not inject v:1 — that is transport's responsibility, scheduler sends {zone, animation, params} only |
|
| ChoreographyScheduler._run(): asyncio.create_task() wrapping loop with 5ms poll interval |
| seek() implemented as play() restart with seek_seconds — simpler than maintaining event index pointer |
|
|
2min |
2026-04-03 |