1単語の音声の録音を含む.wavファイルから基本周波数を推定しようとしています。
私がやろうとしたことは、audioInputStream でファイルを読み取ることです。形式は、PCM_SIGNED 44100.0 Hz、16 ビット、ステレオ、4 バイト/フレーム、リトルエンディアンです。
したがって、チャネルを 1 つだけ含む新しいバッファを作成しました。このコードは次のことを実現します。
double [] audioRight = new double[audioBytes.length/2];
for(int i = 0, k = 0; i <= audioBytes.length-1; i+=4, k+=2){
audioRight[k]=audioBytes[i];
audioRight[k+1]=audioBytes[i+1];
}
次に、データが 2 倍のサイズの fftBuffer に移動され、DFT が適用されます。使用するライブラリは JTransform です。使用される関数は realForwardFull と呼ばれます。
DoubleFFT_1D fftDo= new DoubleFFT_1D(audioLeft.length);
double[] fftBuffer = new double [audioLeft.length*2];
for (int i = 0; i < audioLeft.length; i++){
fftBuffer[i] = audioLeft[i];
}
fftDo.realForwardFull(fftBuffer);
これにより、パワースペクトルを作成するために各複素数の大きさ/振幅を計算するために使用する複素数のリストが得られます。
振幅を取得するために使用される式 Amplitude=sqrt(IM IM+RE RE)。
これにより、高調波総和法を適用する振幅の配列が得られます。高調波総和は、最高の和を与えるインデックス + 3 高調波が基本周波数を表すインデックスです。
double top_sum = 0;
double first_index = 0;
double sum = 0;
double f_0 = 0;
double FR = audioInputStream.getFormat().getSampleRate()/2/ampBuffer.length;
for (int i = 50; i <= ampBuffer.length/4-1; i++){
sum = ampBuffer[i]+ampBuffer[i*2]+ampBuffer[i*3]+ampBuffer[i*4];
if (top_sum < sum){
top_sum=sum;
first_index = i;
ただし、このインデックスは、正しい頻度ドメインにマップし直す必要があります。私の理解では、(index / fttBuffer.length)*sampleRate.
これにより、基本周波数の推定値が得られます。
ただし、結果は「正しく」ありません。テストするいくつかの異なる .wav ファイルがあり、それらのほとんどで、結果が予想範囲をはるかに超えています。同じ女性の声に対して、3 つの異なる単語の結果は 40、13、および 360 になります。3 つの結果はすべて、およそ 250 ~ 350 の範囲にあると予想されます。
これを引き起こしていると思われる問題のいくつかは、振幅バッファ値です。グラフをプロットすると、高調波を表す明確なピークは表示されません。
グラフのイメージは次のとおりです。
これは多くの情報であったことは承知していますが、情報が多いほど、何が行われたかを理解しやすくなると思います。
RECAP: よくわからないのは、振幅データです。値は理にかなっていますか? それらは正しくプロットされていますか? 高調波を検索して基本周波数を見つける前に、データで何かをする必要がありますか?
ある種のウィンドウ処理を適用することを検討しました。これは、プロットのピークが互いに高調波でない理由は漏れである可能性があるのではないかと疑っているためです。
ヘルプや提案をいただければ幸いです。事前に、あなたの助けに感謝します!
編集:提案されたことへの試みとして:
ByteBuffer buf = ByteBuffer.wrap(audioBytes);
buf.order(ByteOrder.LITTLE_ENDIAN);
double[] audio = new double[audioBytes.length/2];
for(int i = 0; i < audioBytes.length/2; i++) {
short s = buf.getShort();
double mono = (double) s;
double mono_norm = mono / 32768.0;
audio[i]=mono_norm;
}
ここで、pcm データの 1 つのチャネルを配列 audio[] に保存する必要があります。