alsa - Use status to retrieve avail, delay
This addresses @diwic's [tip][1] about using the pcm status to retrieve avail and delay, as well as the timestamps, rather than using separate pcm calls for each. [1]: https://github.com/mitchmindtree/cpal/pull/5#issuecomment-621526946
This commit is contained in:
parent
5813e0f97a
commit
cb95bfd88b
|
@ -189,7 +189,7 @@ impl Device {
|
||||||
let hw_params = set_hw_params_from_format(&handle, conf, sample_format)?;
|
let hw_params = set_hw_params_from_format(&handle, conf, sample_format)?;
|
||||||
hw_params.can_pause()
|
hw_params.can_pause()
|
||||||
};
|
};
|
||||||
let (buffer_len, period_len) = set_sw_params_from_format(&handle, conf)?;
|
let (_buffer_len, period_len) = set_sw_params_from_format(&handle, conf)?;
|
||||||
|
|
||||||
handle.prepare()?;
|
handle.prepare()?;
|
||||||
|
|
||||||
|
@ -218,7 +218,6 @@ impl Device {
|
||||||
sample_format,
|
sample_format,
|
||||||
num_descriptors,
|
num_descriptors,
|
||||||
conf: conf.clone(),
|
conf: conf.clone(),
|
||||||
buffer_len,
|
|
||||||
period_len,
|
period_len,
|
||||||
can_pause,
|
can_pause,
|
||||||
creation_instant,
|
creation_instant,
|
||||||
|
@ -434,9 +433,6 @@ struct StreamInner {
|
||||||
// The configuration used to open this stream.
|
// The configuration used to open this stream.
|
||||||
conf: StreamConfig,
|
conf: StreamConfig,
|
||||||
|
|
||||||
// Number of samples that can fit in the buffer.
|
|
||||||
buffer_len: usize,
|
|
||||||
|
|
||||||
// Minimum number of samples to put in the buffer.
|
// Minimum number of samples to put in the buffer.
|
||||||
period_len: usize,
|
period_len: usize,
|
||||||
|
|
||||||
|
@ -499,6 +495,7 @@ fn input_stream_worker(
|
||||||
PollDescriptorsFlow::Continue => continue,
|
PollDescriptorsFlow::Continue => continue,
|
||||||
PollDescriptorsFlow::Return => return,
|
PollDescriptorsFlow::Return => return,
|
||||||
PollDescriptorsFlow::Ready {
|
PollDescriptorsFlow::Ready {
|
||||||
|
status,
|
||||||
avail_frames: _,
|
avail_frames: _,
|
||||||
delay_frames,
|
delay_frames,
|
||||||
stream_type,
|
stream_type,
|
||||||
|
@ -508,7 +505,13 @@ fn input_stream_worker(
|
||||||
StreamType::Input,
|
StreamType::Input,
|
||||||
"expected input stream, but polling descriptors indicated output",
|
"expected input stream, but polling descriptors indicated output",
|
||||||
);
|
);
|
||||||
let res = process_input(stream, &mut ctxt.buffer, delay_frames, data_callback);
|
let res = process_input(
|
||||||
|
stream,
|
||||||
|
&mut ctxt.buffer,
|
||||||
|
status,
|
||||||
|
delay_frames,
|
||||||
|
data_callback,
|
||||||
|
);
|
||||||
report_error(res, error_callback);
|
report_error(res, error_callback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -533,6 +536,7 @@ fn output_stream_worker(
|
||||||
PollDescriptorsFlow::Continue => continue,
|
PollDescriptorsFlow::Continue => continue,
|
||||||
PollDescriptorsFlow::Return => return,
|
PollDescriptorsFlow::Return => return,
|
||||||
PollDescriptorsFlow::Ready {
|
PollDescriptorsFlow::Ready {
|
||||||
|
status,
|
||||||
avail_frames,
|
avail_frames,
|
||||||
delay_frames,
|
delay_frames,
|
||||||
stream_type,
|
stream_type,
|
||||||
|
@ -545,6 +549,7 @@ fn output_stream_worker(
|
||||||
let res = process_output(
|
let res = process_output(
|
||||||
stream,
|
stream,
|
||||||
&mut ctxt.buffer,
|
&mut ctxt.buffer,
|
||||||
|
status,
|
||||||
avail_frames,
|
avail_frames,
|
||||||
delay_frames,
|
delay_frames,
|
||||||
data_callback,
|
data_callback,
|
||||||
|
@ -577,6 +582,7 @@ enum PollDescriptorsFlow {
|
||||||
Return,
|
Return,
|
||||||
Ready {
|
Ready {
|
||||||
stream_type: StreamType,
|
stream_type: StreamType,
|
||||||
|
status: alsa::pcm::Status,
|
||||||
avail_frames: usize,
|
avail_frames: usize,
|
||||||
delay_frames: usize,
|
delay_frames: usize,
|
||||||
},
|
},
|
||||||
|
@ -636,8 +642,14 @@ fn poll_descriptors_and_prepare_buffer(
|
||||||
return Ok(PollDescriptorsFlow::Continue);
|
return Ok(PollDescriptorsFlow::Continue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Get the number of available samples for reading/writing.
|
|
||||||
let (avail_frames, delay_frames) = get_avail_delay(stream)?;
|
let status = stream.channel.status()?;
|
||||||
|
let avail_frames = status.get_avail() as usize;
|
||||||
|
let delay_frames = match status.get_delay() {
|
||||||
|
// Buffer underrun. TODO: Notify the user.
|
||||||
|
d if d < 0 => 0,
|
||||||
|
d => d as usize,
|
||||||
|
};
|
||||||
let available_samples = avail_frames * stream.conf.channels as usize;
|
let available_samples = avail_frames * stream.conf.channels as usize;
|
||||||
|
|
||||||
// Only go on if there is at least `stream.period_len` samples.
|
// Only go on if there is at least `stream.period_len` samples.
|
||||||
|
@ -651,6 +663,7 @@ fn poll_descriptors_and_prepare_buffer(
|
||||||
|
|
||||||
Ok(PollDescriptorsFlow::Ready {
|
Ok(PollDescriptorsFlow::Ready {
|
||||||
stream_type,
|
stream_type,
|
||||||
|
status,
|
||||||
avail_frames,
|
avail_frames,
|
||||||
delay_frames,
|
delay_frames,
|
||||||
})
|
})
|
||||||
|
@ -660,6 +673,7 @@ fn poll_descriptors_and_prepare_buffer(
|
||||||
fn process_input(
|
fn process_input(
|
||||||
stream: &StreamInner,
|
stream: &StreamInner,
|
||||||
buffer: &mut [u8],
|
buffer: &mut [u8],
|
||||||
|
status: alsa::pcm::Status,
|
||||||
delay_frames: usize,
|
delay_frames: usize,
|
||||||
data_callback: &mut (dyn FnMut(&Data, &InputCallbackInfo) + Send + 'static),
|
data_callback: &mut (dyn FnMut(&Data, &InputCallbackInfo) + Send + 'static),
|
||||||
) -> Result<(), BackendSpecificError> {
|
) -> Result<(), BackendSpecificError> {
|
||||||
|
@ -668,7 +682,7 @@ fn process_input(
|
||||||
let data = buffer.as_mut_ptr() as *mut ();
|
let data = buffer.as_mut_ptr() as *mut ();
|
||||||
let len = buffer.len() / sample_format.sample_size();
|
let len = buffer.len() / sample_format.sample_size();
|
||||||
let data = unsafe { Data::from_parts(data, len, sample_format) };
|
let data = unsafe { Data::from_parts(data, len, sample_format) };
|
||||||
let callback = stream_timestamp(stream)?;
|
let callback = stream_timestamp(&status, stream.creation_instant)?;
|
||||||
let delay_duration = frames_to_duration(delay_frames, stream.conf.sample_rate);
|
let delay_duration = frames_to_duration(delay_frames, stream.conf.sample_rate);
|
||||||
let capture = callback
|
let capture = callback
|
||||||
.sub(delay_duration)
|
.sub(delay_duration)
|
||||||
|
@ -686,6 +700,7 @@ fn process_input(
|
||||||
fn process_output(
|
fn process_output(
|
||||||
stream: &StreamInner,
|
stream: &StreamInner,
|
||||||
buffer: &mut [u8],
|
buffer: &mut [u8],
|
||||||
|
status: alsa::pcm::Status,
|
||||||
available_frames: usize,
|
available_frames: usize,
|
||||||
delay_frames: usize,
|
delay_frames: usize,
|
||||||
data_callback: &mut (dyn FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static),
|
data_callback: &mut (dyn FnMut(&mut Data, &OutputCallbackInfo) + Send + 'static),
|
||||||
|
@ -697,7 +712,7 @@ fn process_output(
|
||||||
let data = buffer.as_mut_ptr() as *mut ();
|
let data = buffer.as_mut_ptr() as *mut ();
|
||||||
let len = buffer.len() / sample_format.sample_size();
|
let len = buffer.len() / sample_format.sample_size();
|
||||||
let mut data = unsafe { Data::from_parts(data, len, sample_format) };
|
let mut data = unsafe { Data::from_parts(data, len, sample_format) };
|
||||||
let callback = stream_timestamp(stream)?;
|
let callback = stream_timestamp(&status, stream.creation_instant)?;
|
||||||
let delay_duration = frames_to_duration(delay_frames, stream.conf.sample_rate);
|
let delay_duration = frames_to_duration(delay_frames, stream.conf.sample_rate);
|
||||||
let playback = callback
|
let playback = callback
|
||||||
.add(delay_duration)
|
.add(delay_duration)
|
||||||
|
@ -737,10 +752,12 @@ fn process_output(
|
||||||
// Use the elapsed duration since the start of the stream.
|
// Use the elapsed duration since the start of the stream.
|
||||||
//
|
//
|
||||||
// This ensures positive values that are compatible with our `StreamInstant` representation.
|
// This ensures positive values that are compatible with our `StreamInstant` representation.
|
||||||
fn stream_timestamp(stream: &StreamInner) -> Result<crate::StreamInstant, BackendSpecificError> {
|
fn stream_timestamp(
|
||||||
match stream.creation_instant {
|
status: &alsa::pcm::Status,
|
||||||
|
creation_instant: Option<std::time::Instant>,
|
||||||
|
) -> Result<crate::StreamInstant, BackendSpecificError> {
|
||||||
|
match creation_instant {
|
||||||
None => {
|
None => {
|
||||||
let status = stream.channel.status()?;
|
|
||||||
let trigger_ts = status.get_trigger_htstamp();
|
let trigger_ts = status.get_trigger_htstamp();
|
||||||
let ts = status.get_htstamp();
|
let ts = status.get_htstamp();
|
||||||
let nanos = timespec_diff_nanos(ts, trigger_ts)
|
let nanos = timespec_diff_nanos(ts, trigger_ts)
|
||||||
|
@ -847,19 +864,6 @@ impl StreamTrait for Stream {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the number of frames that are available to read/write along with the latency.
|
|
||||||
fn get_avail_delay(stream: &StreamInner) -> Result<(usize, usize), BackendSpecificError> {
|
|
||||||
match stream.channel.avail_delay() {
|
|
||||||
Err(err) if err.errno() == Some(nix::errno::Errno::EPIPE) => {
|
|
||||||
// buffer underrun
|
|
||||||
// TODO: Notify the user some how.
|
|
||||||
Ok((stream.buffer_len, 0))
|
|
||||||
}
|
|
||||||
Err(err) => Err(err.into()),
|
|
||||||
Ok((avail, delay)) => Ok((avail as usize, delay as usize)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_hw_params_from_format<'a>(
|
fn set_hw_params_from_format<'a>(
|
||||||
pcm_handle: &'a alsa::pcm::PCM,
|
pcm_handle: &'a alsa::pcm::PCM,
|
||||||
config: &StreamConfig,
|
config: &StreamConfig,
|
||||||
|
|
Loading…
Reference in New Issue