-
Notifications
You must be signed in to change notification settings - Fork 435
use set_buffer_size_near to calculate fixed alsa buffer size #990
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Conversation
Agreed, this is the right thing to do. This highly related commit in |
@attackgoat as I became a maintainer today, and agree with your approach, I would like to work with you to getting to a mergable state. Could you update it to:
Thanks! |
I see that #917 also does this (and more). |
Thanks a lot @attackgoat. The author of #917 expressed that he is no longer working on it. Would you be willing to merge the two approaches? |
src/host/alsa/mod.rs
Outdated
if let BufferSize::Default = buffer_size { | ||
// These values together represent a moderate latency and wakeup interval. | ||
// Without them, we are at the mercy of the device | ||
hw_params.set_period_time_near(25_000, alsa::ValueOr::Nearest)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm questioning whether an error here should immediately generate a function result. As you've found, there may be reasons why a device returns a different period size and we probably want to use the actual period size for the buffer time.
As well - though this is opinionated - I'd probably rather have initialization succeed (so we can play sound) rather than fail hard just because the exact period or buffer time can't be set.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The latest commit handles various failures better and relies on the actual ALSA-provided period size after setting time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot, this is really going in the right direction. Some points to make it even more robust - I've got prior experience where I know that ALSA drivers can be really fickle.
At any time just let me know if and how I can help.
CHANGELOG.md
Outdated
@@ -1,6 +1,7 @@ | |||
# Unreleased | |||
|
|||
- ALSA(process_output): Pass `silent=true` to `PCM.try_recover`, so it doesn't write to stderr. | |||
- ALSA: Fix buffer size selection. (error 22) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This PR is going to be capable of doing a lot more 😉
It would be good to concisely but clearly capture the changes to the period size, buffer size, and fallback handling.
src/host/alsa/mod.rs
Outdated
// `default` pcm sometimes fails here, but there no reason to as we attempt to provide a size or | ||
// minimum number of periods. | ||
hw_params | ||
.set_buffer_size(period_size * PERIOD_COUNT as alsa::pcm::Frames) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fallback is stricter than the primary path: should bed we not also use set_buffer_size_near
here?
src/host/alsa/mod.rs
Outdated
fn set_hw_params_periods(hw_params: &alsa::pcm::HwParams, buffer_size: BufferSize) -> bool { | ||
const FALLBACK_PERIOD_TIME: u32 = 25_000; | ||
|
||
// When the API is made available, this could rely on snd_pcm_hw_params_get_periods_min and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed, let's make a "TODO" as a future reminder.
src/host/alsa/mod.rs
Outdated
const PERIOD_COUNT: u32 = 2; | ||
|
||
/// Returns true if the buffer size was reasonably set. | ||
fn set_hw_params_buffer_size(hw_params: &alsa::pcm::HwParams, buffer_size: FrameCount) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can make buffer size setting more robust by inspecting the device capabilities and clamping to the supported buffer sizes. Something like:
fn clamp_frames_to_device_caps(hw_params: &alsa::pcm::HwParams, requested: FrameCount) -> FrameCount {
let min_buf = hw_params.get_buffer_size_min().unwrap_or(1);
let max_buf = hw_params.get_buffer_size_max().unwrap_or(Frames::MAX); // usize::MAX? or something more sensical?
requested.clamp(min_buf, max_buf)
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added this and also updated these other bits to use the same logic:
- Supported stream configuration min/max clamped to valid range
- Buffer size set after using
BufferSize::Default
checked for valid range
src/host/alsa/mod.rs
Outdated
.set_period_size_near(period_size, alsa::ValueOr::Greater) | ||
.is_err() | ||
{ | ||
return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If setting the period size fails, we still could continue getting the actual period size and tuning the buffer size to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this path fails, the fallback will use period time to calculate a valid buffer size. The last commit (4da16f1) has moved this all around a bit.
src/host/alsa/mod.rs
Outdated
/// Returns true if the buffer size was reasonably set. | ||
fn set_hw_params_buffer_size(hw_params: &alsa::pcm::HwParams, buffer_size: FrameCount) -> bool { | ||
// Desired period size | ||
let period_size = (buffer_size / PERIOD_COUNT) as alsa::pcm::Frames; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Period size we could similarly clamp by inspecting device capabilities.
I know it sounds like _near
and ValueOr
would yield something usable in all cases, but from librespot experience I know that not all ALSA drivers do.
Fixes #743
Closes #917
Hardware parameters cannot be set to some arbitrary values within the supported range.
Two alternate fixes are:
I am not familiar enough with CPAL usage to know if the alternate fixes are needed, but for my use cases they are not.