refactor(asr): 抽出共享 pcm_duration_ms

(pcm.len()/2)*1000/16000 这段时长换算原本在 foundry/sherpa_runtime/
sherpa_provider/whisper/mimo 五处各自重复(带魔法数 2/16000)。收口到新
模块 asr/pcm.rs(pcm_duration_ms + pcm_duration_ms_from_bytes),各处改为
薄封装调用,参考 wav::encode_wav_16k_mono 的共享先例。行为不变。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
吕柏青andClaude Opus 4.8 committed 2026-06-07 13:11:58 +08:00
1 parent 72f9ced533
commit db51e95fde
7 files changed
+45 -6

No files matched your search

@@ -148,7 +148,7 @@ impl crate::recorder::AudioConsumer for FoundryLocalWhisperAsr {
}
fn pcm_duration_ms(pcm: &[u8]) -> u64 {
(pcm.len() as u64 / 2) * 1000 / 16_000
crate::asr::pcm::pcm_duration_ms(pcm)
}
fn pcm_to_wav(pcm: &[u8]) -> Vec<u8> {
@@ -222,11 +222,11 @@ impl crate::recorder::AudioConsumer for SherpaOnnxAsr {
}
fn pcm_duration_ms(pcm: &[u8]) -> u64 {
pcm_duration_ms_from_bytes(pcm.len() as u64)
crate::asr::pcm::pcm_duration_ms(pcm)
}
fn pcm_duration_ms_from_bytes(bytes: u64) -> u64 {
(bytes / 2) * 1000 / 16_000
crate::asr::pcm::pcm_duration_ms_from_bytes(bytes)
}
fn trim_transcript_text(text: &str) -> String {
@@ -485,7 +485,7 @@ fn accumulate_dir_size(dir: &Path, bytes: &mut u64) {
}
fn pcm_duration_ms(pcm: &[u8]) -> u64 {
(pcm.len() as u64 / 2) * 1000 / 16_000
crate::asr::pcm::pcm_duration_ms(pcm)
}
enum LoadedModel {
+1 -1
View File
@@ -178,7 +178,7 @@ pub fn extract_mimo_text(json: &Value) -> String {
}
fn pcm_duration_ms(pcm: &[u8]) -> u64 {
(pcm.len() as u64 / PCM_BYTES_PER_SAMPLE as u64) * 1000 / PCM_SAMPLE_RATE_HZ
super::pcm::pcm_duration_ms(pcm)
}
fn split_pcm_by_duration(pcm: &[u8], max_chunk_duration_ms: u64) -> Vec<&[u8]> {
@@ -9,6 +9,7 @@ pub mod bailian;
mod frame;
pub mod local;
pub mod mimo;
pub mod pcm;
pub mod volcengine;
pub mod wav;
pub mod whisper;
+38
View File
@@ -0,0 +1,38 @@
//! 共享 PCM 时长计算。
//!
//! 录音统一是 16 kHz / 单声道 / 16-bit 小端 PCM,时长换算 `(字节数 / 2) * 1000 / 16000`
//! 原本散落在各 ASR provider 里重复(foundry / sherpa / whisper / mimo),这里收口成
//! 唯一实现,各处改为薄封装调用(参考 `wav::encode_wav_16k_mono` 的共享先例)。
/// 每个采样的字节数(16-bit → 2 字节)。
const PCM_BYTES_PER_SAMPLE: u64 = 2;
/// 采样率(16 kHz)。
const PCM_SAMPLE_RATE_HZ: u64 = 16_000;
/// 由原始字节数计算 16 kHz / 单声道 / 16-bit PCM 的时长(毫秒)。
pub fn pcm_duration_ms_from_bytes(bytes: u64) -> u64 {
(bytes / PCM_BYTES_PER_SAMPLE) * 1000 / PCM_SAMPLE_RATE_HZ
}
/// 由 PCM 字节切片计算 16 kHz / 单声道 / 16-bit PCM 的时长(毫秒)。
pub fn pcm_duration_ms(pcm: &[u8]) -> u64 {
pcm_duration_ms_from_bytes(pcm.len() as u64)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn one_second_of_16k_i16_pcm_is_1000ms() {
// 16000 采样 × 2 字节 = 32000 字节 = 1 秒
assert_eq!(pcm_duration_ms(&vec![0u8; 32_000]), 1000);
assert_eq!(pcm_duration_ms_from_bytes(32_000), 1000);
}
#[test]
fn odd_trailing_byte_is_floored() {
// 末尾半个采样向下取整,与历史行为一致
assert_eq!(pcm_duration_ms(&vec![0u8; 33]), pcm_duration_ms(&vec![0u8; 32]));
}
}
@@ -285,7 +285,7 @@ fn extract_confident_text(json: &serde_json::Value) -> String {
}
fn pcm_duration_ms(pcm: &[u8]) -> u64 {
(pcm.len() as u64 / PCM_BYTES_PER_SAMPLE as u64) * 1000 / PCM_SAMPLE_RATE_HZ
super::pcm::pcm_duration_ms(pcm)
}
fn split_pcm_by_duration(pcm: &[u8], max_chunk_duration_ms: Option<u64>) -> Vec<&[u8]> {