FFT アルゴリズムを試してみました。インターネットからのFFTアルゴリズムの作業コードとともにNAudioを使用します。私の観察によると、結果のピッチは不正確です。
何が起こるかというと、E2 (ギターの最低音) から約 E6 までのピッチ進行を含む WAV ファイル (44.1khz、16 ビット、モノラル) に変換された MIDI (GuitarPro から生成) があります。低いノート (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);
次に: (関数は float[] のみを受け入れるため、waveBuffer は単純に byte[] を 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;
更新(まだ興味がある場合):
したがって、以下の回答の 1 つは、FFT からの周波数ピークが常にピッチと等しいとは限らないと述べています。という事は承知しています。でもそれなら自分でやってみたかった(周波数のピークが結果のピッチになる場合もあるという前提で)。したがって、基本的に、オーディオ信号の周波数領域を表示できる 2 つのソフトウェア (DewResearch による SpectraPLUS および FFTProperties ; それらのクレジット) を入手しました。
時間領域での周波数ピークの結果は次のとおりです。
スペクトラプラス
および FFT プロパティ:
これは、A2 (110Hz 前後) のテスト ノートを使用して行われました。画像を見ると、SpectraPLUS では 102 ~ 112 Hz、FFT プロパティでは 108 Hz の範囲に周波数のピークがあります。私のコードでは、104Hz を取得します (8192 ブロックと 44.1khz のサンプルレートを使用します ... 8192 を 2 倍にして複雑な入力にするため、最終的には、SpectraPLUS の 10Hz ビンサイズと比較して、ビンサイズが約 5Hz になります。 )。
ソフトウェアでは正しい結果が返されるように見えますが、私のコードでは常に104Hzになるため、今は少し混乱しています(使用したFFT関数をMath.Netなどの他の関数と比較したことに注意してください。正しい)。
問題は私のデータの解釈にあると思いますか? または、ソフトウェアは周波数スペクトルを表示する前に何か他のことをしますか? ありがとう!