3

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[] に保存する必要があります。

4

1 に答える 1

1

一般的なヒント:

あなたは、ある話し言葉の基本周波数を推定しようとしていると言います。「単語」は、複数の子音と母音 (またはより適切な音素) で構成されます。それぞれの「母音」は異なる基本周波数を持ち、ほとんどの場合、周波数は 1 つの母音内でも変化します (これが文の「メロディー」を生成します)。これは、音声の非常に短い間隔の基本周波数/ピッチを推定し、母音を見ていることを確認する必要があることを意味します (子音はノイズの一種であり、周期成分を持っています)。

したがって、最初のステップは、単語のスペクトログラムを生成することです。

次に、関心のある部分の Short-Term-FFT を計算し、高調波の合計を進めることができます。

ただし、短期自己相関関数を使用すると、より良い結果が得られます。

その他の研究事項: ピッチ検出、ケプストラム

于 2015-02-07T15:21:00.053 に答える