You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中FFT如何获取全频段频谱?当前仅得735个频率点

问题:如何从0.03秒的音频采样中获取完整20000Hz频段的频谱数据

当前每0.03秒只能获取735个音频频率数据,现有代码因每帧采样数为735,仅返回735个数据,但需要得到对应约20000Hz全频段的频谱数据,该怎么实现?

参数说明

  • path:WAV文件路径
  • second_start:处理起始时间(秒),默认0
  • second_length:处理时长(秒),默认10
  • frame_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全频段的频谱,必须满足两个前提:

  1. 音频文件的采样率必须≥40000Hz(奈奎斯特采样定理:最高可捕获频率为采样率的一半);
  2. 通过**零填充(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
}

关键修改点

  1. 采样率校验:添加采样率检查,确保音频能覆盖20000Hz频段;
  2. 零填充扩展FFT长度:将每帧采样数据填充到目标FFT大小(如2048),获得更多频率点;
  3. 保留有效频谱段:只返回FFT结果的前半段,避免对称的冗余数据;
  4. 修复采样索引错误:原代码中buffer_index计算逻辑错误,修正后正确读取连续的帧采样数据。

内容的提问来源于stack exchange,提问作者thesit123

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 01:55:15