【发布时间】:2011-06-25 08:47:20
【问题描述】:
我一直在试验 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;感谢他们)。
以下是时域中频率峰值的结果:
光谱加
和 FFT 属性:
这是使用 A2(大约 110Hz)的测试笔记完成的。查看图像后,SpectraPLUS 的频率峰值范围为 102-112 Hz,FFT 属性的频率峰值范围为 108 Hz。在我的代码中,我得到 104Hz(我使用 8192 个块和 44.1khz 的采样率......然后将 8192 加倍以使其成为复杂输入,所以最后,与 SpectraPLUS 的 10Hz binsize 相比,我得到大约 5Hz 的 binsize )。
所以现在我有点困惑,因为在软件上它们似乎返回了正确的结果,但在我的代码上,我总是得到 104Hz(请注意,我已经将我使用的 FFT 函数与其他函数进行了比较,例如 Math.Net 和这似乎是正确的)。
您认为问题可能出在我对数据的解释上吗?或者软件在显示频谱之前会做其他事情吗?谢谢!
【问题讨论】:
-
嗨!我为 maxBinIndex 得到的值是 bin 20(大约 100-104 Hz),结果大约是 G#,这是假定 A 的半音。这与其他 .wav 文件一致,有时是一个完整的步骤下来。
-
@eryksun 谢谢!你的最后一点很有趣。我会试着调查一下。
-
@eryksun 你好!非常感谢!这似乎是问题所在。我的代码现在可以工作并返回正确的频率。似乎我从 Paul R 的回答中错过了这个解决方案,因为那时我对 FFT 还不太了解。但是,感谢您的帮助,我学到了很多东西。再次感谢!
-
但是,
prevBuffer元素从未设置,因此值始终为 0。这是正确的行为吗?
标签: c# signal-processing fft pitch pitch-tracking