Skip to content

feat: add non-cgo linux pulseaudio driver#277

Merged
hajimehoshi merged 14 commits intoebitengine:mainfrom
darkliquid:feat/pulseaudio
Apr 1, 2026
Merged

feat: add non-cgo linux pulseaudio driver#277
hajimehoshi merged 14 commits intoebitengine:mainfrom
darkliquid:feat/pulseaudio

Conversation

@darkliquid
Copy link
Copy Markdown
Contributor

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.

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.
Copilot AI review requested due to automatic review settings March 28, 2026 10:40
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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=0 Linux 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.

Comment thread driver_alsa_linux.go Outdated
Comment thread driver_alsa_linux.go Outdated
Comment thread driver_alsa_linux.go Outdated
Comment thread driver_alsa_linux.go Outdated
Comment thread driver_alsa_linux.go Outdated
@darkliquid
Copy link
Copy Markdown
Contributor Author

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.

@hajimehoshi
Copy link
Copy Markdown
Member

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)?

@darkliquid
Copy link
Copy Markdown
Contributor Author

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 CGO_ENABLED=0. Is that more what you had in mind?

@hajimehoshi
Copy link
Copy Markdown
Member

Sorry if this is my misunderstanding, but doesn't this make ALSA driver pure Go?

@darkliquid
Copy link
Copy Markdown
Contributor Author

Ahh, no, the ALSA driver remains CGO. The things that change in this PR are:

  • Add a new pure go pulseaudio driver for linux
  • Move the existing ALSA driver from driver_linux.go to driver_alsa_linux.go (since linux now has 2 backends)
  • Update driver_linux.go to delegate to pulse/alsa, preferring pulse audio, but failing back to ALSA as a secondary if it is available.

@hajimehoshi
Copy link
Copy Markdown
Member

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?

@darkliquid
Copy link
Copy Markdown
Contributor Author

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.

@hajimehoshi
Copy link
Copy Markdown
Member

So, I was wondering how feasible it would be to make ALSA pure Go first.

@darkliquid
Copy link
Copy Markdown
Contributor Author

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.

@darkliquid
Copy link
Copy Markdown
Contributor Author

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.

@hajimehoshi
Copy link
Copy Markdown
Member

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?

@darkliquid
Copy link
Copy Markdown
Contributor Author

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.

@hajimehoshi
Copy link
Copy Markdown
Member

Sure, then could you discard the ALSA driver in this PR?

See also #154: there is already someone who said PulseAudio is preferrable.

@darkliquid
Copy link
Copy Markdown
Contributor Author

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?

@hajimehoshi
Copy link
Copy Markdown
Member

hajimehoshi commented Mar 28, 2026

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.

Comment thread driver_unix.go Outdated
Comment thread .github/workflows/test.yml
Comment thread .github/workflows/test.yml Outdated
Comment thread driver_pulseaudio.go Outdated
Comment thread README.md Outdated
@darkliquid
Copy link
Copy Markdown
Contributor Author

Cool, addressed all those. Thanks for working with me so quickly to get this mergeable.

@hajimehoshi
Copy link
Copy Markdown
Member

I'll take a look at this tomorrow. Thank you for your quick work!

Comment thread .github/workflows/test.yml Outdated
Comment thread .github/workflows/test.yml
Comment thread .github/workflows/test.yml Outdated
Comment thread .github/workflows/test.yml Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread driver_unix.go
Comment thread driver_unix.go Outdated
Comment thread context.go Outdated
Comment thread driver_darwin.go Outdated
Comment thread driver_darwin.go Outdated
Comment thread driver_windows.go Outdated
Comment thread go.mod Outdated
Comment thread README.md Outdated
Comment thread README.md Outdated
Comment thread go.sum Outdated
Comment thread driver_unix.go
Comment thread README.md Outdated
Comment thread driver_unix.go
Comment thread driver_unix.go Outdated

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) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use client instead of c

Comment thread driver_unix.go Outdated
Comment thread driver_unix.go Outdated
Comment thread driver_unix.go Outdated
Comment thread driver_unix.go Outdated
Copy link
Copy Markdown
Member

@hajimehoshi hajimehoshi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thanks!

@hajimehoshi hajimehoshi merged commit e3a524c into ebitengine:main Apr 1, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants