我正在尝试使用自相关从录制的语音(44.1 kHz)中获取音高等级。我在做什么基本上在这里描述:http://cnx.org/content/m11714/latest/也在这里实现:http ://code.google.com/p/yaalp/source/browse/trunk/csaudio /WaveAudio/WaveAudio/PitchDetection.cs(使用PitchDetectAlgorithm.Amdf的部分)
因此,为了检测音级,我建立了一个具有 C2 到 B3 频率(2 个八度音阶)的归一化相关性的数组,并选择具有最高值的数组(首先进行“1 - 相关性”转换,因此不搜索最小但最大)
我用生成的音频(简单的窦)测试了它:
data[i] = (short)(Math.Sin(2 * Math.PI * i/fs * freq) * short.MaxValue);
但它只适用于低于 B4 的输入频率。研究生成的数组,我发现从 G3 开始,另一个 peek 进化了,最终变得比正确的更大。并且我的 B4 被检测为 E。更改分析频率的数量根本没有帮助。
我的缓冲区大小为 4000 个样本,B4 的频率约为 493Hz,所以我想不出失败的原因。频率或缓冲区大小是否有更多限制?那里出了什么问题?
我知道我可以像 Performous 一样使用 FFT,但是使用这种方法看起来很简单,并且还提供了可用于显示可视化的加权频率。我不想那么轻易地把它扔掉,至少明白为什么会失败。
更新:使用的核心功能:
private double _GetAmdf(int tone)
{
int samplesPerPeriod = _SamplesPerPeriodPerTone[tone]; // samples in one period
int accumDist = 0; // accumulated distances
int sampleIndex = 0; // index of sample to analyze
// Start value= index of sample one period ahead
for (int correlatingSampleIndex = sampleIndex + samplesPerPeriod; correlatingSampleIndex < _AnalysisBufLen; correlatingSampleIndex++, sampleIndex++)
{
// calc distance (correlation: 1-dist/IntMax*2) to corresponding sample in next period (0=equal .. IntMax*2=totally different)
int dist = Math.Abs(_AnalysisBuffer[sampleIndex] - _AnalysisBuffer[correlatingSampleIndex]);
accumDist += dist;
}
return 1.0 - (double)accumDist / Int16.MaxValue / sampleIndex;
}
使用该功能,音高/音调是(伪代码)
tone = Max(_GetAmdf(tone)) <- for tone = C2..
我还尝试使用实际的自相关:
double accumDist=0;
//...
double dist = _AnalysisBuffer[sampleIndex] * _AnalysisBuffer[correlatingSampleIndex];
//...
const double scaleValue = (double)Int16.MaxValue * (double)Int16.MaxValue;
return accumDist / (scaleValue * sampleIndex);
但是除了 B4 作为 E 之外,还没有获得 A3 作为 D
注意:我不是除以 Bufferlength 而是除以实际比较的样本数。不确定这是否正确,但这似乎是逻辑。