I'll post a hacky workaround that I've been using until an expert tells us how to do this right :) Fair warning that most of what I have comes from diligent cargo culting, so chances are high that I'm not following best practices, and don't take this post as a guide to correct terminology. My use case was that I had to find a solution for MS Teams not allowing system audio to be shared, and I'm going to describe my current setup.
I'll assume you have pulseaudio, and that you can find the UPnP-enabled speaker as an output device in your settings, and your only issue is sending audio to two different places. If this is not the case then the below answer will probably not help you.
You can create a virtual device with
pactl load-module module-null-sink sink_name=myduplexsink
This will create a null sink that acts like a box you can direct audio into and out from. The sink name can be used to retain your setup programmatically by setting a few things in
~/.pulse/default.pa but my setup is unreliable enough that I haven't gotten around to making this all automatic.
You will also need two (I think) loopbacks, which act like virtual cables connecting devices:
pactl load-module module-loopback
pactl load-module module-loopback
Loopbacks cannot be named; as I understand you'd specify their source and sink on creation, if you were to automate this.
Now for the interactive solution, (install and) open
pavucontrol that is a buffed-up configuration panel for pulseaudio. It has five tabs:
- Playback: this is where you should see whatever program is creating the sound you want to share to two places. In my use case this is
mplayer, and I want its audio to be directed both into a null sink (together with my microphone's output) and to my headphones. This is also the tab where you specify the sink of every loopback you create.
- Recording: this is where programs expecting audio input appear. In my use case this is MS Teams, but I think in your case there's none. But this tab also configures the source of every loopback.
- Output devices: you should see your speakers (both kind, I hope) and your null sink named Null Output here.
- Input devices: you should see your microphone (if any) here, and every "monitor" device. For every output device you have a "monitor" that you can use as input. If your speakers are booming music then the monitor of your speakers will carry the music as audio signal.
- Configuration: this shall be ignored now :P
So what I think you should do:
- start the program that provides your audio (e.g. music software). In the Playback tab of
pavucontrol find the row that corresponds to this program, and send it to "Null Output". Now you have a virtual device (the null sink) that has the audio you want to split.
- still in the Playback tab set one loopback to use your speaker (if my laptop is any indication, called "Built-in Analog Stereo"), and the other loopback to use your UPnP speaker as sink.
- In the Recording tab set the source of both loopbacks to your "Monitor of Null Output".
Now you have your input audio enter the null sink, and leave it both toward your local speaker and toward the UPnP speaker. Hopefully this is just what you need.
I've hacked most of this together based on this post. It briefly mentions that once you have manually set up a working configuration you can use
pacmd info and excavate the names of the audio devices that you want to use. This would allow you to recreate the configuration automatically when pulseaudio starts, by adding a few commands to
~/.pulse/default.pa. I haven't been able to get this working, but mostly because I have a weird set of headphones that gets recognized under multiple device names depending on the circumstances. This might not be a problem for you. So in this case the new lines in your
~/.pulse/default.pa would look something like this:
load-module module-null-sink sink_name=myduplexsink
load-module module-loopback source=myduplexsink.monitor sink=<device ID of your local speakers>
load-module module-loopback source=myduplexsink.monitor sink=<device ID of your UPnP speakers>
The device IDs are probably listed in the output of
pacmd info under "[n] sink(s) available". In your case
n will probably be at least 3: the two speakers and
myduplexsink. You can also see a "Default sink name" at the top of the output, which will probably correspond to your local speakers.
You will probably still have to direct the software that provides the audio to your "Null Output" in
If your UPnP speaker is fed through some program that shows up in the Recording tab of
pavucontrol then it's even easier: you can directly tell it to pull audio from the "Monitor of Null Output", and you'd only need a single loopback to feed the same signal to your local speakers.