Look up how to make a loopback with the audio system that you have. I use Ubuntu Studio 22.04 LTS, which is the last if I remember right, to not use PipeWire by default. So I have a script that sets up a bunch of loopbacks/bridges in PulseAudio, one end of which connects to JACK.
(
pactl load-module module-jack-sink ...
or
pactl load-module module-jack-source ...
, depending on which direction it's going)
The DAW (Ardour for me, which comes preinstalled in UStudio and "just works", along with a TON of good plugins!) then connects to the JACK side of those loopbacks just like any other device, and OBS and everything else connects to the PulseAudio side of the same loopbacks just like any other device.
Bash:
#!/bin/bash
AUDIO_CHECK="$1"
shift
AUDIO_SETUP="$1"
shift
AUDIO_TEARDOWN="$1"
shift
echo
"$AUDIO_CHECK"
RESULT=$?
if [[ "$RESULT" != "0" ]]
then
exit $RESULT
fi
echo
echo "Start Jack"
qjackctl &
PID_JACK=$!
sleep 5
echo
echo "Create Bridges to/from PulseAudio"
INDEX_SINK_0=$( pactl load-module module-jack-sink channels=2 sink_name=PA_out_Playback client_name=PA_out_Playback )
INDEX_SINK_1=$( pactl load-module module-jack-sink channels=2 sink_name=PA_out_Meet_Rtrn client_name=PA_out_Meet_Rtrn )
INDEX_SOURCE_0=$( pactl load-module module-jack-source channels=2 source_name=PA_in_Mics client_name=PA_in_Mics )
INDEX_SOURCE_1=$( pactl load-module module-jack-source channels=2 source_name=PA_in_Meet_Send client_name=PA_in_Meet_Send )
INDEX_SOURCE_2=$( pactl load-module module-jack-source channels=2 source_name=PA_in_Record client_name=PA_in_Record )
disconnect_all ()
{
for CONNECTION in $(jack_lsp --connections "$1")
do
if [[ "$CONNECTION" = "$1" ]]
then
continue
fi
jack_disconnect "$1" "$CONNECTION"
done
}
disconnect_all system:capture_1
disconnect_all system:capture_2
disconnect_all system:playback_1
disconnect_all system:playback_2
PA_DEFAULT_SINK=$( pactl get-default-sink )
PA_DEFAULT_SOURCE=$( pactl get-default-source )
pactl set-default-sink PA_out_Playback
pactl set-default-source PA_in_Mics
echo
"$AUDIO_SETUP" "/tmp/pid_audio"
PID_AUDIO=$(<"/tmp/pid_audio")
rm "/tmp/pid_audio"
echo
"$AUDIO_TEARDOWN" $PID_AUDIO
echo
echo "Unload Bridges between Jack and PulseAudio"
pactl set-default-sink "$PA_DEFAULT_SINK"
pactl set-default-source "$PA_DEFAULT_SOURCE"
pactl unload-module $INDEX_SOURCE_2
pactl unload-module $INDEX_SOURCE_1
pactl unload-module $INDEX_SOURCE_0
pactl unload-module $INDEX_SINK_1
pactl unload-module $INDEX_SINK_0
echo
echo "Stop Jack"
kill -TERM "$PID_JACK"
sleep 1
AUDIO_CHECK
,
AUDIO_SETUP
, and
AUDIO_TEARDOWN
are other scripts. Their filenames/paths are passed as arguments to the main script above, so I can use the same structure with different physical mics and speakers. As an example of how they work:
Audio Check:
Bash:
#!/bin/bash
MIC0=hw:CARD=PowerConf,DEV=0
SPK0=hw:CARD=NVidia,DEV=3
echo
echo "Looking for MIC0: $MIC0"
until arecord -L | grep "$MIC0" > /dev/null
do
sleep 1
done
echo "Found MIC0: $MIC0"
echo "Looking for SPK0: $SPK0"
until aplay -L | grep $SPK0 > /dev/null
do
sleep 1
done
echo "Found SPK0: $SPK0"
Audio Setup:
Bash:
#!/bin/bash
MIC0=hw:CARD=PowerConf,DEV=0
SPK0=hw:CARD=NVidia,DEV=3
echo "Use MIC0: $MIC0"
zita-a2j -j MIC0 -d $MIC0 -c 1 -r 16000 &
echo $! >> "$1"
sleep 1
echo "Use SPK0: $SPK0"
zita-j2a -j SPK0 -d $SPK0 -c 2 -r 48000 -p 4096 &
echo $! >> "$1"
sleep 1
The commented-out code is from trying to use several different mics scattered around. Never completely got that to work, and it turned out I didn't need them anyway. But I kept it there so you can see the option.
Audio Teardown:
Bash:
#!/bin/bash
echo "Unuse MIC, SPK"
until [[ "$1" = "" ]]
do
kill -TERM "$1"
shift
sleep 1
done