NAudio FFT 返回所有频率的小而相等的幅度值

Posted

技术标签:

【中文标题】NAudio FFT 返回所有频率的小而相等的幅度值【英文标题】:NAudio FFT returns small and equal magnitude values for all frequencies 【发布时间】:2019-04-09 19:13:19 【问题描述】:

我正在使用 NAudio 1.9 进行一个项目,我想为整首歌曲计算 fft,即将歌曲分成大小相等的块并为每个块计算 fft。问题是 NAudio FFT 函数对于频率谱中的任何频率都返回非常小的和相等的值。

我搜索了以前的相关帖子,但似乎没有一个对我有帮助。

使用 NAudio 计算 FFT 的代码:

public IList<FrequencySpectrum> Fft(uint windowSize) 
        IList<Complex[]> timeDomainChunks = this.SplitInChunks(this.audioContent, windowSize);
        return timeDomainChunks.Select(this.ToFrequencySpectrum).ToList();



private IList<Complex[]> SplitInChunks(float[] audioContent, uint chunkSize) 
        IList<Complex[]> splittedContent = new List<Complex[]>();

        for (uint k = 0; k < audioContent.Length; k += chunkSize) 
            long size = k + chunkSize < audioContent.Length ? chunkSize : audioContent.Length - k;
            Complex[] chunk = new Complex[size];

            for (int i = 0; i < chunk.Length; i++) 
                //i've tried windowing here but didn't seem to help me
                chunk[i].X = audioContent[k + i];
                chunk[i].Y = 0;
            

            splittedContent.Add(chunk);
        
        return splittedContent;



private FrequencySpectrum ToFrequencySpectrum(Complex[] timeDomain) 
        int m = (int) Math.Log(timeDomain.Length, 2);
        //true = forward fft
        FastFourierTransform.FFT(true, m, timeDomain);
        return new FrequencySpectrum(timeDomain, 44100);

频谱:

public struct FrequencySpectrum 

    private readonly Complex[] frequencyDomain;

    private readonly uint samplingFrequency;


     public FrequencySpectrum(Complex[] frequencyDomain, uint samplingFrequency) 
        if (frequencyDomain.Length == 0) 
            throw new ArgumentException("Argument value must be greater than 0", nameof(frequencyDomain));
        
        if (samplingFrequency == 0) 
            throw new ArgumentException("Argument value must be greater than 0", nameof(samplingFrequency));
        

        this.frequencyDomain = frequencyDomain;
        this.samplingFrequency = samplingFrequency;
    


    //returns magnitude for freq
    public float this[uint freq] 
        get 
            if (freq >= this.samplingFrequency) 
                throw new IndexOutOfRangeException();
            

            //find corresponding bin
            float k = freq / ((float) this.samplingFrequency / this.FftWindowSize);
            Complex c = this.frequencyDomain[checked((uint) k)];
            return (float) Math.Sqrt(c.X * c.X + c.Y * c.Y);
        
    

对于包含 440Hz 正弦波的文件

预期输出:freq=440 的值为 0.5,其他值为 0

实际输出:频谱中任何频率的值,例如 0.000168153987f

【问题讨论】:

【参考方案1】:

看来我犯了4个错误:

1) 在这里我假设采样频率为 44100。不过,这不是我的代码无法正常工作的原因

return new FrequencySpectrum(timeDomain, 44100);

2) 始终直观地展示您的输出数据!我必须吸取这个教训......似乎对于包含 440Hz 正弦波的文件,我得到了正确的结果,但是......

3) 由于这个原因,频谱与我的预期略有不同:

int m = (int) Math.Log(timeDomain.Length, 2);
FastFourierTransform.FFT(true, m, timeDomain);

timeDomain 是一个大小为 44100 的数组,因为这是 windowSize 的值(我用 windowSize = 44100 调用该方法),但 FFT 方法需要一个值为 2 的窗口大小。我说“这里,NAudio,计算我这个具有 44100 个元素的数组的 fft,但只考虑第一个 32768"。我没有意识到这会对结果产生严重影响:

float k = freq / ((float) this.samplingFrequency / this.FftWindowSize);

这里this.FftWindowSize是一个基于数组大小的属性,而不是m。因此,在将结果可视化后,我发现 440Hz 频率的幅度实际上对应于调用:

spectrum[371]

而不是

spectrum[440]

所以,我的错误是fft的窗口大小(m)与数组的实际长度不对应(FrequencySpectrum.FftWindowSize)。

4) 我收到的幅度值较小是因为我测试代码的音频文件没有以足够的增益录制。

【讨论】:

以上是关于NAudio FFT 返回所有频率的小而相等的幅度值的主要内容,如果未能解决你的问题,请参考以下文章

返回 Numpys FFT 的数据并在音频文件中找到每秒的幅度和频率

FFT后如何得到幅度和对应的频率

Java - 使用 FFT 查找音频信号的频率和幅度

Android音频FFT使用audiorecord检索特定频率幅度

如何获得 iOS Accelerate FFT 结果的频率幅度?

FFT 的返回值(系数)的单位是啥?