Per-client channel map: play a subset of the channels of a multichannel stream
Is your feature request related to a problem? Please describe.
With a multichannel capture (a multichannel soundcard, a DSP, a multichannel loopback), every client plays the same channels of the stream. There is no way to tell a client "play channels 6 and 7 of this stream on your stereo output", so feeding several independent zones from a single multichannel capture is not possible.
Describe the solution you'd like
A per-client channel map: a list of stream channel indices, where the position in the list is the output channel of the client's sound device.
- A
--channelmap 6,7client option: the PCM device is opened with as many channels as listed, output channeliplays stream channelchannel_map[i](0-based). - A
Client.SetChannelMapJSON-RPC command, so the zones can be assigned centrally, with aClient.OnChannelMapChangednotification. The server persists the map and pushes it in theServerSettingsmessage; the client applies it without reconnecting. The server map takes precedence over the client's option, an empty array clears it. - So that a controller knows how many channels a client accepts before assigning a map,
the client reports its audio device (name, description, rate, bits, channels) in the
Hellomessage. It shows up as adeviceobject of the client inServer.GetStatus, and insnapclient --list.
The channel extraction is done in the client Stream, after resampling, so every audio
backend benefits from it and the sync correction only runs on the extracted channels.
Clients and servers that don't know the added json fields ignore them, so old and new versions interoperate.
Describe alternatives you've considered
- One stream per zone on the server: this multiplies the sources, the encoders and the groups for what is physically a single capture, and the streams are not sample synchronous with each other.
- Doing the routing below snapclient (ALSA
dsnoop/routeplugins, PipeWire, or a loopback per zone): it works, but it has to be configured on every client with a different syntax per backend, it is invisible to the server, and it cannot be changed from a controller.
Additional context
I have a working implementation and I am willing to open a pull request if you think it fits the project.
Branch: https://github.com/QuentinGuiheneuc/snapcast/tree/upstream-pr (7 commits on top of
develop, 21 files, +599 lines)
Tested end to end: server with an 8 channel PCM pipe source and the client output written
to a file, checked sample by sample (correct channels and order, and after a live map
change); live Client.SetChannelMap while playing; fallback to the command line map when
the map is cleared; persistence across reconnects; out of range map ignored with an error
log; invalid RPC parameters rejected. Linux/alsa and Windows/wasapi, built for amd64,
arm64 and armhf.
If it helps to try it out, prebuilt packages (deb for amd64/arm64/armhf, zip for Windows) are here: https://github.com/QuentinGuiheneuc/snapcast/releases/tag/v0.35.0-channelmap1
Source: snapcast/snapcast