我一直在尝试使用FFT算法。我使用NAudio和从互联网上获得的FFT算法工作代码。根据我的性能观察,结果音高不准确。
问题在于,我有一个MIDI文件(由GuitarPro生成),转换为WAV文件(44.1khz,16位,单声道),其中包含从E2(低音吉他音符)开始到大约E6的音高变化。结果是对于较低的音符(约为E2-B3),通常是非常错误的。但是达到C4时,它在某种程度上是正确的,因为您已经可以看到正确的音高变化(下一个音符是C#4,然后是D4等等)。然而,问题在于检测到的音高比实际音高低半个音符(例如,C4应该是音符,但显示为D#4)。
您认为可能出了什么问题?如果需要,我可以发布代码。非常感谢!我还在开始理解DSP领域。
编辑:这是我正在做的简略草图
byte[] buffer = new byte[8192];
int bytesRead;
do
{
bytesRead = stream16.Read(buffer, 0, buffer.Length);
} while (bytesRead != 0);
然后:(waveBuffer只是一个类,用于将byte []转换为float [],因为该函数仅接受float [])
public int Read(byte[] buffer, int offset, int bytesRead)
{
int frames = bytesRead / sizeof(float);
float pitch = DetectPitch(waveBuffer.FloatBuffer, frames);
}
最后一点:(Smbpitchfft是拥有FFT算法的类...我相信它没有问题所以我不会在这里发布它)
private float DetectPitch(float[] buffer, int inFrames)
{
Func<int, int, float> window = HammingWindow;
if (prevBuffer == null)
{
prevBuffer = new float[inFrames]; //only contains zeroes
}
// double frames since we are combining present and previous buffers
int frames = inFrames * 2;
if (fftBuffer == null)
{
fftBuffer = new float[frames * 2]; // times 2 because it is complex input
}
for (int n = 0; n < frames; n++)
{
if (n < inFrames)
{
fftBuffer[n * 2] = prevBuffer[n] * window(n, frames);
fftBuffer[n * 2 + 1] = 0; // need to clear out as fft modifies buffer
}
else
{
fftBuffer[n * 2] = buffer[n - inFrames] * window(n, frames);
fftBuffer[n * 2 + 1] = 0; // need to clear out as fft modifies buffer
}
}
SmbPitchShift.smbFft(fftBuffer, frames, -1);
}
关于结果的解释:
float binSize = sampleRate / frames;
int minBin = (int)(82.407 / binSize); //lowest E string on the guitar
int maxBin = (int)(1244.508 / binSize); //highest E string on the guitar
float maxIntensity = 0f;
int maxBinIndex = 0;
for (int bin = minBin; bin <= maxBin; bin++)
{
float real = fftBuffer[bin * 2];
float imaginary = fftBuffer[bin * 2 + 1];
float intensity = real * real + imaginary * imaginary;
if (intensity > maxIntensity)
{
maxIntensity = intensity;
maxBinIndex = bin;
}
}
return binSize * maxBinIndex;
更新(如果有人仍然感兴趣):
所以,下面的一个回答指出FFT中的频率峰值并不总是等同于音高。我理解这个道理。但是,我想自己尝试一些东西,看看是否如此(在假设有时频率峰值就是结果音高的情况下)。因此,我得到了两个软件(SpectraPLUS和FFTProperties by DewResearch;感谢他们)可以显示音频信号的频域。
接下来是时间域内频率峰值的结果:
SpectraPLUS
和FFT Properties:
这是使用 A2 测试音符(约为 110Hz)完成的。在查看图片时,它们的频率峰值约在 102-112 Hz 范围内(SpectraPLUS)和 108 Hz(FFT Properties)。在我的代码中,我得到了 104Hz(我使用 8192 块和 44.1khz 的采样率…然后将 8192 倍增加为复杂输入,因此最终获得 binsize 约为 5Hz,相对于 SpectraPLUS 的 10Hz binsize)。
现在我有点困惑了,因为在软件上它们似乎返回了正确的结果,但是在我的代码中,我总是得到 104Hz(请注意,我已经将所使用的 FFT 函数与 Math.Net 等其他函数进行了比较,并且似乎是正确的)。
您认为问题可能出现在我的数据解读上吗?还是软件在显示频率谱之前做了其他事情?谢谢!
prevBuffer
元素从未被设置,因此其值始终为0。这是正确的行为吗? - linquize