Backstory to my microphone adventure

🎤 For a long time I’ve been trying to set up my old Behringer C2 condenser microphone for streaming, gaming, Discord calls, etc. It has been collecting a lot of dust since my hobyy-musician days about 10 years earlier.

I wanted to use this microphone for one simple reason: I strongly prefer watching streamers with a clear voice.

No shade to streamers who don’t voicemaxx, but I find it rough listening to an unfiltered headset microphone. My Razer Barracuda-X microphone was performing quite poorly in wireless mode, and while Razer were decent enough to replace it under warranty, there was no improvement.

When I started, I had no idea about the Linux audio stack. I didn’t even know what PulseAudio was. If you’re running Ubuntu 20.04 (or Ubuntu MATE as I do) then you are likely using PulseAudio to provide applications with interfaces to your devices. You may have interacted with it via the little 🔈 on your desktop panel. PulseAudio, perhaps unfairly, has a poor reputation:

Comic strip that insults PulseAudio with blank space where text should be

Courtesy of https://knowyourmeme.com/photos/1173057-welcome-aboard.

How the TASCAM US122L fits in to the story

These condenser mics don’t just plug into a laptop. They have an XLR socket and, more to the point, they require 48V power supply called ‘phantom power’. So we’ll need an ‘audio interface’.

The fancy, small, and neat option would be a SHURE X2u. On the other hand, I had an old reliable TASCAM US122L which surely, surely, will just work. Right?

I was so young and innocent back then.

Extremely short overview of Linux audio

Here’s what I now understand about the way audio is layered in Linux (Ubuntu):

  1. Linux kernel: recognises the hardware and has a low-level API (part of ALSA), to stream audio to and from devices.
  2. ALSA: contains both the kernel-level API and a variety of further tools for setting up devices and streaming from one device to another, e.g. alsa-utils.
  3. PulseAudio: another layer on top of ALSA, which provides a (simpler) API for application developers and provides handy software mixing capabilities.

Everything starts with ALSA, then we can move on to PulseAudio (or JACK or PipeWire).

At the kernel and ALSA level it seems that the US122L was recognised, thanks to the snd-usb-us122l module which has existed for some time. We can see that the card is recognised via:

user@home:~$ cat /proc/asound/cards
...
3 [US122L         ]: USB US-122L - TASCAM US-122L
                     TASCAM US-122L (644:800e if 0 at 003/004)

Let’s make a simplifying assumption: for now we only care about PCM devices, i.e. those that deal with sound ‘wave’ data, not MIDI.

At the kernel level, PCM devices are fairly ‘directly’ controlled (my words). Beyond the kernel, ALSA provides plugins which perform audio conversion and channel mixing. This results in two types of device being emitted by ALSA:

  • ‘hardware’ PCM devices (denoted hw:#); which are, roughly speaking, the raw kernel-level devices;
  • ‘virtual’ (or non-hardware) PCM devices (e.g. plughw:#, dsnoop:#); which usually involve one or more plugins being applied before the data is passed to a specified hardware device.

So now you know, effectively, the difference between aplay --list-devices (lists hardware devices) and aplay --list-pcms (lists all).

Playback and record with the US122L in ALSA

Here is the fun part: the TASCAM US122L doesn’t emit a hw:#; so it won’t appear in aplay --list-devices but it will appear in aplay --list-pcms:

user@home:~$ aplay --list-pcms
...
usbstream:CARD=US122L
    TASCAM US-122L
    USB Stream Output

This assumes that the alsa-plugins package has been installed, which provides:

  1. Additional PCM plugins for specific devices, in particular, the usb_stream plugin; which wraps the kernel-level usb_stream PCM associated with the US122L.
  2. A configuration file which sets up the usb_stream plugin for each card (device), which will be listed as usbstream:# amongst the PCM device names.

This seems like ‘job done’ at the ALSA level, right? Well. Almost.

⚠️ Some nasty things can happen with aplay (or arecord). Not setting period_size killed keyboard input. Some values of period_size and rate led to crashes in aplay (see Ubuntu Launchpad - Bug #1960703, Kernel.org - Bug 217878, alsa-lib/issues/269, and jack1/issues/118) and the device being killed with output:

arecord: pcm_read:2221: read error: Input/output error

The best advice I can give is to use period_size equal to 256 or 512; set the buffer_size to either 2, 3, or 4 times that amount (see also the recommendations in the JACK daemon manual); and rate should be 48000 as per the native rate for the device, e.g:

user@home:~$ arecord --period-size=256 --buffer-size=512 -c2 -fS24_3LE \
    -r48000 -MDusb_stream:CARD=US122L test.wav

Now let’s assume we’re ready to move on.

Making the US122L visible to applications

Many desktop applications won’t talk to ALSA directly, instead they opt for PulseAudio, JACK (e.g. some professional audio software), and more recently PipeWire.

PulseAudio source

The simplest way to use the US122L within PulseAudio is to use the module-alsa-source. A ‘source’ is the PulseAudio-speak for an audio input that can be read from, i.e. a line-in, a microphone, and so on. Using pulseaudio-utils we can create a source from the device:

user@home:~$ sudo apt install pulseaudio-utils
user@home:~$ pactl load-module module-alsa-source device=usbstream:CARD=US122L name=foo

Similarly to the ALSA-level, it might be worth testing different configurations to ensure stability, e.g. the fragments and fragment_size. Once the settings are stable, we can add them to default.pa.

JACK capture port

JACK is an alternative to (but not a replacement for) PulseAudio, designed for low latency and studio recording settings.

We can run the JACK daemon and provide ‘capture ports’ for the device:

user@home:~$ sudo apt install jackd
user@home:~$ jackd -dalsa -Cusb_stream:CARD=US122L -n 3 -p 512 -r 48000

Here we’ve used the recommended 3 buffered frames for a USB device (see JACK daemon manual) and a period size of 512. A ‘capture port’ is JACK-speak for an audio input that can be read from, i.e. a ‘source’ in PulseAudio-speak, and so on.

If you want the device to be available to applications that use PulseAudio, then PulseAudio’s module-jack-source might be a starting point.

Ultimately, however, I ditched JACK in favour of PipeWire.

PipeWire - the saviour of the US122L?

While I was chatting on stream, someone mentioned another alternative to JACK and PulseAudio, called PipeWire. It has much better reviews than PulseAudio, and is actively developed.

PipeWire provides a PulseAudio server, too, and it also has implementations of PulseAudio modules, in particular for our purposes, module-alsa-source. Thus our simplest approach is to use this module to create a source from the US122L. For that, we can create a file ~/.config/pipewire/pipewire-pulse.conf.d/pactl-tascam.conf with the following contents:

pulse.cmd = [
    { cmd = "load-module"
      args = "module-alsa-source source_name=foo device=usbstream:CARD=US122L fragments=3 fragment_size=512"
      flags = [ ] }
]

See https://docs.pipewire.org/page_man_pipewire-pulse-modules_7.html for further details about the modules.

There isn’t, yet, a ’native’ solution in PipeWire. An attempt has been made to use udev rules and an ALSA card profile set, but it has yet to come together. For all the false starts and incremental progress made, see pipewire/issues/3012.

Epilogue

I have since sold the TASCAM US122L, and I am now using a Behringer UMC202HD. It has been much easier to work with, and I have had no problems with stability in OBS. See Adding effects to enhance microphones with PipeWire for how I’ve since improved my audio.