Add Pulse Audio Out player provider#3683
Add Pulse Audio Out player provider#3683iVolt1 wants to merge 164 commits intomusic-assistant:devfrom
Conversation
…ant\providers\spotify_connect_go
…ant\providers\spotify_connect_go
…nt_dev\server\music_assistant\providers\spotify_connect_go
The logic is now: MA sends 32-bit containers → unpack as int32 → scale → clip to 24-bit range → view as uint8 → take low 3 bytes per sample → write as packed s24le to PA. The PA stream opens as PA_SAMPLE_S24LE=9 which matches what the DragonFly remap sink expects.
Three fixes in this pass: Volume passthrough — 24-bit now always repacks even at volume=100, since PA needs packed s24le regardless Byte extraction — [:, 1:] takes bytes 1, 2, 3 (the upper 3 bytes of the left-justified int32), not [:, :3] Mute silence — corrected to 3/4 the input length for s24le
One-line change — channels < 2 → channels < 1. The bridge already opens all streams as 2ch (BRIDGE_CHANNELS = 2), so PA receives stereo and remaps to however many channels the sink has. The 5.1 and 7.1 sinks will now appear as players.
🔒 Dependency Security Report✅ No dependency changes detected in this PR. |
|
Can't we merge this with the normal local out player provider ? |
|
Your merging this merge this with the normal local out player provider would be ideal. Thanks. |
|
The claim that PortAudio does not support PulseAudio seems incorrect. It looks to me like there is a hostapi for PulseAudio https://github.com/PortAudio/portaudio/tree/master/src/hostapi/pulseaudio I imagine there is probably a cleaner way to implement pulseaudio in the current local audio provider. |
We would be looking for you to make this a PR against the existing local audio out player |
Yes, the claim that PortAudio does not support PulseAudio is technically incorrect so I can remove that. Are you ok with me continuing to use pa_simple instead of PortAudio? |
Is it ok for me to just move the code from the pulse_audio provider to the local_audio provider and make a new PR? |
I would like to see this as an enhancement of the Local Audio provider, but this PR needs more work than just moving files around. It looks like it touches files that it doesn't have any reason to, and doesn't cleanly integrate in some that it does (take the Dockerfile.base for instance). I'd recommend implementing it into the local audio provider, but also carefully going over everything it does and how it is implemented. It looks like it was genereated with AI (which is fine), and needs a bit of human touch and engineering to get it over the finish line. |
|
Thanks for your comments. I am working on a clean version. Yes, Claude has been substantially involved. I will continue with pa_simple instead of portaudio if that is ok. One piece of unfinished business is making pactl more available by having MA install it via the Dockerfile rather than having pactl installed in bin directory of the provider. I don't have access to the MA Dockerfile when developing on the Music Assistant DEV SERVER app as far as I can tell so is this something the MA team would like to do or should I continue with locating pactl in the provider bin directory? Thanks. |
I think my preference would be to include the dependency in the Dockerfile.base, but it looks like your PR was adding a whole separate FROM and RUN step rather than just including the dependency with all the others already being installed. Committing binary files directly seems sketchy as opposed to installing a library from a known repo. As far as portaudio goes, although they have some pulseaudio stuff committed in the main branch, it looks like it hasn't made it to an actual release yet, so we probably can't use it yet unless we wanted to build that from source. |
Add Pulse Audio Out player provider
Summary
Adds a new
pulse_audioplayer provider that exposes PulseAudio sinks as Music Assistant sendspin players. Each sink is registered as an external Sendspin bridge client, enabling synchronized multi-room playback alongside existing Sendspin players. Audio is written directly to PulseAudio via a minimallibpulse-simplectypes wrapper — no additional Python dependencies are required beyondnumpyfor software volume scaling.Motivation
Users running Music Assistant on Home Assistant OS with multi-channel sound cards have no way to use those cards as MA players. The existing local audio provider uses PortAudio/sounddevice which cannot enumerate PulseAudio virtual sinks such as
module-remap-sinkstereo pairs. This provider targets PulseAudio directly viapactlandlibpulse-simple, correctly discovering and playing to all sinks including remap sinks, combined sinks, S/PDIF, and HDMI outputs.Changes
New provider —
music_assistant/providers/pulse_audio/Dependencies
libpulse-simple.so.0— must be present on the host (standard PulseAudio installation)pactl— required at startup for sink enumeration (pulseaudio-utilson Debian/Ubuntu,pulseaudioon Alpine)numpy— used for PCM volume scaling (already a MA dependency)depends_on: sendspin)Acknowledgements
This provider is based on the architecture of the existing
local_audioplayer provider, adapting its Sendspin bridge pattern and per-device player model to target PulseAudio sinks directly rather than PortAudio devices.Expanding Outputs with Stereo Pair Remap Sinks
Multi-channel sound cards (5.1, 7.1 surround) expose a single multi-channel PulseAudio sink by default. To use each channel pair as an independent MA player, PulseAudio
module-remap-sinkcan split a multi-channel sink into individual stereo sinks — one per channel pair (front, rear, side, center/LFE). The Pulse Audio Out provider discovers and registers all remap sinks automatically alongside physical sinks, so no additional configuration is needed in MA once the remap sinks exist.For Home Assistant OS users, the companion addon Pulse Audio Stereo Pairs automates this setup. It runs as a lightweight HA addon that creates the remap sinks on startup and reacts to audio device hot-plug and unplug events, removing the need to configure remap sinks manually via
## Add Pulse Audio Out player providerpactl. Once both the addon and this provider are running, each channel pair of every multi-channel card appears as a separate player in Music Assistant.Summary
Adds a new
pulse_audioplayer provider that exposes PulseAudio sinks as Music Assistant players. Each sink is registered as an external Sendspin bridge client, enabling synchronized multi-room playback alongside existing Sendspin players. Audio is written directly to PulseAudio via a minimallibpulse-simplectypes wrapper — no additional Python dependencies are required beyondnumpyfor software volume scaling.Motivation
Users running Music Assistant on Home Assistant OS with multi-channel sound cards have no way to use those cards as MA players. The existing local audio provider uses PortAudio/sounddevice which cannot enumerate PulseAudio virtual sinks such as
module-remap-sinkstereo pairs. This provider targets PulseAudio directly viapactlandlibpulse-simple, correctly discovering and playing to all sinks including remap sinks, combined sinks, S/PDIF, and HDMI outputs.Changes
New provider —
music_assistant/providers/pulse_audio/__init__.pyprovider.pyLocalPulseAudioProvider— thin shell delegating to bridge managerplayer.pyLocalPulseAudioPlayer— MA player model per sink with software volume and mutesendspin_bridge.pySendspinPulseAudioBridgeandLocalPulseAudioBridgeManager— sink enumeration viapactl, Sendspin bridge registration, audio writer looppa_simple.pylibpulse-simplefor direct PCM playback to a named PA sinkhelpers.pyfind_pactl()andpactl_env()utilitiesconstants.pymanifest.jsondepends_on: sendspin, typeplayer, stagealphaModified —
music_assistant/providers/sendspin/bridge_role.pysetup_audio_requirements()gains optionalsample_rate,bit_depth, andchannelsparameters (defaulting to existing bridge constants — no breaking change for AirPlay and other existing callers)preferred_formatproperty exposing the configuredAudioRequirementssoplayback.pycan read per-bridge formatModified —
music_assistant/providers/sendspin/playback.py_get_member_output_format()— theBridgePlayerRolebranch now readsrole.preferred_formatinstead of returning hardcoded bridge constants, enabling per-sink native format routingHow It Works
On startup the provider enumerates all PulseAudio sinks via
pactl --format=json list sinks. For each sink it registers a Sendspin bridge client advertising the sink's native sample rate and bit depth inClientHelloPlayerSupport. This causes MA to transcode each track to the correct format per sink — a 96kHz/24-bit sink receives 96kHz/24-bit PCM, a 48kHz/16-bit sink receives 48kHz/16-bit PCM, and so on.Audio chunks arrive via
BridgePlayerRole.on_audio_chunk, pass through software volume scaling, and are written to the PA sink viapa_simple_write. The 24-bit path handles MA's left-justified 32-bit container delivery, repacking to packeds24lebefore writing.The provider also reacts to volume and mute commands from the MA UI via
BridgePlayerRole.set_player_volumeandset_player_mute.Key Implementation Notes
pa_sample_format_to_string—PA_SAMPLE_S32LE = 7,PA_SAMPLE_S24LE = 9(not the values commonly found in older documentation)s24lefor PApactl set-sink-volumeat startup to prevent clippingTesting
Tested on Home Assistant OS 17.1 (amd64) with the following sink types:
Dependencies
libpulse-simple.so.0— must be present on the host (standard PulseAudio installation)pactl— required at startup for sink enumeration (pulseaudio-utilson Debian/Ubuntu,pulseaudioon Alpine)numpy— used for PCM volume scaling (already a MA dependency)depends_on: sendspin)Acknowledgements
This provider is based on the architecture of the existing
local_audioplayer provider, adapting its Sendspin bridge pattern and per-device player model to target PulseAudio sinks directly rather than PortAudio devices.Expanding Outputs with Stereo Pair Remap Sinks
Multi-channel sound cards (5.1, 7.1 surround) expose a single multi-channel PulseAudio sink by default. To use each channel pair as an independent MA player, PulseAudio
module-remap-sinkcan split a multi-channel sink into individual stereo sinks — one per channel pair (front, rear, side, center/LFE). The Pulse Audio Out provider discovers and registers all remap sinks automatically alongside physical sinks, so no additional configuration is needed in MA once the remap sinks exist.For Home Assistant OS users, the companion addon [Pulse Audio Stereo Pairs](https://github.com/iVolt1/hassio-apps) automates this setup. It runs as a lightweight HA addon that creates the remap sinks on startup and reacts to audio device hot-plug and unplug events, removing the need to configure remap sinks manually via
pactl. Once both the addon and this provider are running, each channel pair of every multi-channel card appears as a separate player in Music Assistant.