feat: add non-cgo linux pulseaudio driver#277
Conversation
Add pulseaudio support on linux via github.com/jfreymuth/pulse that does not require CGO to build. Makes it the default linux audio option, falling back to the ALSA implementation if CGO is enabled. Written entirely with Copilot, plus some manual human testing via the example app.
There was a problem hiding this comment.
Pull request overview
Adds a Linux PulseAudio backend implemented in pure Go (no CGO required), making it the default Linux audio driver while keeping an ALSA fallback when CGO is enabled.
Changes:
- Introduces a new pure-Go PulseAudio driver for Linux and wires it into the Linux driver selection logic.
- Adds a CGO-only ALSA backend for Linux as a fallback when PulseAudio initialization fails.
- Updates documentation and CI to reflect/build-test the new
CGO_ENABLED=0Linux configuration, and adds the new module dependency.
Reviewed changes
Copilot reviewed 8 out of 9 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| go.mod | Adds github.com/jfreymuth/pulse dependency for PulseAudio support. |
| go.sum | Records checksums for the added PulseAudio dependency. |
| driver_unix.go | Excludes Linux from the generic Unix (ALSA/cgo) driver build tag. |
| driver_pulseaudio_linux.go | New pure-Go PulseAudio implementation and suspend/resume plumbing. |
| driver_linux_nocgo.go | Linux !cgo context: PulseAudio-only initialization path. |
| driver_linux.go | Linux cgo context: prefer PulseAudio, fallback to ALSA on init failure. |
| driver_alsa_linux.go | New Linux-only ALSA backend (cgo) used as fallback. |
| README.md | Documents PulseAudio preference and CGO-dependent ALSA fallback/requirements. |
| .github/workflows/test.yml | Adds a CGO_ENABLED=0 Linux build step to CI. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
I'll also mention I've done some aditional tests beyond just the example. I'm currently using AI to port ironwail quake to Go, and I'm using the version of oto from this PR as the audio backend, and all the audio seems to be working well on my pure-go/non-cgo build. |
|
Thanks, but this PR does multiple things at the same time: making the Linux driver pure Go and add PluseAudio driver. Could we split this into two PRs (for pure Go and for PluseAudio)? |
|
Sure, no probs. I'll add a PR that just adds the pulse audio driver, and then a second one for making that the default for Linux when building with |
|
Sorry if this is my misunderstanding, but doesn't this make ALSA driver pure Go? |
|
Ahh, no, the ALSA driver remains CGO. The things that change in this PR are:
|
|
Hmm, adding a driver for PulseAudio as another option is fine, but making it as a default when Cgo is disabled seems a little risky. If we had both driver are pure Go (or both are not pure Go), would there be a strong reason to prefer PulseAudio? |
|
If cgo is disabled, linux doesn't have any drivers, so making it the default in that case made sense to me, but I can see the case for not changing the default behavour when building with CGO, so that existing users don't suddenly end up with a different audio backend being used when they update. I can back that out easy enough. |
|
So, I was wondering how feasible it would be to make ALSA pure Go first. |
|
There are existing pure-go ALSA packages out there. I'm also currently trying to get https://github.com/gen2brain/alsa to work, but while it's building, I'm not actually getting any audio out of it. It is also limited in that it relies on direct hardware access, so can't use virtual mixers, etc provided by ALSA. |
27038ca to
7b4d8e4
Compare
|
I've made it so the alsa driver is the first attempt when probing linux drivers. I also added in an option for setting the pulseaudio client application name. Pulseaudio clients identify themselves in the servers mixer via self-reported names, but prevously I'd hard coded this to be Oto. Now it is only that by default and you can override it with an option when creating the context. |
|
I started to think that we might be able to discard the current ALSA driver and adopt PulseAudio, as PulseAudio seems more de facto standard for Linux audio than ALSA, but I'm not familar with Linux audio system. Do you have any insights? |
|
Linux has too many audio system, but pulseaudio made big inroads a few years ago and is probably more the defacto standard for common desktop audio than ALSA nowadays. Off the top of my head, there is OSS, ALSA, JACK, Pulseaudio and Pipewire. Pipewire is the newest one being rapidly adopted I'm aware of and it includes builtin compatibility for ALSA/JACK/Pulseaudio applications, so targetting pulse audio nets you a fairly wide range of support. I've also been toying with the idea of pure-go bindings for something like https://miniaud.io/ but this seemed like a quick and relatively easy solution before heading down that route. |
|
Sure, then could you discard the ALSA driver in this PR? See also #154: there is already someone who said PulseAudio is preferrable. |
|
Sure, I can do. I'm not sure how available pulseaudio is outside of linux though, so perhaps you want to retain ALSA support for BSD, etc? |
Apparently ALSA's main target is Linux, and FreeBSD has a port of it, but other BSD does not. PulseAudio seems more portable. It's ok not to keep it. |
|
Cool, addressed all those. Thanks for working with me so quickly to get this mergeable. |
|
I'll take a look at this tomorrow. Thank you for your quick work! |
|
|
||
| func newPulseAudioContext(sampleRate int, channelCount int, mux *mux.Mux, bufferSizeInBytes int, applicationName string) (*pulseAudioContext, error) { | ||
| c := &pulseAudioContext{ | ||
| func newPulseAudioContext(sampleRate int, channelCount int, mux *mux.Mux, bufferSizeInBytes int, applicationName string) (c *pulseAudioContext, err error) { |
Add pulseaudio support on linux via github.com/jfreymuth/pulse that does not require CGO to build.
Makes it the default linux audio option, falling back to the ALSA implementation if CGO is enabled.
Written entirely with Copilot, plus some manual human testing via the example app.