impress_asr_input_rust/src/audio/decoder.rs
impressionyang ceb2df18c4
Some checks are pending
Build Windows GUI / build-windows (push) Waiting to run
Build Windows GUI / release (push) Blocked by required conditions
初始提交:Windows 跨平台语音识别应用
功能:
- Tauri v2 GUI 应用
- 系统托盘支持
- 日志输出到文件
- 带时间戳的版本号
- 前端资源嵌入

修复:
- 前端路径使用相对路径
- 移除 devUrl 配置
- 窗口置顶设置
2026-05-21 17:58:18 +08:00

110 lines
3.0 KiB
Rust

//! 音频解码模块
//!
//! 使用 hound 库解码 WAV 文件
use anyhow::{Context, Result};
use std::path::Path;
use tracing::info;
use crate::audio::AudioData;
/// 解码 WAV 文件
///
/// 目前仅支持 WAV 格式
/// 返回解码后的音频数据
pub fn decode_audio(path: &Path) -> Result<AudioData> {
info!("解码音频文件:{:?}", path);
let reader = hound::WavReader::open(path)
.with_context(|| format!("无法打开文件:{:?}", path))?;
let spec = reader.spec();
let len = reader.len() as usize;
info!(
"音频信息:采样率={}, 声道={}, 位深={:?}, 样本数={}",
spec.sample_rate,
spec.channels,
spec.sample_format,
len
);
let samples: Vec<f32> = match spec.sample_format {
hound::SampleFormat::Int => {
let bits = spec.bits_per_sample as u32;
let max_val = (1 << (bits - 1)) as f32;
if bits <= 16 {
reader
.into_samples::<i16>()
.filter_map(|s| s.ok())
.map(|s| s as f32 / max_val)
.collect()
} else {
reader
.into_samples::<i32>()
.filter_map(|s| s.ok())
.map(|s| s as f32 / max_val)
.collect()
}
}
hound::SampleFormat::Float => {
reader
.into_samples::<f32>()
.filter_map(|s| s.ok())
.collect()
}
};
Ok(AudioData::new(samples, spec.sample_rate, spec.channels))
}
/// 解码音频并转换为 ASR 所需的格式
///
/// 转换为单声道、16kHz 采样率
pub fn decode_audio_for_asr(path: &Path) -> Result<AudioData> {
let mut audio = decode_audio(path)?;
// 转换为单声道
let mono_samples = audio.to_mono();
// 重采样到 16kHz (如果需要)
if audio.sample_rate != 16000 {
audio.samples = resample_to_16k(&mono_samples, audio.sample_rate)?;
audio.sample_rate = 16000;
audio.channels = 1;
} else {
audio.samples = mono_samples;
audio.channels = 1;
}
Ok(audio)
}
/// 重采样到 16kHz
fn resample_to_16k(samples: &[f32], original_rate: u32) -> Result<Vec<f32>> {
if original_rate == 16000 {
return Ok(samples.to_vec());
}
// 简单的线性插值重采样
let ratio = 16000.0 / original_rate as f32;
let new_len = (samples.len() as f32 * ratio) as usize;
let mut resampled = Vec::with_capacity(new_len);
for i in 0..new_len {
let src_idx = i as f32 / ratio;
let src_idx_floor = src_idx.floor() as usize;
let src_idx_ceil = src_idx.ceil() as usize;
if src_idx_ceil >= samples.len() {
resampled.push(*samples.last().unwrap_or(&0.0));
} else {
let frac = src_idx - src_idx_floor as f32;
let sample = samples[src_idx_floor] * (1.0 - frac) + samples[src_idx_ceil] * frac;
resampled.push(sample);
}
}
Ok(resampled)
}