Skip to content

Commit

Permalink
WIP improvement to audio quality by doing some buffering during playback
Browse files Browse the repository at this point in the history
  • Loading branch information
mgsloan committed Nov 26, 2024
1 parent 88d48f4 commit 814d5b5
Showing 1 changed file with 26 additions and 16 deletions.
42 changes: 26 additions & 16 deletions crates/live_kit_client/src/live_kit_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -279,44 +279,52 @@ pub fn play_remote_audio_track(
track: &track::RemoteAudioTrack,
background_executor: &BackgroundExecutor,
) -> AudioStream {
let buffer = Arc::new(Mutex::new(Vec::<i16>::new()));
// TODO: put buffer on the channel?
let buffer_mutex = Arc::new(Mutex::new(Vec::<i16>::new()));
let (stream_config_tx, stream_config_rx) = std::sync::mpsc::channel();
// TODO livekit: Pull these out of a proto later
let mut stream = NativeAudioStream::new(track.rtc_track(), 48000, 1);

let receive_task = background_executor.spawn({
let buffer = buffer.clone();
let buffer_mutex = buffer_mutex.clone();
async move {
let mut stream_config = None;
while let Some(frame) = stream.next().await {
let mut buffer = buffer.lock();
let buffer_size = frame.samples_per_channel * frame.num_channels;
debug_assert!(frame.data.len() == buffer_size as usize);

let frame_config = StreamConfig {
channels: frame.num_channels as u16,
sample_rate: cpal::SampleRate(frame.sample_rate),
buffer_size: cpal::BufferSize::Fixed(buffer_size),
// TODO(mgsloan): this multiplier was arbitrarily chosen, but a larger buffer
// size seems to help my audio quality.
buffer_size: cpal::BufferSize::Fixed(buffer_size * 2),
};

if stream_config.as_ref().map_or(true, |c| *c != frame_config) {
buffer.resize(buffer_size as usize, 0);
stream_config = Some(frame_config.clone());
stream_config_tx.send(frame_config).log_err();
}

if frame.data.len() == buffer.len() {
buffer.copy_from_slice(&frame.data);
} else {
buffer.iter_mut().for_each(|x| *x = 0);
let mut buffer = buffer_mutex.lock();
// TODO(mgsloan): max_size multiplier was arbitrarily chosen.
let max_size = (buffer_size * 3) as usize;
let new_size = buffer.len() + frame.data.len();
if new_size > max_size {
let drain_ix = new_size - max_size;
if drain_ix > buffer.len() {
buffer.clear();
} else {
buffer.drain(..new_size - max_size);
}
}
buffer.extend_from_slice(&frame.data);
}
Ok(Some(()))
}
});

let play_task = background_executor.spawn({
let buffer = buffer.clone();
async move {
if cfg!(any(test, feature = "test-support")) {
anyhow::bail!("can't play audio in tests");
Expand All @@ -331,14 +339,16 @@ pub fn play_remote_audio_track(
_output_stream = Some(device.build_output_stream(
&config,
{
let buffer = buffer.clone();
let buffer_mutex = buffer_mutex.clone();
move |data, _info| {
let buffer = buffer.lock();
if data.len() == buffer.len() {
data.copy_from_slice(&buffer);
} else {
data.iter_mut().for_each(|x| *x = 0);
let mut buffer = buffer_mutex.lock();
while data.len() > buffer.len() {
drop(buffer);
std::hint::spin_loop();
buffer = buffer_mutex.lock();
}
data.copy_from_slice(&buffer[..data.len()]);
buffer.drain(..data.len());
}
},
|error| log::error!("error playing audio track: {:?}", error),
Expand Down

0 comments on commit 814d5b5

Please sign in to comment.