Wednesday, February 16, 2022

[FAIL] ALSA, JACK, 2 x cards, no pulseaudio

Basically, it doesn't matter what a post is originally about. If it includes any sound portion, it will end up being pages long attempting to deal with PulseAudio -- attempts to keep it out, to control or limit it, or to circumvent it. Unless you are a retiree with at least a full year's free time, install PulseAudio. The three biggest failures which lead to a full PulseAudio install:

  1. pa_context_connect() calls from an application. These require a full PulseAudio install, not just libpulses.
  2. ALSA developer's decision to add runtime calls (in C++?) into the libalsa binary. OK within itself, but falls apart when it accesses the configuration file /usr/share/alsa/alsa.config. The configuration used to be a plain text file. Now it has to include named variables to respond to libalsa's runtime calls. Documentation is poor and would require a year of full-time work by a user to reverse engineer.
  3. multiple sound cards. If we still had a purely text ALSA config, without runtime calls, might be OK. But with runtime added, an inflexible default must be specified. This limits access to functionality from the other cards needed for eg, HDMI/ELD.

ALSA issues

Can we shrink our ALSA configuration to make it simpler to overlay JACK? Let's try some changes. To restart ALSA after changes....
# systemctl restart alsa-restore.service

1. capture (recording)

$ arecord -l
**** List of CAPTURE Hardware Devices ****
card 0: PCH [HDA Intel PCH], device 0: ALC892 Analog [ALC892 Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0
card 0: PCH [HDA Intel PCH], device 2: ALC892 Alt Analog [ALC892 Alt Analog]
Subdevices: 1/1
Subdevice #0: subdevice #0

*using alsa and jack over pulseaudio (10:42) Nomad Studio, 2016. About 8:00 describes using apulse on PulseAudio apps.


2. cards

$ cat /proc/asound/cards
0 [PCH ]: HDA-Intel - HDA Intel PCH
HDA Intel PCH at 0xf7f30000 irq 40
1 [HDMI ]: HDA-Intel - HDA ATI HDMI
HDA ATI HDMI at 0xf7e60000 irq 41

2. devices

Card 0 w/ 4 devices, Card 1 w/5 HDMI output devices...
$ ls -l /dev/snd
total 0
drwxr-xr-x 2 root root 80 Feb 15 17:46 by-path
crw-rw----+ 1 root audio 116, 14 Feb 15 17:46 controlC0
crw-rw----+ 1 root audio 116, 8 Feb 15 17:46 controlC1
crw-rw----+ 1 root audio 116, 13 Feb 15 17:46 hwC0D0
crw-rw----+ 1 root audio 116, 7 Feb 15 17:46 hwC1D0
crw-rw----+ 1 root audio 116, 10 Feb 15 17:46 pcmC0D0c
crw-rw----+ 1 root audio 116, 9 Feb 15 17:46 pcmC0D0p
crw-rw----+ 1 root audio 116, 11 Feb 15 17:46 pcmC0D1p
crw-rw----+ 1 root audio 116, 12 Feb 15 17:46 pcmC0D2c
crw-rw----+ 1 root audio 116, 6 Feb 15 17:46 pcmC1D10p
crw-rw----+ 1 root audio 116, 2 Feb 15 17:46 pcmC1D3p
crw-rw----+ 1 root audio 116, 3 Feb 15 18:23 pcmC1D7p
crw-rw----+ 1 root audio 116, 4 Feb 15 17:46 pcmC1D8p
crw-rw----+ 1 root audio 116, 5 Feb 16 03:40 pcmC1D9p
crw-rw----+ 1 root audio 116, 1 Feb 15 17:48 seq
crw-rw----+ 1 root audio 116, 33 Feb 15 17:46 timer

3. modules

$ lsmod |grep snd
snd_seq_dummy 16384 0
snd_seq 90112 1 snd_seq_dummy
snd_seq_device 16384 1 snd_seq
snd_hda_codec_realtek 163840 1
snd_hda_codec_generic 98304 1 snd_hda_codec_realtek
snd_hda_codec_hdmi 81920 1
ledtrig_audio 16384 1 snd_hda_codec_generic
snd_hda_intel 61440 0
snd_intel_dspcfg 32768 1 snd_hda_intel
snd_intel_sdw_acpi 20480 1 snd_intel_dspcfg
snd_hda_codec 180224 4 snd_hda_codec_generic,snd_hda_codec_hdmi,snd_hda_intel,snd_hda_codec_realtek
snd_hda_core 114688 5 snd_hda_codec_generic,snd_hda_codec_hdmi,snd_hda_intel,snd_hda_codec,snd_hda_codec_realtek
snd_hwdep 16384 1 snd_hda_codec
snd_pcm 163840 4 snd_hda_codec_hdmi,snd_hda_intel,snd_hda_codec,snd_hda_core
snd_timer 45056 2 snd_seq,snd_pcm
snd 126976 10 snd_hda_codec_generic,snd_seq,snd_seq_device,snd_hda_codec_hdmi,snd_hwdep,snd_hda_intel,snd_hda_codec,snd_hda_codec_realtek,snd_timer,snd_pcm
soundcore 16384 1 snd

default problems

Soundcard 0 has analog capture (mic) and playback (speaker, headpone) channels. Card 1 only plays back but it's a necessary card because has digital(HDMI) connection. So, I must capture through 0 and playback through 1. But how?
  • HDMI playback only works when Card 1 is the default ALSA card
  • Recording only works when Card 0 is the default ALSA card
  • Unable to dynamically change default card(s) using amixer (cset cget)
  • ALSA now includes run-time programming language in its config file (alsa.conf). Learning the ALSA library variables and commands necessary for config file (one year's work)
  • OBS detects audio devices only via JACK or PulseAudio, not ALSA

HDMI playback verfied

$ aplay -D plughw:1,9 /usr/share/sounds/alsa/Front_Center.wav

5. current sound configuration

All configuration files deleted except /usr/share/alsa/alsa.conf. I have an "audio" group with myself as a member in /etc/group.

hardware and ELD HDMI detection

Changing ALSA configurations has no effect on items 1-3 above or the ELD/HDMI connection. ALSA is now installed with the kernel as systemd modules -- its functions are upstream of our user-level configuration. ELD handshakes automatically if the HDMI cable is connected and both items are powered-on. ELD verification is simple: find the device with the "1":

$ grep eld_valid /proc/asound/HDMI/eld*

complete alsa.conf

Only one configuration file is necessary to configure ALSA at the user level, /usr/share/alsa/alsa.conf; all other configuration files may be deleted. However a complete alsa.conf is roughly 600 lines and has eight sections; HOOKS, DEFAULTS, PCM, CONTROL, RAW MIDI, SEQUENCER, HW DEPENDENCE, and TIMER. Furthermore, in 2022 these include run-time function calls to the libs which have a somewhat elaborate syntax. I find the 2022 version a kludge.

smallest alsa.conf

What then is the smallest ALSA configuration? I've gotten ALSA working normally with only about 120 lines: a DEFALUTS, small PCM (below), and CONTROL interface sections.

#
# ALSA library configuration file
# permissions are 644

# PCM interface

pcm.capture1 {
type hw
card 0
device 0
}
pcm.capture2 {
type hw
card 0
device 2
}
pcm.hdmi8 {
type hw
card 1
device 8
}
With this I could, eg, record with explicit commands.
]$ arecord -D capture1 test.wav
However, since ALSA installs muted, this wasn't enough for me. I want alsamixer so I can easily unmute, set levels, select outputs. After a few days' work, in order not to deconstruct every run-time function for a customized version, I reluctantly settled on the original 600 line configuration file. For now.

alsa.conf sites

The alsa.conf is fun and time consuming to play with, and I might do it again. Documentation was sparse however. I gleaned from several sites.

Again, we want a JACK connectioin to Card 0, but not make it the system default. Alternatively, we might, if possible make Card 0 the default, but configure card 1 as an HDMI hook.

jack install overview

First you want the smallest possible ALSA configuration. Least elaborate and simple. A default card is required for JACK can be pared down and the problems are few, some due to PAM's typical interference. Capture is more complicated than playback, so we can test our permissions first on card 1. Once we get a basic configuration, we'll get card 1 going then tweak the more finnicky capture settings until card 0 is able to detect and input a mic.

jack install

I typically install jack2, alsa-plugins, jack-example-tools for multi-card, libffado (less error messages), and qjackctl for a GUI.

basic setup

  1. verify there's an "audio" group and the user is in it. Jump to Step 2. Otherwise, create the group "audio" and add the user
    # groupadd audio
    # usermod -a -G audio user
  2. modify PAM memory permissions to group "audio" in /etc/security/limits.conf, for real-time audio processing.
    # nano /etc/security/limits.conf
    @audio - rtprio 95
    @audio - memlock unlimited
    Verify: logout, log back in, and then...
    $ ulimit -r
    ...should see "95"
  3. determine location of $ qjackctl called on command line.
    $ strace qjackctl
    You might have to save it to a file since so much crap will scroll.
    $ strace qjackctl 2>&1 | tee file.txt
    We only care about the first line in the tens of thousands of lines in the file.
    execve("/usr/bin/qjackctl", ["qjackctl"]....
  4. give audio group permissions to qjackctl and jackd2 and make it group executable (750).
    # chgrp audio /usr/bin/qjackctl
    chmod 750 /usr/bin/qjackctl
    chgrp audio /usr/bin/jackd
    chmod 750 /usr/bin/jackd

first verification - playback only (card 1)

$ jackd -d alsa -d hw:1
jackdmp 1.9.20
Copyright 2001-2005 Paul Davis and others.
Copyright 2004-2016 Grame.
Copyright 2016-2021 Filipe Coelho.
jackdmp comes with ABSOLUTELY NO WARRANTY
This is free software, and you are welcome to redistribute it
under certain conditions; see the file COPYING for details
jack_get_descriptor : dll
jack_get_descriptor returns null for 'jack_internal_metro.so'
no message buffer overruns
no message buffer overruns
jack_get_descriptor : dll
jack_get_descriptor returns null for 'jack_inprocess.so'
jack_get_descriptor : dll
jack_get_descriptor returns null for 'jack_intime.so'
no message buffer overruns
JACK server starting in realtime mode with priority 10
self-connect-mode is "Don't restrict self connect requests"
audio_reservation_init
Acquire audio card Audio1
creating alsa driver ... hw:1|hw:1|1024|2|48000|0|0|nomon|swmeter|-|32bit
ALSA: Cannot open PCM device alsa_pcm for capture. Falling back to playback-only mode
configuring for 48000Hz, period = 1024 frames (21.3 ms), buffer = 2 periods
ALSA: final selected sample format for playback: 32bit integer little-endian
ALSA: use 2 periods for playback

So JACK starts for playback, albeit with some probable PulseAudio garbage errors. However, the error message, "Cannot open PCM device alsa_pcm for capture" will cause a fatal error when attempting to capture, using Card0 next paragraph. What is alsa_pcm and where do we configure alsa_pcm to open for capture? These are likley to be tweaks inside of /usr/share/alsa/alsa.conf. If so, not trivial.

initial capture attempt (card 0)

We need to capture microphone, which can only be done on Card 0.
$ jackd -d alsa -d hw:0
jackdmp 1.9.20
[snip]
Acquire audio card Audio0
creating alsa driver ... hw:0|hw:0|1024|2|48000|0|0|nomon|swmeter|-|32bit
ALSA: Cannot open PCM device alsa_pcm for playback. Falling back to capture-only mode
Released audio card Audio0
audio_reservation_finish
Cannot initialize driver
JackServer::Open failed with -1
Failed to open server

Fail. PCM device alsa_pcm has no playback -- no problem, we don't need playback with Card 0. However, we also receive a capture-only mode failure. Jackd exits. This is large problem: we need Card 0 for mic input capture. So two things are different, we're on Card 0 and we need capture.

alsa.conf

Card 1 was detected, possibly because Card 1 is the default card inside alsa.conf. We want to leave the default configuration on Card 1 (needed for HDMI output), but specify Card 0 for JACK detection, capture only. Here is the relevant Card 1 information for HDMI/ELD detection.

defaults.ctl.card 1
defaults.pcm.card 1
defaults.pcm.device 9
defaults.pcm.subdevice -1
defaults.pcm.nonblock 1
### start - custom for JACK pcm.jack1 { type hw card 0 device 0 } pcm.jack2 { type hw card 0 device 2 } ### end - custom for JACK
pcm.plug0 {
type plug
slave {pcm "jack" }
}
}
pcm.jack {
type jack
slave { pcm "jack" }
playback_ports {
0 alsa_pcm:playback_1
1 alsa_pcm:playback_2
}
capture_ports {
0 alsa_pcm:capture_1
1 alsa_pcm:capture_2
}

No comments: