docs(02-03): complete AudioPlayer plan — AUD-01 and AUD-02 fulfilled

- Add 02-03-SUMMARY.md documenting AudioPlayer implementation
- Update STATE.md: advance to plan 3 of 6, 60% progress
- Update ROADMAP.md: phase 02 in progress (2/6 summaries)
- Mark AUD-01 and AUD-02 requirements complete in REQUIREMENTS.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Claude
2026-04-03 14:22:35 +02:00
parent 5a2ef4d5f0
commit 09c1ac1321
4 changed files with 141 additions and 11 deletions

View File

@@ -15,8 +15,8 @@
### Audio
- [ ] **AUD-01**: User kann lokale Songdateien laden und abspielen (MP3, FLAC, WAV)
- [ ] **AUD-02**: User kann Songs pausieren, fortsetzen und an beliebige Stelle springen (Seek)
- [x] **AUD-01**: User kann lokale Songdateien laden und abspielen (MP3, FLAC, WAV)
- [x] **AUD-02**: User kann Songs pausieren, fortsetzen und an beliebige Stelle springen (Seek)
- [ ] **AUD-03**: System-Audio wird ueber PipeWire/PulseAudio fuer Beat-Detection erfasst
- [ ] **AUD-04**: Beat/Onset-Detection erkennt Schlaege aus dem Audio-Stream in Echtzeit
@@ -82,8 +82,8 @@
| FW-03 | Phase 1 | Complete |
| FW-04 | Phase 1 | Complete |
| FW-05 | Phase 1 | Complete |
| AUD-01 | Phase 2 | Pending |
| AUD-02 | Phase 2 | Pending |
| AUD-01 | Phase 2 | Complete |
| AUD-02 | Phase 2 | Complete |
| AUD-03 | Phase 2 | Pending |
| AUD-04 | Phase 2 | Pending |
| CHR-04 | Phase 2 | Pending |

View File

@@ -52,7 +52,7 @@ Plans:
Plans:
- [ ] 02-01-PLAN.md — Project scaffold (pyproject.toml, uv deps) + Pydantic choreography models
- [x] 02-02-PLAN.md — UDP transport client (asyncio DatagramProtocol to ESP32)
- [ ] 02-03-PLAN.md — Audio player (miniaudio playback, position tracking, pause/seek)
- [x] 02-03-PLAN.md — Audio player (miniaudio playback, position tracking, pause/seek)
- [ ] 02-04-PLAN.md — Choreography scheduler (absolute monotonic clock, event dispatch)
- [ ] 02-05-PLAN.md — Beat detector (sounddevice + aubio, asyncio bridge)
- [ ] 02-06-PLAN.md — Main entry point + asyncio REPL + hardware acceptance checkpoint

View File

@@ -3,14 +3,14 @@ gsd_state_version: 1.0
milestone: v1.0
milestone_name: milestone
status: executing
stopped_at: Completed 02-app-core-audio-02-PLAN.md
last_updated: "2026-04-03T12:21:05.248Z"
stopped_at: Completed 02-app-core-audio/02-03-PLAN.md
last_updated: "2026-04-03T12:22:10.260Z"
last_activity: 2026-04-03
progress:
total_phases: 4
completed_phases: 1
total_plans: 10
completed_plans: 5
completed_plans: 6
percent: 0
---
@@ -26,7 +26,7 @@ See: .planning/PROJECT.md (updated 2026-04-03)
## Current Position
Phase: 02 (app-core-audio) — EXECUTING
Plan: 2 of 6
Plan: 3 of 6
Status: Ready to execute
Last activity: 2026-04-03
@@ -59,6 +59,7 @@ Progress: [░░░░░░░░░░] 0%
| Phase 01-esp32-firmware P04 | 5 | 1 tasks | 2 files |
| Phase 01-esp32-firmware P04 | 5 | 2 tasks | 2 files |
| Phase 02-app-core-audio P02 | 4min | 1 tasks | 4 files |
| Phase 02-app-core-audio P03 | 4 | 1 tasks | 3 files |
## Accumulated Context
@@ -84,6 +85,8 @@ Recent decisions affecting current work:
- [Phase 01-esp32-firmware]: WIFI_PS_NONE re-applied on WiFi reconnect to prevent RMT flicker after disconnect/reconnect cycle
- [Phase 01-esp32-firmware]: Hardware acceptance test approved — all 5 FW requirements (FW-01 through FW-05) validated on physical ESP32-C3 SuperMini with both LED strips
- [Phase 02-app-core-audio]: asyncio DatagramProtocol with fire-and-forget sendto() for ESP32 UDP transport (D-10); v:1 injected by transport layer; connected state via STATUS ok only (D-12)
- [Phase 02-app-core-audio]: D-04 applied: AudioPlayer uses miniaudio in its own OS thread, no asyncio dependencies in player.py
- [Phase 02-app-core-audio]: D-09 applied: pause() saves _paused_position, resume() calls _start_device(seek_seconds=saved) for accurate seek-based resume
### Pending Todos
@@ -98,6 +101,6 @@ None yet.
## Session Continuity
Last session: 2026-04-03T12:21:05.236Z
Stopped at: Completed 02-app-core-audio-02-PLAN.md
Last session: 2026-04-03T12:22:10.243Z
Stopped at: Completed 02-app-core-audio/02-03-PLAN.md
Resume file: None

View File

