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

  • 本文关键字:FFT 返回 频率 NAudio 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

似乎我犯了 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);

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

spectrum[371]

而不是

spectrum[440]

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

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

最新更新