Files
led2/.planning/phases/02-audio-engine/02-01-SUMMARY.md
Claude 3173fa2228 docs(02-01): complete audio engine plan 01 — MPVEngine, SUMMARY, STATE, ROADMAP updated
- Create 02-01-SUMMARY.md with task commits, decisions, self-check
- Advance plan counter to plan 2 of 3
- Update progress to 71% (5/7 plans complete)
- Add 3 key decisions to STATE.md
- Mark AUD-01, AUD-03, INF-03 complete in REQUIREMENTS.md
- Update ROADMAP.md phase 2 progress
2026-04-06 13:05:02 +00:00

6.1 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
02-audio-engine 01 audio
mpv
python-mpv
libmpv
soundfile
numpy
ffmpeg
audio-engine
playback
phase provides
01-foundation FastAPI app skeleton, lifespan pattern, DeviceRegistry, ShowStore, WebSocket ConnectionManager
MPVEngine class with load/play/pause/seek/stop and 10Hz position polling
apply_timer_fix() with sys.platform == win32 guard (INF-03)
Alpine Dockerfile with mpv-libs, ffmpeg, ffmpeg-libs, libsndfile
pyproject.toml with python-mpv, soundfile, numpy
MPVEngine integrated into FastAPI lifespan (app.state.engine)
02-02 (waveform extraction uses soundfile/numpy added here)
02-03 (WebSocket broadcast loop reads MPVEngine.get_state())
all future phases that use engine.get_state() for position
added patterns
python-mpv>=1.0.5 (libmpv ctypes binding for audio playback)
soundfile>=0.12.0 (audio file reading via libsndfile)
numpy>=1.26.0 (waveform downsampling)
mpv-libs 0.37.0-r0 (Alpine apk, libmpv.so.2)
ffmpeg + ffmpeg-libs 6.1.1-r0 (Alpine apk, MP3 decode support)
libsndfile 1.2.2-r1 (Alpine apk, WAV/FLAC/OGG reading)
10Hz polling via time.sleep(0.1) in daemon thread (not observe_property which fires per audio frame)
Thread-safe state snapshot via threading.Lock in get_state()
MPVEngine injected at lifespan startup, exposed via app.state.engine
ao=null default for server-mode (no audio output), overridable via MPV_AO env var
created modified
lightsync/audio/__init__.py
lightsync/audio/engine.py
Dockerfile
pyproject.toml
lightsync/main.py
MPV_AO env var controls audio output device — defaults to null for Docker server mode, overridable for local dev
Polling at 100ms (10Hz) over observe_property — observe_property fires per decoded audio frame (~43x/s at 44.1kHz), overwhelming asyncio
apply_timer_fix() is a Linux no-op — timeBeginPeriod is Windows-only Win32 API; guard with sys.platform == win32
MPVEngine uses ao param in constructor defaulting to null — caller controls output device
Pattern: Daemon thread polling loop — blocking operations in thread, results accessed thread-safely via Lock
Pattern: app.state.engine — MPVEngine available to all API routes via request.app.state.engine
Pattern: Lifespan start/stop symmetry — engine.start() on startup, engine.stop() on shutdown
AUD-01
AUD-03
INF-03
10min 2026-04-06

Phase 02 Plan 01: Audio Engine — MPVEngine Backend

MPVEngine wraps libmpv via python-mpv for headless audio playback with 10Hz position polling, integrated into FastAPI lifespan with Alpine Docker audio stack

Performance

  • Duration: ~10 min
  • Started: 2026-04-06T13:01:00Z
  • Completed: 2026-04-06T13:03:49Z
  • Tasks: 2 of 2
  • Files modified: 5

Accomplishments

  • Created MPVEngine class with load/play/pause/seek/stop methods and thread-safe 10Hz position polling
  • Updated Dockerfile with Alpine audio stack (mpv-libs, ffmpeg, ffmpeg-libs, libsndfile)
  • Added python-mpv, soundfile, numpy to pyproject.toml dependencies
  • Integrated MPVEngine into FastAPI lifespan with MPV_AO env var for configurable audio output
  • Implemented INF-03 as platform-guarded no-op (apply_timer_fix is Linux-safe)

Task Commits

Each task was committed atomically:

  1. Task 1: Add audio dependencies to Dockerfile and pyproject.toml - 474b4a7 (chore)
  2. Task 2: Create MPVEngine class with position polling and platform timer fix - 5f75a19 (feat)

Plan metadata: (committed with SUMMARY + state updates)

Files Created/Modified

  • lightsync/audio/__init__.py - Package marker for audio module
  • lightsync/audio/engine.py - MPVEngine class with headless mpv wrapper, 10Hz polling daemon thread, thread-safe get_state(), apply_timer_fix() INF-03 platform guard
  • Dockerfile - Added apk packages: mpv-libs, ffmpeg, ffmpeg-libs, libsndfile
  • pyproject.toml - Added python-mpv>=1.0.5, soundfile>=0.12.0, numpy>=1.26.0
  • lightsync/main.py - Import MPVEngine, create/start/stop in lifespan, expose via app.state.engine

Decisions Made

  • MPV_AO env var for audio output device: defaults to null (server mode, no speakers needed), overridable for local dev with actual audio output. This avoids hardcoding and keeps Docker images working without audio devices.
  • Polling over observe_property: observe_property fires on every decoded audio frame (~43x/s at 44.1kHz), which would flood the asyncio event loop. Fixed 100ms polling gives exactly 10Hz regardless of audio format.
  • apply_timer_fix() as Linux no-op: timeBeginPeriod is a Windows-only Win32 API (winmm.dll). On Linux, the kernel provides sub-millisecond timer resolution natively. INF-03 is satisfied as a platform-guarded stub.

Deviations from Plan

None — plan executed exactly as written. The load() method follows the research correction noted in the plan (no wait_for_playback() call, using async mpv loadfile).

Issues Encountered

None — all dependencies verified in research phase, implementation matched expected patterns.

User Setup Required

None - no external service configuration required. MPV_AO env var is optional (defaults to "null" which works in Docker without audio devices).

Next Phase Readiness

  • MPVEngine is ready for Plan 02-02 (waveform extraction using soundfile/numpy added in this plan)
  • MPVEngine is ready for Plan 02-03 (WebSocket broadcast loop using engine.get_state())
  • engine accessible in all API route handlers via request.app.state.engine
  • Docker image will build with all required audio libraries once deployed

Self-Check: PASSED

  • lightsync/audio/init.py: FOUND
  • lightsync/audio/engine.py: FOUND
  • lightsync/main.py: FOUND
  • .planning/phases/02-audio-engine/02-01-SUMMARY.md: FOUND
  • Commit 474b4a7 (chore: audio deps): FOUND
  • Commit 5f75a19 (feat: MPVEngine): FOUND

Phase: 02-audio-engine Completed: 2026-04-06