@@ -0,0 +1,127 @@
---
phase: 02-app-core-audio
plan: "03"
subsystem: audio
tags: [miniaudio, audio-playback, threading, position-tracking, raspberry-pi]
requires:
- phase: 02-app-core-audio/02-01
provides: pyproject.toml with miniaudio dep, led_sync package scaffold
- phase: 02-app-core-audio/02-02
provides: project structure, uv lockfile
provides:
- AudioPlayer class with play/pause/resume/seek/stop (AUD-01, AUD-02)
- Thread-safe position_seconds property via _frames_played counter + Lock
- Pause/resume via stop+seek pattern (D-09 compliant)
- src/led_sync/audio/__init__.py re-exporting AudioPlayer
affects: [02-04, 02-05, 02-06, 03-tui]
tech-stack:
added: [miniaudio 1.61]
patterns:
- "miniaudio generator pattern: stream_file() generator + PlaybackDevice.start()"
- "Thread-safe position: _frames_played atomic counter updated inside generator, read under Lock"
- "Pause/resume: device.stop() saves position, new device.start() seeks to saved position (D-09)"
key-files:
created:
- src/led_sync/audio/player.py
- src/led_sync/audio/__init__.py
- tests/test_player_smoke.py
modified: []
key-decisions:
- "D-04 applied: miniaudio runs in its own OS thread, no asyncio inside AudioPlayer"
- "D-09 applied: pause saves _paused_position, resume calls _start_device(seek_seconds=saved)"
- "position_seconds returns _paused_position when paused (frozen), _frames_played/SAMPLE_RATE when playing"
- "seek() when paused: start device then immediately stop+record new position"
patterns-established:
- "Pattern: miniaudio PlaybackDevice wrapper with generator-based frame counting"
- "Pattern: _paused_position sentinel (None=playing/stopped, float=paused) for state machine"
requirements-completed: [AUD-01, AUD-02]
duration: 4min
completed: 2026-04-03
---
# Phase 02 Plan 03: AudioPlayer — miniaudio with Position Tracking Summary
**miniaudio-based AudioPlayer with sample-accurate position tracking via frame-counting generator, pause/seek via stop+restart pattern, and thread-safe position property**
## Performance
- **Duration:** 4 min
- **Started:** 2026-04-03T12:16:20Z
- **Completed:** 2026-04-03T12:20:37Z
- **Tasks:** 1 (TDD)
- **Files modified:** 3 created
## Accomplishments
- AudioPlayer class implements AUD-01 (MP3/FLAC/WAV playback via miniaudio) and AUD-02 (pause/resume/seek)
- Sample-accurate position tracking: `_frames_played` counter updated every audio chunk in generator, read under `threading.Lock`
- Pause/resume state machine: `_paused_position` sentinel (None=playing, float=paused), D-09 compliant
- All public methods safe from asyncio context via run_in_executor (no blocking waits)
- Double pause()/resume() no-ops — no exception raised
## Task Commits
Each task was committed atomically:
1. **Task 1: AudioPlayer — miniaudio playback with position tracking** - `378620e` (feat, included in 02-02 commit)
_Note: AudioPlayer files were created and committed as part of the 02-02 session commit. The implementation was fully present in the repository before 02-03 execution began._
## Files Created/Modified
- `src/led_sync/audio/player.py` — AudioPlayer class: play/pause/resume/seek/stop, _make_stream generator, _start_device/_stop_device device lifecycle
- `src/led_sync/audio/__init__.py` — Package marker, re-exports AudioPlayer
- `tests/test_player_smoke.py` — Manual smoke test for Pi hardware validation (not automated)
## Decisions Made
- D-04 applied: AudioPlayer has zero asyncio dependencies — runs in miniaudio's OS thread
- D-09 applied: pause() = device.stop() + record position; resume() = play(seek_seconds=saved)
- seek() when paused re-pauses at new position (start device → immediately stop → set _paused_position)
- BYTES_PER_FRAME = 4 (2 channels × 2 bytes for SIGNED16) used to convert chunk bytes to frame count
## Deviations from Plan
### Auto-fixed Issues
**1. [Rule 3 - Blocking] Files already existed from prior session**
- **Found during:** Task 1 startup
- **Issue:** Plans 02-01 and 02-02 had already been executed in a prior session; src/led_sync/audio/player.py was already committed at 378620e
- **Fix:** Verified existing implementation matches plan specification exactly; ran all verification commands; confirmed tests pass
- **Files modified:** None (already correct)
- **Verification:** State machine tests pass (AudioPlayer state machine tests passed)
- **Committed in:** 378620e (prior session)
---
**Total deviations:** 1 (discovery that implementation was pre-existing from prior session)
**Impact on plan:** No scope creep. Implementation fully matches plan specification.
## Issues Encountered
- sounddevice requires libportaudio2 system library which is not installed on this dev host (VPS). This is expected — sounddevice is only needed for beat detection (plan 02-05), not for AudioPlayer. On the actual Raspberry Pi target, libportaudio2 is installed via apt.
## Known Stubs
None — AudioPlayer is fully implemented. The smoke test (`tests/test_player_smoke.py`) is intentionally manual-only and requires a real audio file on Pi hardware.
## Next Phase Readiness
- AudioPlayer ready for use by REPL (plan 02-06) and scheduler (plan 02-04)
- `from led_sync.audio import AudioPlayer` importable and verified
- Position tracking thread-safe for asyncio via run_in_executor
- Scheduler's pause_offset (D-09) can compensate by reading player.position_seconds on pause
---
*Phase: 02-app-core-audio*
*Completed: 2026-04-03*