35

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

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

代替テキスト

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;
}

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

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

4

3 に答える 3

50

周波数(客観的な測定基準) はピッチ(主観的な量)と同じではありません。一般に、ピッチ検出は非常に難しい問題です。

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

FFT の実装に苦労している場合は、FFT は実際には離散フーリエ変換 (DFT) を計算するための効率的なアルゴリズムであることに注意してください。http://en.wikipedia.org/wiki/Discrete_Fourier_transformを参照してください。基本的な DFT アルゴリズムははるかに簡単ですが (ネストされた 2 つのループのみ)、実行速度が大幅に低下します (O(N log N) ではなく O(N^2))。

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

于 2011-01-16T23:12:45.700 に答える
24

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

利用可能な FFT の Java 実装があります。例: 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 は繰り返し信号を想定しているため、入力信号にウィンドウを適用することができます。しかし、最初はこれについて心配する必要はありません。

Web で詳細情報を見つけることができます。例:初心者向けの FFT

また、Oli は、複数の周波数が存在する場合、知覚されるピッチはより複雑な現象であると指摘しています。

于 2011-01-16T23:03:59.937 に答える
2

この問題について、stackoverflowにはにもいくつか 質問があります。多分これらが役立つでしょう。

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

于 2011-01-16T23:14:18.113 に答える