feat(audio): rebuild playback + visualization on resident daemon and PCM cache
Two fragility points, rebuilt at the root: Playback: one resident mpv daemon (--idle --keep-open) with a persistent IPC connection and observe_property state instead of spawn-per-episode and connect-per-poll. Play/pause/seek are sub-ms commands; time-pos pushes at ~20Hz; external pauses arrive as events. Boot session restore preloads the episode paused (loadfile + paused time-pos seek, since mpv defers --start stream work until playback) so first Play is a ~400ms unpause instead of a cold 4.3s open+seek. Load ops are mutex-serialized so a raced preload cannot clobber an in-flight play. Data throttling: mpv demuxer cache capped (cache-secs=90, max-bytes=40MiB) so a paused preload no longer races to its 150MiB default (measured 45.7MB/12s); decoder paced at 4x realtime instead of 84x so playback start isn't starved by the visualizer ripping the whole episode. Visualization: replaced the paced-ring reader (AudioStreamReader) with a position-indexed PCM cache (audio-pcm-cache). ffmpeg fills a cache indexed by absolute playback time; reads at the player position are always exact. Pause freezes the render loop, resume re-arms it — no coverage guessing, no clamped-buffer freeze (the pause->broken-waveform->freeze bug). Seeks and speed changes need no pipeline restarts; uncovered reads return empty and the last frame holds. Cover art: persistent per-URL disk cache under XDG cache dir; play() no longer awaits a curl subprocess (up to 8s). Cache hit = one stat; misses apply late via mpv video-add. Test suite: 161 pass. New tests pin the position-index contract (sample- exact window reads, hold-on-uncovered, pause-keeps-cache, seek segments), the daemon contract (play/pause/resume/seek/stop, preload fast path, EOF->replay), and cover cache/single-flight/404.
This commit is contained in:
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* Uses a self-generated local WAV (a frequency chirp, so different playback
|
||||
* positions produce measurably different bar output) and the real ffmpeg +
|
||||
* native cavacore pipeline, mirroring audio-stream-reader.test.ts.
|
||||
* native cavacore pipeline, mirroring audio-pcm-cache.test.ts.
|
||||
*
|
||||
* Timing note: this is an integration test of the store's real timers — the
|
||||
* unload path is a genuine `setTimeout` in the store, and bun 1.3.8 ships no
|
||||
|
||||
Reference in New Issue
Block a user