我正在使用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
答案 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)我收到的幅度值很小,是因为我测试代码的音频文件未获得足够的增益记录。