rhoulam
technologies llc

PulseAudio Virtual Microphone

December 6, 2022

audio, linux, misc

A couple quick scripts to set up a virtal microphone using PulseAudio.

Set-up Script


#!/usr/bin/env sh

# https://unix.stackexchange.com/questions/576785/redirecting-pulseaudio-sink-to-a-virtual-source

PA_MODULE_ID_DIR="/tmp/$USER-pulseaudio-vmic"
PA_DEFAULT_SINK=$(pactl get-default-sink)
PA_DEFAULT_SOURCE=$(pactl get-default-source)
mkdir -p $PA_MODULE_ID_DIR

pactl load-module module-null-sink \
    sink_name=mix-virtual-mic \
    > $PA_MODULE_ID_DIR/vmic-sink-id

pactl load-module module-combine-sink \
    sink_name=virtual-mic-sink \
    slaves=mix-virtual-mic,$PA_DEFAULT_SINK \
    > $PA_MODULE_ID_DIR/combine-sink-id

pactl load-module module-loopback \
    source=$PA_DEFAULT_SOURCE \
    sink=mix-virtual-mic \
    latency_msec=20 \
    > $PA_MODULE_ID_DIR/loopback-id

pactl load-module module-remap-source \
    source_name=virtual-mic-source \
    master=mix-virtual-mic.monitor \
    > $PA_MODULE_ID_DIR/remap-id

First, some PulseAudio terminology. A sink is an audio stream produced by a local application. Usually this means it’s destined for the user’s ears. So, intuitively, a sink is something the user can listen to. A source is an audio stream coming in from somewhere external, such as a microphone. Usually this means the user is creating it, such as with a microphone or musical instrument. Usually, this means it’s destined for somewhere other than the user’s ears, such as a recording application or a networked communication application.

To briefly explain,

  1. We create a virtual sink for mixing application audio and microphone audio into.
  2. Then we create a Y-splitter in the form of a combine sink, which sends application audio to both the mixing virtual sink and the default sink (speaker/headphones).
  3. Then we create a loopback from the microphone to the mixing virtual sink.
  4. Finally, we make a source out of our mixing sink using module-remap-source. This provides the final virtual microphone device.

We store the output in a temp directory because we will need it for the tear-down.

Tear-down Script


#!/usr/bin/env sh

PA_MODULE_ID_DIR="/tmp/$USER-pulseaudio-vmic"

pactl unload-module $(cat $PA_MODULE_ID_DIR/remap-id)
pactl unload-module $(cat $PA_MODULE_ID_DIR/combine-sink-id)
pactl unload-module $(cat $PA_MODULE_ID_DIR/loopback-id)
pactl unload-module $(cat $PA_MODULE_ID_DIR/vmic-sink-id)

rm -f $PA_MODULE_ID_DIR/*

Here we unload the modules we loaded in the set-up script. The files we’re reading from were created in the set-up script via input redirection; they contain the IDs of the module instances we created.

Also the order here matters, since the modules are dependent on one another. We unload them almost in reverse order. While unloading the most fundamental modules will cause the dependent modules to also be unloaded, it’s better to tear things down correctly than to make our sound server clean up after us.

Credits

This is a combination of two StackExchange answers.

saraedum’s answer

Christopher Donham’s answer