Rust中FFT如何获取全频段频谱?当前仅得735个频率点
问题:如何从0.03秒的音频采样中获取完整20000Hz频段的频谱数据
当前每0.03秒只能获取735个音频频率数据,现有代码因每帧采样数为735,仅返回735个数据,但需要得到对应约20000Hz全频段的频谱数据,该怎么实现?
参数说明
path:WAV文件路径second_start:处理起始时间(秒),默认0second_length:处理时长(秒),默认10frame_rate:每秒帧数,默认60
现有Rust代码
fn fft_this(mut buffer: Vec<Complex<f64>>, samples_in_frame: usize) -> Vec<f64> { let mut planner = FftPlanner::new(); let fft = planner.plan_fft_forward(samples_in_frame); fft.process(&mut buffer[..]); let mut frame_data: Vec<f64> = Vec::with_capacity(samples_in_frame); for i in 0..samples_in_frame { frame_data.push(buffer[i].norm()) } return frame_data; } #[tauri::command] pub fn analyze( path: &str, second_start: f64, second_length: f64, frame_rate: f64, ) -> Vec<Vec<f64>> { let wave_file: Wave64 = Wave64::load(path).expect("Could not load wave."); let samples_start: f64 = second_start * wave_file.sample_rate(); let samples_in_each_frame: usize = (wave_file.sample_rate() / frame_rate) as usize; let frames_in_length: usize = (second_length * frame_rate) as usize; let mut wave_buffer: Vec<Vec<f64>> = vec![]; for frame_index in 0..frames_in_length { let mut frame_buffer: Vec<Complex<f64>> = vec![]; for sample_index in 0..samples_in_each_frame { let buffer_index: usize = frame_index * sample_index + samples_start as usize; frame_buffer.push(Complex { re: wave_file.at(0, buffer_index), im: 0.0, }); } let processed_frame: Vec<f64> = fft_this(frame_buffer, samples_in_each_frame); wave_buffer.push(processed_frame); } return wave_buffer; }
解决方案
核心逻辑梳理
要获取20000Hz全频段的频谱,必须满足两个前提:
- 音频文件的采样率必须≥40000Hz(奈奎斯特采样定理:最高可捕获频率为采样率的一半);
- 通过**零填充(Zero Padding)**扩展FFT输入长度,提升频率分辨率,获得更多频段数据点。
FFT的输出点数等于输入长度,零填充不会增加原始音频的信息,但能让频谱曲线更平滑,同时提供更细致的频率划分,覆盖到奈奎斯特频率以内的所有频段。
修改后的代码实现
use rustfft::FftPlanner; use rustfft::num_complex::Complex; use hound::Wave64; fn fft_this(mut buffer: Vec<Complex<f64>>, target_fft_size: usize) -> Vec<f64> { // 零填充到目标FFT长度(优先选2的幂数,FFT计算效率更高) if buffer.len() < target_fft_size { buffer.resize(target_fft_size, Complex { re: 0.0, im: 0.0 }); } let mut planner = FftPlanner::new(); let fft = planner.plan_fft_forward(target_fft_size); fft.process(&mut buffer[..]); // 只保留前半段频谱:FFT结果对称,前半段对应0到采样率/2的有效频段 let mut frame_data: Vec<f64> = Vec::with_capacity(target_fft_size / 2); for i in 0..target_fft_size / 2 { frame_data.push(buffer[i].norm()) } frame_data } #[tauri::command] pub fn analyze( path: &str, second_start: f64, second_length: f64, frame_rate: f64, ) -> Vec<Vec<f64>> { let wave_file: Wave64 = Wave64::load(path).expect("Could not load wave."); let sample_rate = wave_file.sample_rate(); // 校验采样率:必须≥40000Hz才能捕获20000Hz的频率 if sample_rate < 40000 { panic!("音频采样率至少需40000Hz才能获取20000Hz频段数据"); } let samples_start: usize = (second_start * sample_rate as f64) as usize; let samples_in_each_frame: usize = (sample_rate as f64 / frame_rate) as usize; let frames_in_length: usize = (second_length * frame_rate) as usize; // 设置目标FFT大小,比如2048(2的幂数,平衡计算效率和分辨率) let target_fft_size = 2048; let mut wave_buffer: Vec<Vec<f64>> = vec![]; for frame_index in 0..frames_in_length { let mut frame_buffer: Vec<Complex<f64>> = Vec::with_capacity(samples_in_each_frame); // 正确读取当前帧的连续采样点(修复原代码索引计算错误) for sample_offset in 0..samples_in_each_frame { let buffer_index = samples_start + frame_index * samples_in_each_frame + sample_offset; // 超出音频范围时填充0 let sample = if buffer_index < wave_file.len() { wave_file.at(0, buffer_index) } else { 0.0 }; frame_buffer.push(Complex { re: sample, im: 0.0 }); } let processed_frame = fft_this(frame_buffer, target_fft_size); wave_buffer.push(processed_frame); } wave_buffer }
关键修改点
- 采样率校验:添加采样率检查,确保音频能覆盖20000Hz频段;
- 零填充扩展FFT长度:将每帧采样数据填充到目标FFT大小(如2048),获得更多频率点;
- 保留有效频谱段:只返回FFT结果的前半段,避免对称的冗余数据;
- 修复采样索引错误:原代码中
buffer_index计算逻辑错误,修正后正确读取连续的帧采样数据。
内容的提问来源于stack exchange,提问作者thesit123
相关产品推荐
相关产品推荐

