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

NAudio FFT returns small and equal magnitude values for all frequencies

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

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

使用 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

看来我犯了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 方法需要一个 window 大小价值幂为 2。我说的是 "Here, NAudio, compute me the fft of this array that has 44100 elements, but take into account only the first 32768"。我没有意识到这会对结果产生严重影响:

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

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

spectrum[371]

而不是

spectrum[440]

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

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