Improve Alsa backend buffer (#811)

* Reuse the buffer for the life of the Alsa sink
* Don't depend on capacity being exact when sizing the buffer
* Always give the PCM a period's worth of audio even when draining the buffer
* Refactoring and code cleanup
This commit is contained in:
Jason Gray 2021-07-06 01:37:29 -05:00 committed by GitHub
parent b519a4a47d
commit 68bec41e08
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -152,10 +152,15 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, usize), Alsa
pcm.sw_params(&swp).map_err(AlsaError::Pcm)?; pcm.sw_params(&swp).map_err(AlsaError::Pcm)?;
trace!("Frames per Buffer: {:?}", frames_per_buffer);
trace!("Frames per Period: {:?}", frames_per_period);
// Let ALSA do the math for us. // Let ALSA do the math for us.
pcm.frames_to_bytes(frames_per_period) as usize pcm.frames_to_bytes(frames_per_period) as usize
}; };
trace!("Period Buffer size in bytes: {:?}", bytes_per_period);
Ok((pcm, bytes_per_period)) Ok((pcm, bytes_per_period))
} }
@ -193,7 +198,22 @@ impl Sink for AlsaSink {
match open_device(&self.device, self.format) { match open_device(&self.device, self.format) {
Ok((pcm, bytes_per_period)) => { Ok((pcm, bytes_per_period)) => {
self.pcm = Some(pcm); self.pcm = Some(pcm);
self.period_buffer = Vec::with_capacity(bytes_per_period); // If the capacity is greater than we want shrink it
// to it's current len (which should be zero) before
// setting the capacity with reserve_exact.
if self.period_buffer.capacity() > bytes_per_period {
self.period_buffer.shrink_to_fit();
}
// This does nothing if the capacity is already sufficient.
// Len should always be zero, but for the sake of being thorough...
self.period_buffer
.reserve_exact(bytes_per_period - self.period_buffer.len());
// Should always match the "Period Buffer size in bytes: " trace! message.
trace!(
"Period Buffer capacity: {:?}",
self.period_buffer.capacity()
);
} }
Err(e) => { Err(e) => {
return Err(io::Error::new(io::ErrorKind::Other, e)); return Err(io::Error::new(io::ErrorKind::Other, e));
@ -205,20 +225,22 @@ impl Sink for AlsaSink {
} }
fn stop(&mut self) -> io::Result<()> { fn stop(&mut self) -> io::Result<()> {
{ // Zero fill the remainder of the period buffer and
// Write any leftover data in the period buffer // write any leftover data before draining the actual PCM buffer.
// before draining the actual buffer self.period_buffer.resize(self.period_buffer.capacity(), 0);
self.write_bytes(&[])?; self.write_buf()?;
let pcm = self.pcm.as_mut().ok_or_else(|| {
io::Error::new(io::ErrorKind::Other, "Error stopping AlsaSink, PCM is None") let pcm = self.pcm.as_mut().ok_or_else(|| {
})?; io::Error::new(io::ErrorKind::Other, "Error stopping AlsaSink, PCM is None")
pcm.drain().map_err(|e| { })?;
io::Error::new(
io::ErrorKind::Other, pcm.drain().map_err(|e| {
format!("Error stopping AlsaSink {}", e), io::Error::new(
) io::ErrorKind::Other,
})? format!("Error stopping AlsaSink {}", e),
} )
})?;
self.pcm = None; self.pcm = None;
Ok(()) Ok(())
} }
@ -228,22 +250,24 @@ impl Sink for AlsaSink {
impl SinkAsBytes for AlsaSink { impl SinkAsBytes for AlsaSink {
fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> { fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> {
let mut processed_data = 0; let mut start_index = 0;
while processed_data < data.len() { let data_len = data.len();
let data_to_buffer = min( let capacity = self.period_buffer.capacity();
self.period_buffer.capacity() - self.period_buffer.len(), loop {
data.len() - processed_data, let data_left = data_len - start_index;
); let space_left = capacity - self.period_buffer.len();
let data_to_buffer = min(data_left, space_left);
let end_index = start_index + data_to_buffer;
self.period_buffer self.period_buffer
.extend_from_slice(&data[processed_data..processed_data + data_to_buffer]); .extend_from_slice(&data[start_index..end_index]);
processed_data += data_to_buffer; if self.period_buffer.len() == capacity {
if self.period_buffer.len() == self.period_buffer.capacity() {
self.write_buf()?; self.write_buf()?;
self.period_buffer.clear();
} }
if end_index == data_len {
break Ok(());
}
start_index = end_index;
} }
Ok(())
} }
} }
@ -276,6 +300,7 @@ impl AlsaSink {
})? })?
} }
self.period_buffer.clear();
Ok(()) Ok(())
} }
} }