Skip to content

Commit

Permalink
[ALSA] Improve stream setup parameters.
Browse files Browse the repository at this point in the history
* Always set period before setting buffer size, even if
  `BufferSize::Fixed`.
* Set `start_threshold` based on `alsa::Direction` to avoid underruns on
  playback streams.

Also refactor the return values of the set params functions to reflect their
actual use and resolve some clippy warnings (including a logic error in
`stream_timestamp()`).
  • Loading branch information
alexmoon authored and strohel committed Jun 17, 2021
1 parent 47ef62d commit c349eb1
Showing 1 changed file with 35 additions and 31 deletions.
66 changes: 35 additions & 31 deletions src/host/alsa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,11 +251,8 @@ impl Device {
Err((e, _)) => return Err(e.into()),
Ok(handle) => handle,
};
let can_pause = {
let hw_params = set_hw_params_from_format(&handle, conf, sample_format)?;
hw_params.can_pause()
};
let (_buffer_len, period_len) = set_sw_params_from_format(&handle, conf)?;
let can_pause = set_hw_params_from_format(&handle, conf, sample_format)?;
let period_len = set_sw_params_from_format(&handle, conf, stream_type)?;

handle.prepare()?;

Expand All @@ -277,7 +274,9 @@ impl Device {
_ => None,
};

handle.start()?;
if let alsa::Direction::Capture = stream_type {
handle.start()?;
}

let stream_inner = StreamInner {
channel: handle,
Expand Down Expand Up @@ -420,10 +419,10 @@ impl Device {
supported_formats.len() * supported_channels.len() * sample_rates.len(),
);
for &sample_format in supported_formats.iter() {
for channels in supported_channels.iter() {
for &channels in supported_channels.iter() {
for &(min_rate, max_rate) in sample_rates.iter() {
output.push(SupportedStreamConfigRange {
channels: *channels,
channels,
min_sample_rate: SampleRate(min_rate as u32),
max_sample_rate: SampleRate(max_rate as u32),
buffer_size: buffer_size_range.clone(),
Expand Down Expand Up @@ -856,14 +855,13 @@ fn stream_timestamp(
None => {
let trigger_ts = status.get_trigger_htstamp();
let ts = status.get_htstamp();
let nanos = timespec_diff_nanos(ts, trigger_ts)
.try_into()
.unwrap_or_else(|_| {
panic!(
"get_htstamp `{:?}` was earlier than get_trigger_htstamp `{:?}`",
ts, trigger_ts
);
});
let nanos = timespec_diff_nanos(ts, trigger_ts);
if nanos < 0 {
panic!(
"get_htstamp `{:?}` was earlier than get_trigger_htstamp `{:?}`",
ts, trigger_ts
);
}
Ok(crate::StreamInstant::from_nanos(nanos))
}
Some(creation) => {
Expand Down Expand Up @@ -966,11 +964,11 @@ impl StreamTrait for Stream {
}
}

fn set_hw_params_from_format<'a>(
pcm_handle: &'a alsa::pcm::PCM,
fn set_hw_params_from_format(
pcm_handle: &alsa::pcm::PCM,
config: &StreamConfig,
sample_format: SampleFormat,
) -> Result<alsa::pcm::HwParams<'a>, BackendSpecificError> {
) -> Result<bool, BackendSpecificError> {
let hw_params = alsa::pcm::HwParams::any(pcm_handle)?;
hw_params.set_access(alsa::pcm::Access::RWInterleaved)?;

Expand All @@ -993,7 +991,10 @@ fn set_hw_params_from_format<'a>(
hw_params.set_channels(config.channels as u32)?;

match config.buffer_size {
BufferSize::Fixed(v) => hw_params.set_buffer_size(v as alsa::pcm::Frames)?,
BufferSize::Fixed(v) => {
hw_params.set_period_size_near((v / 4) as alsa::pcm::Frames, alsa::ValueOr::Nearest)?;
hw_params.set_buffer_size(v as alsa::pcm::Frames)?;
}
BufferSize::Default => {
// These values together represent a moderate latency and wakeup interval.
// Without them we are at the mercy of the device
Expand All @@ -1004,37 +1005,40 @@ fn set_hw_params_from_format<'a>(

pcm_handle.hw_params(&hw_params)?;

Ok(hw_params)
Ok(hw_params.can_pause())
}

fn set_sw_params_from_format(
pcm_handle: &alsa::pcm::PCM,
config: &StreamConfig,
) -> Result<(usize, usize), BackendSpecificError> {
stream_type: alsa::Direction,
) -> Result<usize, BackendSpecificError> {
let sw_params = pcm_handle.sw_params_current()?;
sw_params.set_start_threshold(0)?;

let (buffer_len, period_len) = {
let period_len = {
let (buffer, period) = pcm_handle.get_params()?;
if buffer == 0 {
return Err(BackendSpecificError {
description: "initialization resulted in a null buffer".to_string(),
});
}
sw_params.set_avail_min(period as alsa::pcm::Frames)?;
let buffer = buffer as usize * config.channels as usize;
let period = period as usize * config.channels as usize;
(buffer, period)

let start_threshold = match stream_type {
alsa::Direction::Playback => buffer - period,
alsa::Direction::Capture => 1,
};
sw_params.set_start_threshold(start_threshold.try_into().unwrap())?;

period as usize * config.channels as usize
};

sw_params.set_tstamp_mode(true)?;
// TODO:
// Pending new version of alsa-sys and alsa-rs getting published.
// sw_params.set_tstamp_type(alsa::pcm::TstampType::MonotonicRaw)?;
sw_params.set_tstamp_type(alsa::pcm::TstampType::MonotonicRaw)?;

pcm_handle.sw_params(&sw_params)?;

Ok((buffer_len, period_len))
Ok(period_len)
}

impl From<alsa::Error> for BackendSpecificError {
Expand Down

0 comments on commit c349eb1

Please sign in to comment.