web-dev-qa-db-ja.com

音のピッチ(周波数)をグラフ化する

音の高さをグラフにプロットしたい。

現在、振幅をプロットできます。以下のグラフは、getUnscaledAmplitude()によって返されたデータによって作成されます。

alt text

AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new BufferedInputStream(new FileInputStream(file)));
byte[] bytes = new byte[(int) (audioInputStream.getFrameLength()) * (audioInputStream.getFormat().getFrameSize())];
audioInputStream.read(bytes);

// Get amplitude values for each audio channel in an array.
graphData = type.getUnscaledAmplitude(bytes, 1);


public int[][] getUnscaledAmplitude(byte[] eightBitByteArray, int nbChannels)
{
    int[][] toReturn = new int[nbChannels][eightBitByteArray.length / (2 * nbChannels)];
    int index = 0;

    for (int audioByte = 0; audioByte < eightBitByteArray.length;)
    {
        for (int channel = 0; channel < nbChannels; channel++)
        {
            // Do the byte to sample conversion.
            int low = (int) eightBitByteArray[audioByte];
            audioByte++;
            int high = (int) eightBitByteArray[audioByte];
            audioByte++;
            int sample = (high << 8) + (low & 0x00ff);

            toReturn[channel][index] = sample;
        }
        index++;
    }

    return toReturn;
}

ただし、振幅ではなく、オーディオのピッチを表示する必要があります。 高速フーリエ変換 はピッチを取得するように見えますが、私が持っている生のバイトよりも多くの変数を知る必要があり、非常に複雑で数学的です。

これを行う方法はありますか?

34
Amy B

頻度(客観的な指標)はpitch(a主観的な量)。一般に、ピッチ検出は非常に難しい問題です。

今のところ周波数応答をグラフ化したいだけの場合、FFTを使用する以外に選択肢はほとんどありません[〜#〜] [〜#〜]時間領域データの周波数応答を取得する方法。 (まあ、離散コサイン変換のような他の方法もありますが、それらは実装するのと同じくらい扱いにくく、解釈するのがより難しいです)。

FFTの実装に苦労している場合、それは実際には離散フーリエ変換(DFT)を計算するための効率的なアルゴリズムにすぎないことに注意してください。 http://en.wikipedia.org/wiki/Discrete_Fourier_transform を参照してください。基本的なDFTアルゴリズムははるかに簡単です(2つのループが入れ子になっているだけ)が、lotがO(N logではなくO(N ^ 2)より遅く実行されますN))。

周波数の内容を単にプロットすることよりも複雑なことをしたい場合(ピッチ検出やウィンドウ処理(他の人が示唆しているように)など)、数学の意味を学習する必要があると思います。

49

高速フーリエ変換は、持っている入力バイト以上のものを知る必要はありません。ウィキペディアの記事に怖がらないでください。 FFTアルゴリズムは入力信号を取り(一般的なFFTアルゴリズムでは、サンプル数は2の累乗である必要があります(例:256、512、1024))、同じサイズの複素数のベクトルを返します。入力は複素数ではなく実数なので(虚数部をゼロに設定)、返されるベクトルは対称になります。その半分だけがデータを含みます。位相は気にしないので、単純に複素数の大きさ、つまりsqrt(a ^ 2 + b ^ 2)をとることができます。複素数のabsoulte値を取るだけでも機能する場合があります。一部の言語では、これは前の式と同等です。

Java FFTの実装が利用可能です。例: http://www.cs.princeton.edu/introcs/97data/FFT.Java.html

擬似コードは次のようになります。

Complex in[1024];
Complex out[1024];
Copy your signal into in
FFT(in, out)
for every member of out compute sqrt(a^2+b^2)
To find frequency with highest power scan for the maximum value in the first 512 points in out

出力には、サンプリング周波数のゼロと半分の間の周波数の全体が含まれます。

FFTは繰り返し信号を想定しているため、入力信号に window を適用することができます。しかし、最初はこれについて心配する必要はありません。

あなたはウェブ上でより多くの情報を見つけることができます、例: FFT for beginners

また、複数の周波数が存在する場合のOliによると、知覚されるピッチはより複雑です 現象

24
Guy Sirton

この問題については いくつかその他質問stackoverflow があります。多分これらは助けるでしょう。

代わりに、Craig Lindleyの Digital Audio with Java のコピーを見つけてみてください。もう印刷されているとは思いませんが、私の机のコピーにはFFTのセクションがあり、ギターチューナーのサンプルアプリケーションもあります。

2
Stewart Murrie