[ALSA] using default device on linux fails
I am using a x86 Linux machine, when trying to use the default device, e.g. using examples/recorder_wav.rs it fails with this output
Running `target/debug/examples/record_wav`
Default input device: default
ALSA lib confmisc.c:767:(parse_card) cannot find card '0'
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_card_driver returned error: No such file or directory
ALSA lib confmisc.c:392:(snd_func_concat) error evaluating strings
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_concat returned error: No such file or directory
ALSA lib confmisc.c:1246:(snd_func_refer) error evaluating name
ALSA lib conf.c:4528:(_snd_config_evaluate) function snd_func_refer returned error: No such file or directory
ALSA lib conf.c:5007:(snd_config_expand) Evaluate error: No such file or directory
ALSA lib pcm.c:2495:(snd_pcm_open_noupdate) Unknown PCM default
thread 'main' panicked at 'Failed to get default input format: DeviceNotAvailable', libcore/result.rs:945:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.
The enumerate example works, this is the first few lines of output:
Running `target/debug/examples/enumerate`
Default Input Device:
Some("default")
Default Output Device:
Some("default")
Devices:
1. "default:CARD=AT2020USB"
Default input stream format:
Format { channels: 2, sample_rate: SampleRate(44100), data_type: F32 }
All supported input stream formats:
1.1. SupportedFormat { channels: 1, min_sample_rate: SampleRate(4000), max_sample_rate: SampleRate(4294967295), data_type: I16 }
As a workaround changing the record example to use the first input device and not the default device (which are the same device...), solves this issue, as following:
fn main() {
// Setup the default input device and stream with the default input format.
// ORIGINAL API:
// let device = cpal::default_input_device().expect("Failed to get default input device");
// WORKAROUND:
let device = cpal::devices().nth(0).unwrap();
println!("Default input device: {}", device.name());
let format = device.default_input_format().expect("Failed to get default input format");
I'm similarly seeing a segfault when attempting the recording demo on the Pi 3B+ with multiple USB mics. Recording on the device works with other programs.
While writing #286 I noticed that the ALSA backend currently just assumes that there are "default" devices available in the backend without actually checking the API:
#[inline]
pub fn default_input_device() -> Option<Device> {
Some(Device("default".to_owned()))
}
#[inline]
pub fn default_output_device() -> Option<Device> {
Some(Device("default".to_owned()))
}
While I believe using the "default" name is the approach recommended for working with ALSA, we should actually check that it exists. Maybe a better approach would be to enumerate all devices, check if one of the devices is named "default", if so return it, otherwise return the first device that was yielded?
That is odd. What does arecord -lL list? And can you record sound using arecord without specifying the device? The existence of the name "default:CARD=AT2020USB" suggests that the name default exists as well (At least that is the case on my systems). Now I think "failing to open" is a perfectly acceptable response to the missing default device.
Falling back to the first device is completely inappropriate imho. You go from "default" to something that is poorly defined and may result in sound appearing from a source it shouldn't or not at all while it seems to be working otherwise. It only works if it is guaranteed that what should be "default" is the first device. If that guarantee is given, go for it, but otherwise erroring out is just better.
EDIT: Even for devices that do not have a corresponding friendly named device listed it still works to just use the friendly prefix. Apparently aplay and arecord use the same binary, so aplay would work just as well.
Given the trace, this file seems relevant: https://git.alsa-project.org/?p=alsa-lib.git;a=blob_plain;f=src/conf/pcm/default.conf;hb=HEAD
Looks like the defaults.pcm.card is set to 0 while this card does not actually exist. Which suggests the USB device of the OP is not card 0 even though it is the only card present. Now I am even more curious whether arecord is actually able to record.
I was able to confirm that setting defaults.pcm.card to a nonexistent card has two effects: It removes the (sys)default device hints and it causes aplay to throw the exact same trace.
Encountered this issue today.
arecord -lL shows the following:
null
Discard all samples (playback) or generate zero samples (capture)
pipewire
PipeWire Sound Server
sysdefault:CARD=sofhdadsp
sof-hda-dsp,
Default Audio Device
**** List of CAPTURE Hardware Devices ****
card 0: sofhdadsp [sof-hda-dsp], device 0: HDA Analog (*) []
Subdevices: 0/1
Subdevice #0: subdevice #0
card 0: sofhdadsp [sof-hda-dsp], device 6: DMIC (*) []
Subdevices: 0/1
Subdevice #0: subdevice #0
card 0: sofhdadsp [sof-hda-dsp], device 7: DMIC16kHz (*) []
Subdevices: 1/1
Subdevice #0: subdevice #0
Try with https://github.com/RustAudio/cpal/pull/917
Try with #917
Same behavior.
Looks like it is this: https://github.com/RustAudio/cpal/blob/f43d36e55494993bbbde3299af0c53e5cdf4d4cf/src/host/alsa/enumerate.rs#L88 whereas the assumption that "default" exists, does not hold (any more). Could be related to pipewire.
I don't know of any way to programmatically query ALSA for a default device that isn't called "default". This reply is relevant: that'd be a system configuration issue.
If you wish to point your ALSA default to PipeWire, then put this in your ~/.asoundrc:
pcm.!default {
type pipewire
}
ctl.!default {
type pipewire
}