Here is how I accomplish this using pulseaudio. Basically what we need to do is create 2 new sinks and one new source. I'm going to name them as follows:
- to_obs will be a new sink that goes to OBS and is recorded in OBS, but where the sound is not sent upstream. This will be used for audio from your videoconference, which you don't want to send back upstream. It will also go to your speakers.
- to_obsmon will go to OBS to be recorded and sent back upstream. You would use this, for example, if you were were playing a video for people in your zoom chat, and want the audio both recorded and sent upstream.
- from_obs will be a source for sound coming out of OBS.
The complication here is that the only way to get sound out of OBS is through the monitor device, and there is only one. So while in an ideal world you would have two audio output streams from OBS, send one to zoom and both to your speakers, we are going to have to approach this differently. Instead, we'll have pulseaudio direct both to_obs and to_obsmon to your speakers, and will send the audio output of OBS just to zoom, not to any real devices.
So then this is what you need to put in
~/.config/pulse/default.pa. Note that to test this out, you can paste these into pacmd if you want to test things out. Also a reminder that to kill pulseaudio, you can run pulseaudio -k, and then it will re-read your new default.pa when it restarts (which may be automatic for many configurations). In my example, my real microphone is called nt5 and my speakers are called something complicated, so I'll just call them
speakers here for simplicity. You'll have to edit accordingly.
Code:
load-module module-virtual-sink master=speakers use_volume_sharing=no sink_name=to_obs sink_properties=device.description=To_OBS
load-module module-virtual-sink master=speakers use_volume_sharing=no sink_name=to_obsmon sink_properties=device.description="To_OBS\ (Monitored)"
load-module module-null-sink sink_name=from_obs sink_properties=device.description="OBS\ monitor\ sink"
load-module module-virtual-source source_name=from_obs master=from_obs.monitor source_properties=device.description=From_OBS
load-module module-echo-cancel aec_method=webrtc source_name=aec_mic source_master=nt5 sink_name=to_obsaec sink_master=to_obs use_master_format="true" aec_args="analog_gain_control=0\ digital_gain_control=1"
Okay, let me break down what is going on here. In the first two lines, we create "virtual sinks." A virtual sink is a sink that just passes audio along to another sink. Why is it useful? Because each sink has its own monitor. So anything you play to
to_obs
or
to_obsmon
will just come out of your speakers (because of
maseter=speakers, which you will have to replace with the real name of your speakers from the output of
pactl list short sinks
). But, it will be possible for OBS to distinguish what you've sent to
to_obs from what you've sent to
to_obsmon.
Next, we create a third sink called
from_obs. This is a null sink, meaning pulseaudio will just discard the audio played to that sink. Again, the reason this is useful is that the null sink has a monitor source. Hence, even though the audio will not go to any real audio device, we want zoom to send the
from_obs.monitor audio back upstream. The slight problem is that zoom will not give you an option to use a monitor source, because zoom thinks you want to use a real microphone, which should look like a hardware audio device. So this time we solve the problem using a virtual source, which, you guessed it, just copies data from some other source. We create a source called
from_obs that just copies audio from the monitor of the
from_obs sink, called
from_obs.monitor. (Yes, there is both a source and a sink called
from_obs.) Zoom will happily let you select the virtual
from_obs source even while it hides
from_obs.monitor.
Now you have to configure OBS. Under Settings -> Audio, you should configure two desktop audio devices. The primary one will be "To_OBS", and the second one will be "To_OBS (Monitored)" (which are the descriptions of
to_obs and
to_obsmon respectively). Also in the Audio settings, under Advanced set the monitoring device to "Monitor of OBS monitor sink." Next, in the audio mixer panel, click the gear for any audio source and go to "Advanced Audio Properties". Make sure that "Desktop audio" is set to "Monitor Off," while "Desktop Audio 2" is set to "Monitor and Output." Finally, on your microphone source, you will probably want to click filters and add a Noise Suppression filter (RNNoise). I also add a compressor to mine. Then in the Zoom Audio settings, set "Suppress background noise" to "Low," because now OBS is doing your noise suppression.
So in my experience what I've described so far is good enough to work with zoom, but isn't quite good enough for webrtc-based conferencing systems like google meet. In these systems, people complain that they hear themselves echo, presumably because OBS is inserting delay that messes with the browser's echo cancellation. So if you have that problem, you will need one more trick, which is to do the echo cancellation inside pulseaudio. That's the purpose of the final line in the
default.pa
snippet above. The way echo cancellation works in pulseaudio is that you have to feed audio through in both directions--it's essentially a virtual source and a virtual sink in one.
Hence, to make use of echo cancellation you need to do two things. First, instead of sending your zoom audio to "To_OBS", you'll want to sent it to "To_OBS (echo cancelled with NT5)" (recall that NT5 is the name of my microphone--it will be something else for you). Second, instead of selecting NT5 as your microphone, you will want to select "NT5 (echo cancelled with To_OBS)". With this, there is only one tiny problem left. Sending your zoom audio to the echo-cancel sink "To_OBS (echo cancelled with NT5)" has no disadvantages--you can just do that all the time. However, there will be a slight degradation in audio quality if you use an echo cancelled microphone. So what you really want to do is send the echo cancelled microphone upstream, while recording your real microphone in OBS.
To record your raw microphone while sending echo-cancelled audio upstream, you need to go into OBS Settings -> Audio and select two microphone devices. For the main mic, choose the echo cancelled source. For Mic/Auxiliary Audio 2, select your raw mic device. Now once again to to Advanced Audio Properties and click "Monitor and Output" (or "Monitor only") for the main Mic (which is echo cancelled). Deselect all the track recordings so you don't record this. Then for Mic/Aux 2 (the non-echo cancelled one), make sure it is set to "Monitor Off" and that recording is selected.