| 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 |
|
|
10min |
2026-04-06 |