[tux with headphones]

Controlling PulseAudio from the Command Line

Controlling PulseAudio via pavucontrol is all very nice, but it's time consuming and fiddly: you have to do a lot of clicking in a lot of tabs any time you want to change anything.

I wanted quick ways to do a few things I do a lot: turn all microphones off (this is a big one!); switch to the external speakers so I can listen to music; send audio output to HDMI if I'm giving a public presentation. In none of these cases do I want to spend several minutes fiddling with pavucontrol. Fortunately, PulseAudio does have ways you can make changes from the command line. All it lacks is documentation.

Note: I now have a script, pulsehelper.py that uses the commands below to provide an easier interface.

Most of this is accomplished through two commands, pacmd and pactl. I haven't figured out a rule for when to use one vs the other; they seem to fulfill a lot of the same functions and I'm not sure why there are two of them.

If you normally use an 80-column wide terminal, you may want to stretch it wider while you're testing PA commands.

Listing current status

List sound cards
pactl list short cards
List everything
pacmd list-cards
This gives a very long list, but there's a lot of useful information there.

Volume

Pulseaudio has different volume levels for each sink. You can list those with:

pactl list sinks | grep -e Name: -e Volume:

But that isn't enough, because Pulse maintains a separate sink and a separate volume for each application. You can get a verbose list of running programs that are producing sound this way:

pactl list sink-inputs  | grep -e Sink: -e media.name -e application.name -e Volume:

The sink is only listed by number; it corresponds to the sink id you get from

pacmd list-sinks | grep -e name: -e index -e description -e muted -e volume:

Changing things

Enabling/Disabling Sound Cards

First, get the indices for the cards you want to enable or disable.

$ pactl list short cards
0	alsa_card.usb-0c76_USB_PnP_Audio_Device-00	module-alsa-card.c
1	alsa_card.pci-0000_00_1f.3-platform-skl_hda_dsp_generic	module-alsa-card.c

