fix: stabilize tsclientlib audio playback buffering

This commit is contained in:
ReTeamSpeak
2026-05-13 16:27:29 +09:00
parent 43967cf309
commit a852374bf9
2 changed files with 24 additions and 14 deletions
+22 -4
View File
@@ -9,6 +9,7 @@ use tsproto_packets::packets::{AudioData, InAudioBuf};
const SAMPLE_RATE: u32 = 48000;
const CHANNELS: u16 = 2;
const FRAME_SIZE: usize = 960; // 20ms at 48kHz mono
const PLAYBACK_FILL_SAMPLES: usize = 1920; // 20ms at 48kHz stereo for tsclientlib playback
const OPUS_MAX_PACKET: usize = 1275;
type PacketSender = std::sync::mpsc::Sender<InAudioBuf>;
@@ -74,6 +75,8 @@ impl AudioPlayback {
let callback_volumes = Arc::clone(&volumes);
let callback_muted = Arc::clone(&muted_clients);
let mut handler = AudioHandler::<ClientId>::new();
let mut playback_chunk = vec![0.0f32; PLAYBACK_FILL_SAMPLES];
let mut playback_chunk_offset = PLAYBACK_FILL_SAMPLES;
let stream = device
.build_output_stream(
@@ -87,9 +90,6 @@ impl AudioPlayback {
};
let _ = handler.handle_packet(ClientId(from), packet);
}
for sample in data.iter_mut() {
*sample = 0.0;
}
let volumes = callback_volumes.lock().ok().map(|volumes| volumes.clone());
let muted_clients = callback_muted.lock().ok().map(|muted| muted.clone());
for (id, queue) in handler.get_mut_queues().iter_mut() {
@@ -103,7 +103,25 @@ impl AudioPlayback {
.unwrap_or(false);
queue.volume = if muted { 0.0 } else { volume };
}
handler.fill_buffer(data);
let mut written = 0;
while written < data.len() {
if playback_chunk_offset >= playback_chunk.len() {
playback_chunk.fill(0.0);
handler.fill_buffer(&mut playback_chunk);
playback_chunk_offset = 0;
}
let available = playback_chunk.len() - playback_chunk_offset;
let remaining = data.len() - written;
let copy_len = available.min(remaining);
data[written..written + copy_len].copy_from_slice(
&playback_chunk
[playback_chunk_offset..playback_chunk_offset + copy_len],
);
playback_chunk_offset += copy_len;
written += copy_len;
}
},
|err| tracing::error!("Audio output error: {err}"),
None,
+2 -10
View File
@@ -912,19 +912,11 @@ impl App {
iced::Event::Keyboard(iced::keyboard::Event::KeyPressed {
key: iced::keyboard::Key::Character(c),
..
}) => {
if c.as_str().to_lowercase() == "v" {
return Message::PttPressed;
}
}
}) if c.as_str().to_lowercase() == "v" => return Message::PttPressed,
iced::Event::Keyboard(iced::keyboard::Event::KeyReleased {
key: iced::keyboard::Key::Character(c),
..
}) => {
if c.as_str().to_lowercase() == "v" {
return Message::PttReleased;
}
}
}) if c.as_str().to_lowercase() == "v" => return Message::PttReleased,
_ => {}
}
Message::Noop