PulseAudio allows you to set names for audio inputs and outputs using pacmd update-sink-proplist, but this only works if the device is present when the rules are run. This works perfectly fine for renaming the built-in sound devices, but doesn't work for my new USB DAC. If the DAC isn't connected when I log in, I end up with no audio.
I was able to find that PulseAudio does in fact read some variables that are set by udev, which means we can apply a name when the device is connected! I'll explain both, as both have their uses in my situation.
Primer
PulseAudio uses the following terminology:
- Card : This refers to the "DAC" itself. This is the "chip" or USB / PCI etc. interface that Linux sees and shuffles audio samples to / from.
- View these with pacmd list-cards
- Sink: This refers to the outputs (speakers) of the Card. One card can have zero to many sinks. Some sinks may not be available for some reason (e.g. headphones not plugged in, screen not active). Some cards may have one sink and switch it based on a hardware switch rather than providing the sinks separately to the OS.
- View these with pacmd list-sinks
- Source: This is the inputs (microphones) to the Card. Like a Sink, your card can have none or many, and they are not always available.
- View these with pacmd list-sources
- Port: Each source or sink can have multiple ports. In the situations I've seen, it's usually an alternate port connected to the same section of the DAC, and not fully independent from each other. In the USB DACs I've seen, they typically expose an "analog-output: Analog Output" and a "iec958-stereo-output: Digital Output (S/PDIF)". Both "ports" ended up to the same speakers. In another case, my GPU exposes three "outputs", one for each possible monitor.
- Sink Input: This is what application(s) are "sending" (playing) the audio to the output. This would be your music, system sounds videos etc.
- View these with pacmd list-sink-inputs
- Source Output: This is what application(s) are "receiving" (recording) the audio from the input. This would be a recording application, and your audio to the others in a video call.
- View these with pacmd list-source-outputs
- Monitor of ...: This is a "source" that loops back to a sink. This allows you to capture your screen and stream it. You'd see this in a audio input selection prompt.
As an example, here's how it looks for my system without any extra cards attached:
pacmd list-sinks 1 sink(s) available. * index: 0 name: <alsa_output.pci-0000_07_00.6.HiFi__hw_Generic_1__sink> driver: <module-alsa-card.c> flags: HARDWARE HW_MUTE_CTRL HW_VOLUME_CTRL DECIBEL_VOLUME LATENCY state: IDLE suspend cause: (none) priority: 9032 volume: front-left: 10690 / 16% / -47.25 dB, front-right: 10690 / 16% / -47.25 dB balance 0.00 base volume: 65536 / 100% / 0.00 dB volume steps: 65537 muted: no current latency: 80.64 ms max request: 30 KiB max rewind: 30 KiB monitor source: 0 sample spec: s32le 2ch 48000Hz channel map: front-left,front-right Stereo used by: 0 linked by: 1 fixed latency: 80.00 ms card: 1 <alsa_card.pci-0000_07_00.6> module: 8 properties: alsa.resolution_bits = "32" device.api = "alsa" device.class = "sound" alsa.class = "generic" alsa.subclass = "generic-mix" alsa.name = "ALC257 Analog" alsa.id = "ALC257 Analog" alsa.subdevice = "0" alsa.subdevice_name = "subdevice #0" alsa.device = "0" alsa.card = "1" alsa.card_name = "HD-Audio Generic" alsa.long_card_name = "HD-Audio Generic at 0xfd3c0000 irq 124" alsa.driver_name = "snd_hda_intel" device.bus_path = "pci-0000:07:00.6" sysfs.path = "/devices/pci0000:00/0000:00:08.1/0000:07:00.6/sound/card1" device.bus = "pci" device.vendor.id = "1022" device.vendor.name = "Advanced Micro Devices, Inc. [AMD]" device.product.id = "15e3" device.product.name = "Family 17h (Models 10h-1fh) HD Audio Controller" device.description = "Laptop" device.string = "hw:Generic_1" device.buffering.buffer_size = "30720" device.buffering.fragment_size = "3840" device.access_mode = "mmap" device.profile.name = "HiFi: hw:Generic_1: sink" device.profile.description = "Speaker + Headphones" alsa.mixer_device = "hw:Generic_1" module-udev-detect.discovered = "1" device.icon_name = "audio-card-pci" ports: [Out] Speaker: Speaker (priority 100, latency offset 0 usec, available: unknown) properties: [Out] Headphones: Headphones (priority 200, latency offset 0 usec, available: no) properties: active port: <[Out] Speaker>
The pacmd output above essentially shows you all you need to know about that sink.
We're able to update some of these properties above and that's how we can make this card more "presentable"
In most cases source and sink are interchangeable in command names, assuming you're also updating the device / target it's applying to.
The pacmd way
As mentioned, this method only works for devices that will always be present. Thankfully that works perfectly fine to rename the internal sound cards.
In this particular situation, I want to rename the audio card, and both of it's inputs. As it stands, I get to chose between:
- Family 17h (Models 10h-1fh) HD Audio Controller - Digital Microphone
- Family 17h (Models 10h-1fh) HD Audio Controller - Headphones Stereo Microphone
- Monitor of (Every possible output)
When you attempt to use a microphone in Firefox, it only gives you a narrow drop down menu to pick from, so it's quite difficult to get it right the first time.
I want to rename the card to Laptop, and then the inputs to "Laptop Microphone" and "Laptop Headphones".
The pacmd commands for that is:
pacmd 'update-sink-proplist alsa_output.pci-0000_07_00.6.HiFi__hw_Generic_1__sink device.description="Laptop"' pacmd 'update-source-proplist alsa_input.pci-0000_07_00.6.HiFi__hw_acp__source device.product.name="Laptop" device.description="Laptop Microphone"
These commands will only work for my machine, and systems very similar to it. You will need to grab the name: <alsa_output.pci-0000_07_00.6.HiFi__hw_Generic_1__sink> from the particular card you want and strip the angle brackets.
The single quotes are important as otherwise pacmd breaks if there is a space in the name / description.
If you run that in a terminal as your user account, it should take effect immediately, but some applications take some time or need to be restarted.
To have this apply automatically, we need to "override" the default.pa script file that is used when PulseAudio starts up. PulseAudio will use ~/.config/pulse/default.pa if it's present, otherwise it will use /etc/pulse/default.pa. You can copy the system config into the user folder, but instead we can import the system config and then add our additions afterwards:
- mkdir -p ~/.config/pulse
- nano ~/.config/pulse/default.pa
.include /etc/pulse/default.pa update-source-proplist << source name>> device.product.name="Card Name" device.description="Input Name" update-source-proplist << source name>> device.product.name="Card Name" device.description="Other Input Name"
Note that we're only putting the contents of the single quotes above into this file, as it is a "PulseAudio" script file, and is interpreted by pacmd itself.
To apply these changes without logging out and back in, or restarting, either run pulseaudio --kill or systemctl --user restart pulseaudio.service.
Finally, to verify that it's working correctly, you can use the pacmd list-** commands, or use pavucontrol:
The udev way
Now unfortunately PulseAudio will spit it's chips if you have a command in your default.pa script referencing a card that's not there. Instead, we can apply a limited set of properties using udev, which will apply them when the device is first seen by the system.
At this stage, it looks like it's only possible to set the name of the "card", rather than the specific sinks or sources.
For USB devices:
(USB-C dock audio, external DACs etc.)
- Connect your device
- Use lsusb to find the USB Vendor and Product ID of the device (something like 17ef:30d1)
- Create a new (or use an existing) udev rule - sudo nano /etc/udev/rules.d/90-pulse-ids.rules and paste in the following:
- ATTRS{idVendor}=="17ef", ATTRS{idProduct}=="30d1", ENV{SOUND_DESCRIPTION}="Dock Headphones"
- Replace the values of idVendor, idProduct, and the description as desired
- Apply these new settings:
- sudo udevadm control --reload && sudo udevadm trigger && systemctl --user restart pulseaudio.service
For PCI devices:
(e.g. "default" laptop audio, audio through the GPU, things on Thunderbolt that don't show up in USB)
- Connect your device (preferably while the machine is off, unless it supports hot-swap)
- Identify your device's sysfs path:
- From the output of pacmd list-cards, grab the properties -> sysfs.path, it should look like /devices/pci0000:00/0000:00:08.1/0000:07:00.6/sound/card1
- Add /sys to the front of it
- Get a list of udev properties against the device:
- udevadm info -a -p << SYSFS PATH >>
- We want to apply settings to the PCI device that supplies the card, so we need to look at the details of the parent device. Look under the first looking at parent device ...
- We need to find IDs that uniquely identify this card, but that aren't likely to change with a reboot or update. I've grabbed what I think are PCI IDs: vendor, device, subsystem_vendor and subsystem_device.
- Create a new (or use an existing) udev rule - sudo nano /etc/udev/rules.d/90-pulse-ids.rules and paste in the following:
- ATTRS{vendor}=="0x1022", ATTRS{device}=="0x15e2", ATTRS{subsystem_vendor}=="0x17aa", ATTRS{subsystem_device}=="0x5082", ENV{SOUND_DESCRIPTION}="GPU Audio"
- Update the SOUND_DESCRIPTION to what you want the card's label to be.
- Apply these new settings:
- sudo udevadm control --reload && sudo udevadm trigger && systemctl --user restart pulseaudio.service
Resulting config
/etc/udev/rules.d/90-pulse.rules:
ATTRS{idVendor}=="04da", ATTRS{idProduct}=="23c2", ENV{SOUND_DESCRIPTION}="SA-PMX82" ATTRS{idVendor}=="17ef", ATTRS{idProduct}=="30d1", ENV{SOUND_DESCRIPTION}="Dock Headphones" ATTRS{vendor}=="0x1002", ATTRS{device}=="0x1637", ATTRS{subsystem_vendor}=="0x17aa", ATTRS{subsystem_device}=="0x5082", ENV{SOUND_DESCRIPTION}="GPU Audio" ATTRS{vendor}=="0x1022", ATTRS{device}=="0x15e3", ATTRS{subsystem_vendor}=="0x17aa", ATTRS{subsystem_device}=="0x5082", ENV{SOUND_DESCRIPTION}="Laptop Audio"
~/.config/pulse/default.pa
.include /etc/pulse/default.pa update-sink-proplist alsa_output.pci-0000_07_00.6.HiFi__hw_Generic_1__sink device.description="Laptop" update-source-proplist alsa_input.pci-0000_07_00.6.HiFi__hw_acp__source device.product.name="Laptop" device.description="Laptop Microphone" update-source-proplist alsa_input.pci-0000_07_00.6.HiFi__hw_Generic_1__source device.product.name="Laptop" device.description="Laptop Headphones"
Links
- https://jamielinux.com/blog/tell-pulseaudio-to-ignore-a-usb-device-using-udev/ - Reference for using udev to configure pulse
- https://github.com/pulseaudio/pulseaudio/blob/3349e1c471f16f46251a51acfc1740cdf012a098/src/modules/udev-util.c#L247 - PulseAudio source code indicating what options it accepts from udev rules