From 09c1ac1321c3c41504717165ac3d6b5e9ea6a956 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 3 Apr 2026 14:22:35 +0200 Subject: [PATCH] =?UTF-8?q?docs(02-03):=20complete=20AudioPlayer=20plan=20?= =?UTF-8?q?=E2=80=94=20AUD-01=20and=20AUD-02=20fulfilled?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- .planning/REQUIREMENTS.md | 8 +- .planning/ROADMAP.md | 2 +- .planning/STATE.md | 15 ++- .../phases/02-app-core-audio/02-03-SUMMARY.md | 127 ++++++++++++++++++ 4 files changed, 141 insertions(+), 11 deletions(-) create mode 100644 .planning/phases/02-app-core-audio/02-03-SUMMARY.md diff --git a/.planning/REQUIREMENTS.md b/.planning/REQUIREMENTS.md index 15fcaab..6df35da 100644 --- a/.planning/REQUIREMENTS.md +++ b/.planning/REQUIREMENTS.md @@ -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 | diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index f12a948..a4e0e5a 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -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 diff --git a/.planning/STATE.md b/.planning/STATE.md index 73095a6..b009b9e 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -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 diff --git a/.planning/phases/02-app-core-audio/02-03-SUMMARY.md b/.planning/phases/02-app-core-audio/02-03-SUMMARY.md new file mode 100644 index 0000000..31fe76a --- /dev/null +++ b/.planning/phases/02-app-core-audio/02-03-SUMMARY.md @@ -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*