FFT不精确C#
我一直在试验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的频率峰值并不总是等于音调。我明白那个。但是,如果是这种情况,我想为自己尝试一些事情(假设有时频率峰值是最终的音调)。所以基本上,我有2个软件(DewResearch的SpectraPLUS和FFTProperties;相信它们)能够显示音频信号的频域。
以下是时域中频率峰值的结果:
SpectraPLUS
和FFT属性:
这是使用A2的测试笔记(大约110Hz)完成的。在查看图像时,SpectraPLUS的频率峰值在102-112 Hz附近,FFT属性的频率峰值在108 Hz附近。在我的代码中,我得到104Hz(我使用8192个块,44.1khz的采样率... 8192然后加倍使其复杂输入,所以最后,我得到大约5Hz的binsize,与SpectraPLUS的10Hz binsize相比)。
所以现在我有点困惑,因为在软件上他们似乎返回了正确的结果,但在我的代码上,我总是得到104Hz(注意我已经比较了我和其他人使用的FFT函数,如Math.Net,它似乎是正确的)。
您是否认为问题可能与我对数据的解释有关?或者,在显示频谱之前,软件是否会做其他事情?谢谢!
没有找到相关结果
已邀请:
4 个回复
诉嘎归亮
,其中
是采样率,
是FFT的大小 对于音阶较低的音符,连续音符之间的频率差异相对较小,因此你需要一个足够大的N来区分半音的音符(见下面的注1) 第一个bin(索引0)包含以0 Hz为中心的能量,但包括来自
的能量 bin
包含以
为中心的能量,但包括来自该中心频率两侧
的能量 你会得到相邻垃圾箱的光谱泄漏 - 这有多糟糕取决于你使用的窗口功能 - 没有窗口(==矩形窗口)和光谱泄漏会非常糟糕(非常宽的峰值) - 对于频率估算你要选择一个窗口功能,为您提供尖锐的峰值 音高与频率不同 - 音高是感知,频率是物理量 - 乐器的感知音高可能与基频略有不同,具体取决于乐器的类型(有些乐器甚至不会产生显着的音高)能量在它们的基频上,但我们仍然认为它们的音调好像基本存在一样) 我从可用的有限信息中得到的最好的猜测是,你可能在bin索引转换为频率的某个地方“一个接一个”,或者你的FFT太小而无法为低音提供足够的分辨率,你可能需要增加N. 您还可以通过几种技术(例如倒谱分析)或查看FFT输出的相位分量并将其与连续FFT进行比较来改善音高估计(这样可以在给定FFT大小的bin中进行更准确的频率估计)。 笔记 (1)只是为此加上一些数字,E2为82.4 Hz,F2为87.3 Hz,所以你需要一个比5 Hz更好的分辨率来区分吉他上最低的两个音符(如果你实际上要比这个更精细)想做,比方说,准确的调整)。在44.1 kHz样本处,您可能需要至少N = 8192的FFT才能获得足够的分辨率(44100/8192 = 5.4 Hz),可能N = 16384会更好。
埃庐
字符串1,基本= 329.6 Hz: 字符串2,基本= 246.9 Hz: 字符串3,基本= 196.0 Hz: 字符串4,基本= 146.8 Hz: 字符串5,基本= 110.0 Hz: 字符串6,基本= 82.4 Hz: 基频并不总是主要的谐波。它确定周期信号的谐波之间的间隔。
茬贺努充尽
炬卤遁蝎变