自己相関を使用して、録音された音声 (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 から始まる別のピークが進化し、最終的に正しいものよりも大きくなることがわかりました。私の 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);
しかし、それは E としての B4 に加えて D としての A3 の取得に失敗します
注: Bufferlength で除算するのではなく、実際に比較したサンプル数で除算します。これが正しいかどうかはわかりませんが、論理のようです。