cai
2026-07-06 9c2d8f0dba462b347a706d0a539ccbe163e51b85
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);
    }
}