Open Ricky12Awesome opened 3 years ago
~Yay! Too much text nobody is gonna read!~
TL:DR (Too Long, Didn't Read): cpal is not able to handle/catch these errors, as neitheralsa-rs
does, becuase they are written to standard error by ALSA, not Rust; which points to the solution, i.e. to catch stderr.
Therefore, I will create an issue/pull request to alsa-rs :D
P.D. If any other sound library, like JACK, has a similar issue, a similar fix can be written.
On the other hand, it's worth noting that cpal ignores whatever of these could be catched by Rust only, in order to only show the available devices, not those who do not work, or error, and yet are known to ALSA (such as JACK audio I/O device, in this case), which is also helpful to know whether an audio device is for input or output, even though it's not an issue.
I couldn't not write this little story, apologies :sweat_smile:
Unnecessarily, and by curiosity, I tried to list all devices to select instead of the defaults, and was surprised to see many errors, none which lead to panic or an error in Rust (with Result) that could be fixed.
That's how, all day, I tried to catch an error within Rust, in my code, cpal and its dependency alsa-rs, until I realized these were thrown by ALSA, nowhere in Rust where they could be catched or suppressed from standard error.
Without further ado, here's what happens.
In Linux, unless specified, cpal defaults to ALSA 1, 2, contrary to what the errors may say [^1]. devices
has to be called so we can fetch all devices, as it is not done upon creation; however, the method only creates a new Devices
3 instance, which we have to iterate over to get each device (next
) 4, which is why the error is thrown even when using count
.
next
calls DeviceHandles::open
5, which tries to open a handle for both playback and capture, and returns if at least one was opened successfully 6, which is useful to know if a device is either for I/O, even though it's determined differently 7, and is better than the current approach.
Anyways, on both directions try_open
is called with PCM::new
from alsa-rs
8, which returns a Result
from open
9 catched through the macro acheck!
10, which calls the Rust bindings generated in alsa-sys
from ALSA.
At the end, the error roots from acheck!
, which catches a error different to the ones printed, and nowhere suppresses the errors that are printed by ALSA to standard error, which means this is not a cpal issue, and must be fixed in alsa-rs.
Before anything, to temporarily edit alsa-rs code unto a project, rhack
11 can be used to readily download the crate code and set it up on the current project, or otherwise manually with Cargo.toml's [patch]
12 (can set a GitHub repository).
We just have to catch the errors from standard error, which we can either print or not. In fact, a similar issue, which is "how to catch a C library's stderr in Rust" 13, has already been solved, and is used as reference in the following code.
If you're curious of how to log all the errors from the code flow, here's the code:
and here's how it looks (without the optional logs):
In fact, the errors are thrown when cpal cannot open a device with neither playback or capture. However, it's also possible to catch the individual errors when either one of those do not work, from what I remember.
I've learnt some new things, documented the whole process, and fixed an issue (or two), didn't hurt, right?
[^1]: As mentioned, this is becuase JACK seems to create an audio device that is detected by ALSA, as shown by output of the errors in Err?.
CPAL Version:
0.13.4
Distro:Garuda Dragonized Linux
I don't really know what's causing this issue, but here's the code I've tried and the output of it