NAudio FFT对所有频率返回较小且相等的幅度值

时间:2019-04-09 19:13:19

标签: c# fft naudio

我正在使用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 个答案:

答案 0 :(得分:2)

看来我犯了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的幂次幂的窗口大小。计算这个数组的fft,它有44100个元素,但仅考虑前32768“。我没有意识到这会对结果产生严重影响:

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

这里。FftWindowSize是基于数组大小的属性,而不是基于 m 的属性。因此,在可视化结果之后,我发现440Hz频率的幅度实际上与该呼叫相对应:

spectrum[371]

代替

spectrum[440]

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

4)我收到的幅度值很小,是因为我测试代码的音频文件未获得足够的增益记录。