Separate Audio Output for Applications

I'm trying to find a way to change output sources for applications so I can have OBS only play the audio I want it to for streaming (i.e the stream can hear my voice and game audio but not my music or discord calls). I want to hear all the audio currently playing through my headphones but only have OBS output specific sources for streams.

In Windows, people would use Voicemeeter Banana or virtual cables to do this; what is the linux equivalent? I've tried messing with null-sinks and loopback, but I don't have a clear understanding on what they're for or how they work. I somewhat got it to function, but when setting it up in OBS, the audio either starting echoing horribly or didn't sound as clear when switching between my headphones and the combined sink.

Many people have mentioned JACK, but it seems to have issues with my setup. I have a pair of Turtle Beach PX24s connected to my motherboard's audio ports (it requires an audio splitter cable to work on PC if you have a mic and speaker port; it is not usb connected) and I can not for the life of me figure out how to get JACK to select my headphones as the output device and play audio; I tried all the output device sources available and none playback audio when JACK runs.

How would I go about setting this up? I do have an audio interface (Focusrite Scarlette Solo 1st), though I can only use it for my guitar (don't own a XLR Microphone, nor studio headphones).

I may not be entirely understanding what you are trying to accomplish but kde lets you choose an audio output per application in system settings or via the Audio Volume in the tray . Do you need something more complicated that that?

In a way, I guess. I know you can change the output per application, but I'm trying to get it to so that all the audio can still be heard from my headphones.

It's something like this:

Application 1 plays through Output 1 (Headphones)
Application 2 plays through Output 2 (Virtual Output or something)

Even though they play audio through two different outputs, I can still hear both from one output (my headphones).

In OBS, the audio settings would be:

Desktop Audio 1 set to Output 1 (I'll call this "Master Volume")
Desktop Audio 2 set to Output 2 (and this "Stream Volume")

Anything that plays in Master Volume is what I hear, while the audio playing in Stream Volume is only what the stream hears. I still hear every sound playing on my system, but the stream can only hear what is being outputted through the Stream Volume (Output 2). This means I hear Application 1 & 2, while stream only Application 2.

I think this video explains it best at what I'm trying to do.


I know it's not going to be the same process with Linux, so I'm trying to find the equivalent method, whether through ALSA, PulseAudio, or JACK (or whatever else I need to use).

I think this is what you need.
https://wiki.archlinux.org/index.php/PulseAudio/Troubleshooting#Enable_Echo/Noise-Cancellation

So far it seems to have solved my issue, but now I have a question about this.

When I opened up a game (DOOM 2016), I had about 3 application sound output sources pop up in the volume tray. Is that supposed to happen or is it just something with OBS, and is there a way to fix that?

I am not sure maybe provide a picture so someone else can have a look.

Here's a screenshot of what I'm talking about, 3 instances of DOOM audio . I don't think this is a OBS issue 'cause I took this screenshot with the application closed.

EDIT: I unloaded the echo/noise-cancellation and it still appears, so not sure if messing with pulseaudio made this happen or it's a game/steam (proton) issue.
Screenshot_20200520_040954

What is the output of pacmd list-sink-inputs?

This is what I get when the game isn't running:

[noire@Hyperdimension ~]$ pacmd list-sink-inputs
1 sink input(s) available.
    index: 0
        driver: <module-echo-cancel.c>
        flags: VARIABLE_RATE START_CORKED 
        state: CORKED
        sink: 1 <alsa_output.pci-0000_28_00.3.analog-stereo>
        volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        muted: no
        current latency: 0.00 ms
        requested latency: 100.14 ms
        sample spec: float32le 2ch 32000Hz
        channel map: front-left,front-right
                     Stereo
        resample method: speex-float-1
        module: 26
        properties:
                media.name = "Echo-Cancel Sink Stream"
                media.role = "filter"
                module-stream-restore.id = "sink-input-by-media-role:filter"

And here's when it is:

[noire@Hyperdimension ~]$ pacmd list-sink-inputs
3 sink input(s) available.
    index: 0
        driver: <module-echo-cancel.c>
        flags: VARIABLE_RATE START_CORKED 
        state: CORKED
        sink: 1 <alsa_output.pci-0000_28_00.3.analog-stereo>
        volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        muted: no
        current latency: 0.00 ms
        requested latency: 100.14 ms
        sample spec: float32le 2ch 32000Hz
        channel map: front-left,front-right
                     Stereo
        resample method: speex-float-1
        module: 26
        properties:
                media.name = "Echo-Cancel Sink Stream"
                media.role = "filter"
                module-stream-restore.id = "sink-input-by-media-role:filter"
    index: 11
        driver: <protocol-native.c>
        flags: 
        state: RUNNING
        sink: 1 <alsa_output.pci-0000_28_00.3.analog-stereo>
        volume: front-left: 65536 / 100% / 0.00 dB,   front-right: 65536 / 100% / 0.00 dB
                balance 0.00
        muted: no
        current latency: 113.39 ms
        requested latency: 100.14 ms
        sample spec: float32le 2ch 48000Hz
        channel map: front-left,front-right
                     Stereo
        resample method: copy
        module: 13
        client: 23 <DOOMx64vk.exe>
        properties:
                media.name = "Simple DirectMedia Layer"
                application.name = "DOOMx64vk.exe"
                native-protocol.peer = "UNIX socket client"
                native-protocol.version = "33"
                application.process.id = "4661"
                application.process.user = "noire"
                application.process.host = "Hyperdimension"
                application.process.binary = "wine64-preloader"
                application.language = "en_US.UTF-8"
                window.x11.display = ":0"
                application.process.machine_id = "4becef8b55f94bc1bb1ede596a2d69ba"
                application.process.session_id = "1"
                module-stream-restore.id = "sink-input-by-application-name:DOOMx64vk.exe"
    index: 12
        driver: <protocol-native.c>
        flags: 
        state: RUNNING
        sink: 1 <alsa_output.pci-0000_28_00.3.analog-stereo>
        volume: mono: 65536 / 100% / 0.00 dB
                balance 0.00
        muted: no
        current latency: 165.41 ms
        requested latency: 100.14 ms
        sample spec: float32le 1ch 11025Hz
        channel map: mono
                     Mono
        resample method: speex-float-1
        module: 13
        client: 24 <DOOMx64vk.exe>
        properties:
                media.name = "Simple DirectMedia Layer"
                application.name = "DOOMx64vk.exe"
                native-protocol.peer = "UNIX socket client"
                native-protocol.version = "33"
                application.process.id = "4661"
                application.process.user = "noire"
                application.process.host = "Hyperdimension"
                application.process.binary = "wine64-preloader"
                application.language = "en_US.UTF-8"
                window.x11.display = ":0"
                application.process.machine_id = "4becef8b55f94bc1bb1ede596a2d69ba"
                application.process.session_id = "1"
                module-stream-restore.id = "sink-input-by-application-name:DOOMx64vk.exe"

This is with Echo/Noise-Cancellation running on my system. I also notice that my game crashes often now (might be the issue) when I minimize the window.

You should only have one, something is probably wrong with you configuration.

index11 - resample method: copy
index12 - resample method: speex-float-1

I posted about using loopback and null-sink not too long ago
using Pulseaudio modules alone is similar to trying to hardwire audio in Windows with just virtual cables
forum.manjaro.org - Voicemeeter equivalent for linux

those modules would allow a mix to discord of all audio playing to desktop
for use-case described so far, probably need another module in pulseaudio to be able to send audio to 2 simultaneous outputs

pactl load-module module-combine-sink

and then assign audio streams in pavucontrol --tab=1

select from 3 different options available in drop-down menu

  1. simultaneous output - audio to headphones and null-sink mix for discord
    game audio would be routed to simultaneous output
    and anything else to be heard in headphones and on discord

  2. headphones
    audio from callers on discord must be routed only to headphones
    if audio is sent to simultaneous output audio from discord callers will be echoed back to them
    (music routed to headphones would also not be heard in null-sink discord mix)

  3. null-sink
    microphone must be routed to null sink only
    do not want to hear microphone in headphones as it will be delayed by looping around in pulseaudio (listen at source from device or mic monitor in ALSA for no delay)

connecting all this with pulseaudio modules is not easy to work out because there is not much to see in pavucontrol or other audio controls
may help to install pagraphcontrol from AUR to see what is going on in pulseaudio

How would I go about fixing that? Oh, I forgot to put the third DOOM audio instance in (it only loads once you get to the game start screen).

Works so far. How do I save the settings?

settings to direct audio streams in pavucontrol should be saved and applied to same audio sources in future, when same audio sinks are available

pactl commands to load the extra modules would not survive a reboot
or restarting pulseaudio - systemctl --user restart pulseaudio.service

this may be useful if the extra modules are only needed temporarily
modules can be loaded by using the 4 commands in a bash script
and restart to remove them when no longer needed

modules can be loaded permanently by changing pulseaudio system configuration file etc/pulse/default.pa
but it is better to create a customised configuration in home folder

copy the default configuration file to home folder
cp /etc/pulse/default.pa ~/.config/pulse/default.pa

then edit the new file and add the extra lines for the modules:

load-module module-null-sink sink_name=inputs
load-module module-loopback sink=inputs
load-module module-combine-sink
set-default-source inputs.monitor

restart pulseaudio or reboot to load new configuration in home folder

I figured out how to disable the inputs (the third one is the actual audio source), but they'll appear again if I close the game and reopen it. Is there a way to disable or just plain remove the first two sources on game startup?

I guess the bigger question is whether this is a Steam, Proton, Linux, or game issue. I deleted and reinstalled DOOM 2016, checked other games that used Proton, deleted/reinstalled Proton and Steam; not sure what else I can to do at this point.

I think the issue should be in your sink configuration, if you delete it and restart pulseaudio it should go back to normal, paste your sink configuration.

Where do I find my sink configuration?

Sorry, should been more specific:
post the contents of /etc/pulse/default.pa

#!/usr/bin/pulseaudio -nF
#
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PulseAudio is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.

# This startup script is used only if PulseAudio is started per-user
# (i.e. not in system mode)

.fail

### Automatically restore the volume of streams and devices
load-module module-device-restore
load-module module-stream-restore
load-module module-card-restore

### Automatically augment property information from .desktop files
### stored in /usr/share/application
load-module module-augment-properties

### Should be after module-*-restore but before module-*-detect
load-module module-switch-on-port-available

### Load audio drivers statically
### (it's probably better to not load these drivers manually, but instead
### use module-udev-detect -- see below -- for doing this automatically)
#load-module module-alsa-sink
#load-module module-alsa-source device=hw:1,0
#load-module module-oss device="/dev/dsp" sink_name=output source_name=input
#load-module module-oss-mmap device="/dev/dsp" sink_name=output source_name=input
#load-module module-null-sink
#load-module module-pipe-sink

### Automatically load driver modules depending on the hardware available
.ifexists module-udev-detect.so
load-module module-udev-detect
.else
### Use the static hardware detection module (for systems that lack udev support)
load-module module-detect
.endif

### Automatically connect sink and source if JACK server is present
.ifexists module-jackdbus-detect.so
.nofail
load-module module-jackdbus-detect channels=2
.fail
.endif

### Automatically load driver modules for Bluetooth hardware
.ifexists module-bluetooth-policy.so
load-module module-bluetooth-policy
.endif

.ifexists module-bluetooth-discover.so
load-module module-bluetooth-discover
.endif

### Load several protocols
load-module module-dbus-protocol
.ifexists module-esound-protocol-unix.so
load-module module-esound-protocol-unix
.endif
load-module module-native-protocol-unix

### Network access (may be configured with paprefs, so leave this commented
### here if you plan to use paprefs)
#load-module module-esound-protocol-tcp
#load-module module-native-protocol-tcp
#load-module module-zeroconf-publish

### Load the RTP receiver module (also configured via paprefs, see above)
#load-module module-rtp-recv

### Load the RTP sender module (also configured via paprefs, see above)
#load-module module-null-sink sink_name=rtp format=s16be channels=2 rate=44100 sink_properties="device.description='RTP Multicast Sink'"
#load-module module-rtp-send source=rtp.monitor

### Load additional modules from GSettings. This can be configured with the paprefs tool.
### Please keep in mind that the modules configured by paprefs might conflict with manually
### loaded modules.
.ifexists module-gsettings.so
.nofail
load-module module-gsettings
.fail
.endif


### Automatically restore the default sink/source when changed by the user
### during runtime
### NOTE: This should be loaded as early as possible so that subsequent modules
### that look up the default sink/source get the right value
load-module module-default-device-restore

### Automatically move streams to the default sink if the sink they are
### connected to dies, similar for sources
load-module module-rescue-streams

### Make sure we always have a sink around, even if it is a null sink.
load-module module-always-sink

### Honour intended role device property
load-module module-intended-roles

### Automatically suspend sinks/sources that become idle for too long
load-module module-suspend-on-idle

### If autoexit on idle is enabled we want to make sure we only quit
### when no local session needs us anymore.
.ifexists module-console-kit.so
load-module module-console-kit
.endif
.ifexists module-systemd-login.so
load-module module-systemd-login
.endif

### Enable positioned event sounds
load-module module-position-event-sounds

### Cork music/video streams when a phone stream is active
load-module module-role-cork

### Modules to allow autoloading of filters (such as echo cancellation)
### on demand. module-filter-heuristics tries to determine what filters
### make sense, and module-filter-apply does the heavy-lifting of
### loading modules and rerouting streams.
load-module module-filter-heuristics
load-module module-filter-apply

### Make some devices default
#set-default-sink output
#set-default-source input

Forum kindly sponsored by