So index 0 is my the sound card built into my USB-C hub (because it's on USB); index 1 is the laptop's built-in audio (on PCI).

Next, you need the list of profiles available for each card. Just wade through the long output of pacmd list-cards, looking under profiles for each device. I don't know of any shorter way. For instance, my card 0, the USB one, has

	profiles:
		input:mono-fallback: Mono Input (priority 1, available: unknown)
		input:multichannel-input: Multichannel Input (priority 1, available: unknown)
		output:analog-stereo: Analog Stereo Output (priority 6500, available: unknown)
		output:analog-stereo+input:mono-fallback: Analog Stereo Output + Mono Input (priority 6501, available: unknown)
		output:analog-stereo+input:multichannel-input: Analog Stereo Output + Multichannel Input (priority 6501, available: unknown)
		output:iec958-stereo: Digital Stereo (IEC958) Output (priority 5500, available: unknown)
		output:iec958-stereo+input:mono-fallback: Digital Stereo (IEC958) Output + Mono Input (priority 5501, available: unknown)
		output:iec958-stereo+input:multichannel-input: Digital Stereo (IEC958) Output + Multichannel Input (priority 5501, available: unknown)
		off: Off (priority 0, available: unknown)
	active profile: 

Now you can set the profile for a specific card. For instance, to enable the USB card as output only, and turn off the internal card:

pactl set-card-profile 0 output:analog-stereo
pactl set-card-profile 1 off

Setting Fallbacks (Defaults)

Let's talk about speakers first, which PulseAudio calls "sinks" (microphones are "sources"). You can list all available sinks with pacmd list-sinks if you want pages and pages of output.

To get shorter output and find out the current sink:

$ pacmd list-sinks | grep -e name: -e index -e description -e muted
    index: 1
	name: 
		device.profile.description = "HDMI3/DP3 Output"
		device.description = "Cannon Point-LP High Definition Audio Controller HDMI3/DP3 Output"
    index: 2
	name: 
		device.profile.description = "HDMI2/DP2 Output"
		device.description = "Cannon Point-LP High Definition Audio Controller HDMI2/DP2 Output"
    index: 3
	name: 
		device.profile.description = "HDMI1/DP1 Output"
		device.description = "Cannon Point-LP High Definition Audio Controller HDMI1/DP1 Output"
    index: 4
	name: 
		device.profile.description = "Speaker + Headphones"
		device.description = "Cannon Point-LP High Definition Audio Controller Speaker + Headphones"
  * index: 5
	name: 
		device.profile.description = "Digital Stereo (IEC958)"
		device.description = "USB PnP Audio Device Digital Stereo (IEC958)"

Note the asterisk next to sink 5: that's currently my fallback (default) speaker. Theoretically that means if I run anything, it'll go to my external speakers plugged in to the USB hub. In practice, that's not always true since PulseAudio has its own (secret) idea of apps that should use a different sink.

Armed with that, you can set a new default sink. My laptop's built-in speakers are index 4 (I know that because it's the only one that isn't either USB or HDMI). So:

pactl set-default-sink 4

Except -- wait, no. Setting the default sink actually does nothing, and each program continues to output to wherever it was outputting last.

If you want to be able to change the default sink from the commandline at runtime, you need to edit /etc/pulse/default.pa, find the line with module-stream-restore, and add "=false" at the end:

load-module module-stream-restore restore_device=false

You can also set other options, like restore_volume=false: see the module-stream-restore documentation.

You'll need to restart PulseAudio, or reboot, but after that you may be able to change the default sink. Changing the default source still doesn't work for me, though; pactl set-default-source 4 doesn't change anything. Sigh.

As long as you're editing that file, take a look at some of the other options. For instance, hotplugging a sound card sometimes causes running apps to switch to the newly plugged option. I think that's because of

load-module module-switch-on-connect
### Use hot-plugged devices like Bluetooth or USB automatically (LP: #1702794)
.ifexists module-switch-on-connect.so
load-module module-switch-on-connect
.endif
There are some other interesting prefs there too.

Good background reading on Pulse, fallbacks, and their pitfalls: Down the drain: The elusive ‘default’ PulseAudio sink. Though that article mentions an Applications tab in pavucontrol that mine doesn't have.

Of course, this was all for sinks (speakers). But sources (microphones) work the same way.

Use a specific output or input for one program

Being able to change defaults is great. But sometimes you might want to run a program and have it use a non-default device, without changing the default for everything else. You can do that with the environment variables PULSE_SINK and PULSE_SOURCE.

Suppose I have default audio going to my laptop's built-in audio, but I want to play some music, using my little musicplayer app, to the good speakers. I can do that with

PULSE_SINK='alsa_output.usb-0c76_USB_PnP_Audio_Device-00.iec958-stereo' musicplayer

Now music is playing from the good speakers, but other sounds will still go to the laptop speakers.

Mute Microphones

List all mics:

pacmd list-sources | grep -e index -e name: -e muted -e device.description

It may be a surprisingly long list. As I'm typing this, my laptop has nothing else plugged into it, just the built-in sound card, yet there are six sources:

pacmd list-sources | grep -e index -e name: -e muted -e device.description
    index: 0
	name: 
	muted: no
		device.description = "Monitor of Cannon Point-LP High Definition Audio Controller HDMI3/DP3 Output"
    index: 1
	name: 
	muted: no
		device.description = "Monitor of Cannon Point-LP High Definition Audio Controller HDMI2/DP2 Output"
    index: 2
	name: 
	muted: no
		device.description = "Monitor of Cannon Point-LP High Definition Audio Controller HDMI1/DP1 Output"
    index: 3
	name: 
	muted: no
		device.description = "Monitor of Cannon Point-LP High Definition Audio Controller Speaker + Headphones"
    index: 4
	name: 
	muted: yes
		device.description = "Cannon Point-LP High Definition Audio Controller Headphones Stereo Microphone"
  * index: 5
	name: 
	muted: yes
		device.description = "Cannon Point-LP High Definition Audio Controller Digital Microphone"

The first three of them are HDMI. I didn't even know HDMI had microphone capacity. The other three are two entries for a hypothetical headset-mic I might plug in, plus, finally, the actual built-in mic. It's muted; I muted it in pavucontrol last night before I shut down, and it looks like pulse remembered that, which is nice to know. The little LED on the F4 (mic mute) key is on, so it's good to know this laptop gives me a visible indication when the built-in mic is muted.

Notice, in the previous listing, there's an asterisk before the built-in mic, which menas it's the default source. But if you need to get the pulseaudio name for thedefault source for a script, a shorter way is:

pactl info | grep "Default Source"

Okay, you have a list of mics. How do you mute them?

pactl set-source-mute alsa_input.pci-0000_00_1f.3-platform-skl_hda_dsp_generic.HiFi__hw_sofhdadsp_6__source 1
or use 0 to unmute. You can combine that with the previous command to mute the default source:
pactl set-source-mute $(pactl info | grep "Default Source" | awk '{print $3;}') 1
and likewise for the default sink:
pactl set-sink-mute $(pactl info | grep "Default Sink" | awk '{print $3;}') 1

If you want to make sure all sources are muted,

for source in $(pacmd list-sources | grep name: | sed -e 's/.*.*//'); do               ~/src/scripts
    pactl set-source-mute $source 1
done

More on muting, plus a nice script to mute or adjust mic volume: How do you mute from the command line?

Move an already playing stream

One last thing: if you make a mistake, have something using one audio device and want to move it to another, you can do that. I don't think I'll need this much, but it's fun to know it's possible.

First, get the stream index:

pacmd list-sink-inputs

Get the index of the new sink where you want to be listening to the stream, using the instructions above. Then: pacmd move-sink-input stream_index sink_index

Clear Pulse Preferences

Pulse stores its prefs in ~/.config/pulse. The association between apps/streams and volume or device is in ~/.config/pulse/stream-volumes.tdb; Apparently the format is "trivial database" or tdb, and you can dump it with tdbdump from the tdb-tools package. Probably removing it will remove all the associations; I haven't tried that yet.