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:
@@ -15,8 +15,8 @@
|
|||||||
|
|
||||||
### Audio
|
### Audio
|
||||||
|
|
||||||
- [ ] **AUD-01**: User kann lokale Songdateien laden und abspielen (MP3, FLAC, WAV)
|
- [x] **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-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-03**: System-Audio wird ueber PipeWire/PulseAudio fuer Beat-Detection erfasst
|
||||||
- [ ] **AUD-04**: Beat/Onset-Detection erkennt Schlaege aus dem Audio-Stream in Echtzeit
|
- [ ] **AUD-04**: Beat/Onset-Detection erkennt Schlaege aus dem Audio-Stream in Echtzeit
|
||||||
|
|
||||||
@@ -82,8 +82,8 @@
|
|||||||
| FW-03 | Phase 1 | Complete |
|
| FW-03 | Phase 1 | Complete |
|
||||||
| FW-04 | Phase 1 | Complete |
|
| FW-04 | Phase 1 | Complete |
|
||||||
| FW-05 | Phase 1 | Complete |
|
| FW-05 | Phase 1 | Complete |
|
||||||
| AUD-01 | Phase 2 | Pending |
|
| AUD-01 | Phase 2 | Complete |
|
||||||
| AUD-02 | Phase 2 | Pending |
|
| AUD-02 | Phase 2 | Complete |
|
||||||
| AUD-03 | Phase 2 | Pending |
|
| AUD-03 | Phase 2 | Pending |
|
||||||
| AUD-04 | Phase 2 | Pending |
|
| AUD-04 | Phase 2 | Pending |
|
||||||
| CHR-04 | Phase 2 | Pending |
|
| CHR-04 | Phase 2 | Pending |
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ Plans:
|
|||||||
Plans:
|
Plans:
|
||||||
- [ ] 02-01-PLAN.md — Project scaffold (pyproject.toml, uv deps) + Pydantic choreography models
|
- [ ] 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)
|
- [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-04-PLAN.md — Choreography scheduler (absolute monotonic clock, event dispatch)
|
||||||
- [ ] 02-05-PLAN.md — Beat detector (sounddevice + aubio, asyncio bridge)
|
- [ ] 02-05-PLAN.md — Beat detector (sounddevice + aubio, asyncio bridge)
|
||||||
- [ ] 02-06-PLAN.md — Main entry point + asyncio REPL + hardware acceptance checkpoint
|
- [ ] 02-06-PLAN.md — Main entry point + asyncio REPL + hardware acceptance checkpoint
|
||||||
|
|||||||
@@ -3,14 +3,14 @@ gsd_state_version: 1.0
|
|||||||
milestone: v1.0
|
milestone: v1.0
|
||||||
milestone_name: milestone
|
milestone_name: milestone
|
||||||
status: executing
|
status: executing
|
||||||
stopped_at: Completed 02-app-core-audio-02-PLAN.md
|
stopped_at: Completed 02-app-core-audio/02-03-PLAN.md
|
||||||
last_updated: "2026-04-03T12:21:05.248Z"
|
last_updated: "2026-04-03T12:22:10.260Z"
|
||||||
last_activity: 2026-04-03
|
last_activity: 2026-04-03
|
||||||
progress:
|
progress:
|
||||||
total_phases: 4
|
total_phases: 4
|
||||||
completed_phases: 1
|
completed_phases: 1
|
||||||
total_plans: 10
|
total_plans: 10
|
||||||
completed_plans: 5
|
completed_plans: 6
|
||||||
percent: 0
|
percent: 0
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ See: .planning/PROJECT.md (updated 2026-04-03)
|
|||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Phase: 02 (app-core-audio) — EXECUTING
|
Phase: 02 (app-core-audio) — EXECUTING
|
||||||
Plan: 2 of 6
|
Plan: 3 of 6
|
||||||
Status: Ready to execute
|
Status: Ready to execute
|
||||||
Last activity: 2026-04-03
|
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 | 1 tasks | 2 files |
|
||||||
| Phase 01-esp32-firmware P04 | 5 | 2 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 P02 | 4min | 1 tasks | 4 files |
|
||||||
|
| Phase 02-app-core-audio P03 | 4 | 1 tasks | 3 files |
|
||||||
|
|
||||||
## Accumulated Context
|
## 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]: 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 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]: 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
|
### Pending Todos
|
||||||
|
|
||||||
@@ -98,6 +101,6 @@ None yet.
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-04-03T12:21:05.236Z
|
Last session: 2026-04-03T12:22:10.243Z
|
||||||
Stopped at: Completed 02-app-core-audio-02-PLAN.md
|
Stopped at: Completed 02-app-core-audio/02-03-PLAN.md
|
||||||
Resume file: None
|
Resume file: None
|
||||||
|
|||||||
127
.planning/phases/02-app-core-audio/02-03-SUMMARY.md
Normal file
127
.planning/phases/02-app-core-audio/02-03-SUMMARY.md
Normal 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*
|
||||||
Reference in New Issue
Block a user