我正在研究一个带有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,计算我的这个阵列的fft有44100个元素,但只考虑第一个32768“。我没有意识到这会对结果产生严重影响:
float k = freq / ((float) this.samplingFrequency / this.FftWindowSize);
这里this.FftWindowSize是一个基于数组大小的属性,而不是m。因此,在对结果进行可视化后,我发现440Hz频率的幅度实际上与调用相对应:
spectrum[371]
代替
spectrum[440]
所以,我的错误是fft(m)的窗口大小与数组的实际长度(FrequencySpectrum.FftWindowSize)不对应。
4)我收到的大小的值来自于我正在测试我的代码的音频文件没有记录足够的增益。