1

楽器からオーディオ周波数を抽出する Android アプリを開発しようとしています。Jtransformsで高速フーリエ変換法を使用しています。これが私がこれまでに持っているものです:

public class MainActivity extends Activity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    new readFrequencies().execute();
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

private class readFrequencies extends AsyncTask<Void,Integer,Integer> {

        @Override
        protected Integer doInBackground(Void... arg0) {
            AudioRecord recorder = null;
            int bufferSize = 0;
            boolean recording = true;

            int rate = 8000;
            short audioFormat = AudioFormat.ENCODING_PCM_16BIT;
            short channelConfig = AudioFormat.CHANNEL_IN_MONO;

            try {
                bufferSize = AudioRecord.getMinBufferSize(rate,channelConfig, audioFormat);

                recorder = new AudioRecord(AudioSource.DEFAULT, rate, 
                    channelConfig, audioFormat, bufferSize);

                if (recorder.getState() == AudioRecord.STATE_INITIALIZED) {
                    /*
                     *  Android 4.1.2
                     * 
                    int recorder_id = recorder.getAudioSessionId();
                    if (NoiseSuppressor.isAvailable()) NoiseSuppressor.create(recorder_id);
                    */
                }
                else {
                    Toast.makeText(getApplicationContext(), 
                            "Error en la inicialización", Toast.LENGTH_SHORT).show();
                }
            } catch (Exception e) {}

            short[] audioData = new short[bufferSize];

            if (recorder != null) {
                while (recording) {
                    if (recorder.getRecordingState() == AudioRecord.RECORDSTATE_STOPPED) {
                        recorder.startRecording();
                    }
                    else {
                        int numshorts = recorder.read(audioData,0,audioData.length);
                        if ((numshorts != AudioRecord.ERROR_INVALID_OPERATION) && 
                            (numshorts != AudioRecord.ERROR_BAD_VALUE)) {

                            //  Hann
                            double[] preRealData = new double[bufferSize];
                            double PI = 3.14159265359;
                            for (int i = 0; i < bufferSize; i++) {
                                double multiplier = 0.5 * (1 - Math.cos(2*PI*i/(bufferSize-1)));
                                preRealData[i] = multiplier * audioData[i];
                            }

                            DoubleFFT_1D fft = new DoubleFFT_1D(bufferSize);
                            double[] realData = new double[bufferSize * 2];

                            for (int i=0;i<bufferSize;i++) {
                                realData[2*i] = preRealData[i];
                                realData[2*i+1] = 0;    
                            }
                            fft.complexForward(realData);

                            double magnitude[] = new double[bufferSize / 2];

                            for (int i = 0; i < magnitude.length; i++) {
                                double R = realData[2 * i];
                                double I = realData[2 * i + 1];

                                magnitude[i] = Math.sqrt(I*I + R*R);
                            }

                            int maxIndex = 0;
                            double max = magnitude[0];
                            for(int i = 1; i < magnitude.length; i++) {
                                if (magnitude[i] > max) {
                                    max = magnitude[i];
                                    maxIndex = i;
                                }
                            }

                            int frequency = rate * maxIndex / bufferSize;
                            publishProgress(frequency);
                        }
                        else {
                            if (numshorts == AudioRecord.ERROR_BAD_VALUE) {
                                Toast.makeText(getApplicationContext(), 
                                        "ERROR_BAD_VALUE", Toast.LENGTH_SHORT).show();
                            }
                            else {
                                Toast.makeText(getApplicationContext(), 
                                        "ERROR_INVALID_OPERATION", Toast.LENGTH_SHORT).show();
                            }

                            return -1;
                        }
                    }
                }

                if (recorder.getState() == AudioRecord.RECORDSTATE_RECORDING) 
                    recorder.stop(); //stop the recorder before ending the thread
                recorder.release();
                recorder=null;
            }
            return 0;
        }

        protected void onProgressUpdate(Integer... f) {
            TextView texto = (TextView) findViewById(R.id.texto);
            texto.setText(String.valueOf(f[0]));
        }

        protected void onPostExecute(Integer f) {
            TextView texto = (TextView) findViewById(R.id.texto);
            int frecuencias = f.intValue();
            texto.setText(String.valueOf(frecuencias));
        }
}

}

このコードを使用すると、純粋な信号を生成する周波数発生器から正確な周波数を取得できます。しかし、楽器で同じことを試みると、ランダムな周波数が得られます。実際の楽器に関して言えば、生成される信号には最終結果に影響を与える可能性のある高調波が含まれていることは知っていますが、この場合の実際の周波数を取得する方法はわかりません。誰でも私を助けることができますか?

TarsosDSP を使用し、自己相関法も試しましたが、望み通りの結果が得られませんでした。

前もって感謝します。

4

2 に答える 2

7

スペクトル内の最も強力な周波数成分と、人間のリスナーが知覚するピッチとの間には大きな違いがあります。

この学術論文は、おそらくピッチ検出の問題のいくつかを解決するためのアプローチの決定的なレビューですが、実際の信号を使用して対処する必要がある知覚の問題には対処していません. 少なくとも、音符の最初と最後でオクターブをジャンプする弦楽器と管楽器、および欠落している基音を考慮する必要があります。これは、デバイスのマイクが一貫していない Android での特定の宝くじになります。

スペクトル内のピーク周波数を検索することは、自己相関を使用することに勝るものはなく、実際の信号ではしばしば間違っています。

FFT アプローチ (より正確には、STFT と位相差からの周波数推定を使用) を機能させることができますが、スペクトルの多くの後処理が必要です。市販のクロマチック チューナー アプリの中には、FFT ベースのアプローチを使用しているものがいくつかあります (これは 1 つ書いたので証明できます)。

Sonic Visualiserの特徴抽出プラグインのいくつかがこの問題をどのように解決しているかをご覧になりたいと思われるかもしれません。

また、ほとんど同じことを尋ねている過去の膨大な数の質問を確認することをお勧めします - 通常、質問者はギター チューナーを構築したいと考えています。

于 2013-07-04T19:35:37.843 に答える
0

価値のあることですが、楽器用の電子チューナー(ギターチューナーなど)はこのようにはしないと思います。あなたがやっているようにFFTを行う代わりに、彼らは単に波の周期を測定しています - (つまり、ゼロクロス間の時間)、周期から周波数を計算します。

于 2013-07-04T19:05:10.070 に答える