Skip to content

Commit 68bec41

Browse files
authored
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
1 parent b519a4a commit 68bec41

1 file changed

Lines changed: 52 additions & 27 deletions

File tree

playback/src/audio_backend/alsa.rs

Lines changed: 52 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,15 @@ fn open_device(dev_name: &str, format: AudioFormat) -> Result<(PCM, usize), Alsa
152152

153153
pcm.sw_params(&swp).map_err(AlsaError::Pcm)?;
154154

155+
trace!("Frames per Buffer: {:?}", frames_per_buffer);
156+
trace!("Frames per Period: {:?}", frames_per_period);
157+
155158
// Let ALSA do the math for us.
156159
pcm.frames_to_bytes(frames_per_period) as usize
157160
};
158161

162+
trace!("Period Buffer size in bytes: {:?}", bytes_per_period);
163+
159164
Ok((pcm, bytes_per_period))
160165
}
161166

@@ -193,7 +198,22 @@ impl Sink for AlsaSink {
193198
match open_device(&self.device, self.format) {
194199
Ok((pcm, bytes_per_period)) => {
195200
self.pcm = Some(pcm);
196-
self.period_buffer = Vec::with_capacity(bytes_per_period);
201+
// If the capacity is greater than we want shrink it
202+
// to it's current len (which should be zero) before
203+
// setting the capacity with reserve_exact.
204+
if self.period_buffer.capacity() > bytes_per_period {
205+
self.period_buffer.shrink_to_fit();
206+
}
207+
// This does nothing if the capacity is already sufficient.
208+
// Len should always be zero, but for the sake of being thorough...
209+
self.period_buffer
210+
.reserve_exact(bytes_per_period - self.period_buffer.len());
211+
212+
// Should always match the "Period Buffer size in bytes: " trace! message.
213+
trace!(
214+
"Period Buffer capacity: {:?}",
215+
self.period_buffer.capacity()
216+
);
197217
}
198218
Err(e) => {
199219
return Err(io::Error::new(io::ErrorKind::Other, e));
@@ -205,20 +225,22 @@ impl Sink for AlsaSink {
205225
}
206226

207227
fn stop(&mut self) -> io::Result<()> {
208-
{
209-
// Write any leftover data in the period buffer
210-
// before draining the actual buffer
211-
self.write_bytes(&[])?;
212-
let pcm = self.pcm.as_mut().ok_or_else(|| {
213-
io::Error::new(io::ErrorKind::Other, "Error stopping AlsaSink, PCM is None")
214-
})?;
215-
pcm.drain().map_err(|e| {
216-
io::Error::new(
217-
io::ErrorKind::Other,
218-
format!("Error stopping AlsaSink {}", e),
219-
)
220-
})?
221-
}
228+
// Zero fill the remainder of the period buffer and
229+
// write any leftover data before draining the actual PCM buffer.
230+
self.period_buffer.resize(self.period_buffer.capacity(), 0);
231+
self.write_buf()?;
232+
233+
let pcm = self.pcm.as_mut().ok_or_else(|| {
234+
io::Error::new(io::ErrorKind::Other, "Error stopping AlsaSink, PCM is None")
235+
})?;
236+
237+
pcm.drain().map_err(|e| {
238+
io::Error::new(
239+
io::ErrorKind::Other,
240+
format!("Error stopping AlsaSink {}", e),
241+
)
242+
})?;
243+
222244
self.pcm = None;
223245
Ok(())
224246
}
@@ -228,22 +250,24 @@ impl Sink for AlsaSink {
228250

229251
impl SinkAsBytes for AlsaSink {
230252
fn write_bytes(&mut self, data: &[u8]) -> io::Result<()> {
231-
let mut processed_data = 0;
232-
while processed_data < data.len() {
233-
let data_to_buffer = min(
234-
self.period_buffer.capacity() - self.period_buffer.len(),
235-
data.len() - processed_data,
236-
);
253+
let mut start_index = 0;
254+
let data_len = data.len();
255+
let capacity = self.period_buffer.capacity();
256+
loop {
257+
let data_left = data_len - start_index;
258+
let space_left = capacity - self.period_buffer.len();
259+
let data_to_buffer = min(data_left, space_left);
260+
let end_index = start_index + data_to_buffer;
237261
self.period_buffer
238-
.extend_from_slice(&data[processed_data..processed_data + data_to_buffer]);
239-
processed_data += data_to_buffer;
240-
if self.period_buffer.len() == self.period_buffer.capacity() {
262+
.extend_from_slice(&data[start_index..end_index]);
263+
if self.period_buffer.len() == capacity {
241264
self.write_buf()?;
242-
self.period_buffer.clear();
243265
}
266+
if end_index == data_len {
267+
break Ok(());
268+
}
269+
start_index = end_index;
244270
}
245-
246-
Ok(())
247271
}
248272
}
249273

@@ -276,6 +300,7 @@ impl AlsaSink {
276300
})?
277301
}
278302

303+
self.period_buffer.clear();
279304
Ok(())
280305
}
281306
}

0 commit comments

Comments
 (0)