feat: localav audio backend (FFmpeg + miniaudio)#894
Draft
Conversation
Define LocalPlayer interface and shared AudioDevice/MediaInfo types in backend/player so both mpv and a future alternative backend can implement the same contract without importing each other. - backend/player/equalizer.go: Equalizer interface + ISO10/15BandEqualizer moved from backend/player/mpv; mpv/equalizer.go is now type aliases - backend/player/localplayer.go: LocalPlayer interface, AudioDevice, MediaInfo - backend/player/mpv/player.go: implements player.LocalPlayer - backend/app.go: LocalPlayer field is now player.LocalPlayer interface; initMPV/setupMPV renamed to initLocalPlayer/setupLocalPlayer (dispatch via build-tagged files) - backend/app_player_mpv.go: //go:build !localav — extracts initLocalPlayer for the mpv backend - backend/playbackengine.go, ui/**: update type assertions and imports to use player.* types instead of mpv.* Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
New alternative local player backend gated behind the `localav` build tag.
Build with: go build -tags "migrated_fynedo localav" ./...
Stack:
- libavformat/libavcodec/libavfilter for demux, decode, and DSP
- miniaudio (vendored single-header 0.11.21) for audio output and
device enumeration via CoreAudio/WASAPI/ALSA
Key features:
- Gapless playback via double-slot decoder: next track is pre-opened and
swapped in immediately when the current decoder hits EOF, before the
ring buffer drains, eliminating audible gaps
- SPSC lock-free ring buffer between decode goroutine and miniaudio callback
- 15-band parametric EQ via avfilter graph (same filter string as mpv backend)
- ReplayGain from file tags via avfilter volume node
- Audio device enumeration and selection including exclusive mode
- Peak/RMS metering via astats avfilter
- Seek safety: decode goroutine is stopped synchronously before
av_player_seek rebuilds the filter graph (prevents SIGSEGV race)
- Direct PCM sample access hook point for future ProjectM visualizer
Files:
- backend/player/localav/av_player.{h,c} — C engine
- backend/player/localav/player.go — CGo bindings, implements LocalPlayer
- backend/player/localav/miniaudio.h — vendored miniaudio 0.11.21
- backend/player/localav/cgo_{darwin,linux,windows}.go — platform CGo flags
- backend/app_player_localav.go — //go:build localav app init
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Replaces libmpv as the local audio engine with a bespoke stack built on FFmpeg (libav*) and miniaudio. Gated behind the
localavbuild tag so mpv remains the default until this is production-ready.Build:
Why
The primary motivation is direct PCM sample access to enable additional visualizers such as ProjectM — libmpv's abstraction layer prevents tapping decoded frames — as well as other new audio features such as crossfade playback that aren't supported by MPV. A lower-level pipeline also removes the large libmpv native binary dependency.
Stack
Features
Commit structure
Equalizer,AudioDevice,MediaInfo, andLocalPlayerinterface out of thempvpackage intobackend/player, so both backends can satisfy the same contract without importing each otherbackend/player/localav/backend + build-tagged app-init filesWhat's not done yet
Test plan
go build -tags "migrated_fynedo localav" ./...compiles cleanlygo test -tags "migrated_fynedo localav" ./backend/player/localav/...passesgo build -tags migrated_fynedo ./...) still compiles and plays🤖 Generated with Claude Code