From e83f248c676f1ed56e5c42d16da9fba395285fe3 Mon Sep 17 00:00:00 2001
From: cai <cai@nbcai.cc>
Date: Mon, 06 Jul 2026 03:54:46 +0800
Subject: [PATCH] fix: resample pcm stream chunks for livekit
---
src/audio.rs | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 files changed, 56 insertions(+), 1 deletions(-)
diff --git a/src/audio.rs b/src/audio.rs
index 1aadcb4..6bdea19 100644
--- a/src/audio.rs
+++ b/src/audio.rs
@@ -142,6 +142,45 @@
)
}
+pub fn pcm_s16le_bytes_to_frames(
+ pcm_bytes: &[u8],
+ source_sample_rate_hz: u32,
+ source_num_channels: u32,
+ target_sample_rate_hz: u32,
+ target_num_channels: u16,
+) -> Result<Vec<PcmFrame>> {
+ if source_sample_rate_hz == 0 {
+ return Err(anyhow!("pcm_s16le source sample rate is zero"));
+ }
+ if source_num_channels == 0 {
+ return Err(anyhow!("pcm_s16le source channel count is zero"));
+ }
+ if pcm_bytes.len() % 2 != 0 {
+ return Err(anyhow!("pcm_s16le payload has odd byte length"));
+ }
+ let samples: Vec<i16> = pcm_bytes
+ .chunks_exact(2)
+ .map(|chunk| i16::from_le_bytes([chunk[0], chunk[1]]))
+ .collect();
+ if samples.is_empty() {
+ return Ok(Vec::new());
+ }
+ let source_channels = u16::try_from(source_num_channels)
+ .map_err(|_| anyhow!("unsupported pcm_s16le channel count {source_num_channels}"))?;
+ let mut target_samples = remap_channels(samples, source_channels, target_num_channels);
+ target_samples = resample_linear(
+ target_samples,
+ source_sample_rate_hz,
+ target_sample_rate_hz,
+ target_num_channels,
+ );
+ Ok(chunk_pcm_samples(
+ target_samples,
+ target_sample_rate_hz,
+ target_num_channels,
+ ))
+}
+
fn decode_audio_frames(
audio_bytes: &[u8],
source_kind: &'static str,
@@ -674,7 +713,7 @@
#[cfg(test)]
mod tests {
- use super::load_pre_recorded_frames;
+ use super::{load_pre_recorded_frames, pcm_s16le_bytes_to_frames};
#[tokio::test]
async fn load_pre_recorded_frames_reads_local_wav() {
@@ -717,4 +756,20 @@
assert!(loaded.diagnostics.debug_source_path.is_some());
assert!(loaded.diagnostics.debug_pcm_wav_path.is_some());
}
+
+ #[test]
+ fn pcm_s16le_bytes_to_frames_resamples_elevenlabs_pcm_to_livekit_frames() {
+ let mut pcm = Vec::new();
+ for index in 0..4410 {
+ let sample = if index % 2 == 0 { 1024_i16 } else { -1024_i16 };
+ pcm.extend_from_slice(&sample.to_le_bytes());
+ }
+
+ let frames = pcm_s16le_bytes_to_frames(&pcm, 44_100, 1, 48_000, 1).expect("pcm frames");
+
+ assert!(!frames.is_empty());
+ assert_eq!(frames[0].sample_rate, 48_000);
+ assert_eq!(frames[0].num_channels, 1);
+ assert_eq!(frames[0].samples_per_channel, 960);
+ }
}
--
Gitblit v1.9.3