1

Android 携帯用の簡単なピッチ検出アプリケーションを作成しようとしています。計算した自己相関値のグラフを電話に表示させました。これは double の 1 次元配列に格納されています。次に、配列内の繰り返しパターンを検出する方法を理解する必要があります。これは自己相関グラフのスクリーンショットで、一定のピッチでハミングしています。

安定したピッチでハミングしたときの自己相関グラフのスクリーンショット

このスライド デッキにある 1D 配列の再帰的ピーク検出アルゴリズムを実装しようとしました: http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdfしかし、Android でメモリ不足エラーが発生しました。

次に、二次導関数を見つけるためにこのアルゴリズムのようなものを実装しようとしました: https://stackoverflow.com/a/3869172しかし、電話からの自己相関値は非常に不安定で、最小値と最大値が多すぎます。

どうすればよいかを理解する必要があるのは、自己相関データにある種のフィルターを適用してそれを滑らかにすることですが、私は数学が苦手で、何をすべきかわかりません。自己相関値を小数点以下数桁に丸めようとしましたが、探していた結果が得られませんでした。

基本的に、繰り返しパターンの全体的な最大値 (実際には最初のものだけで十分でしょう) を見つける方法を理解するのに助けが必要です。上のスクリーンショットでは、パターンは高いピークとそれに続く 2 つの短いピークです。ピッチを計算できるように、2 番目の高いピークがいつ発生するかを知る必要があります。

4

2 に答える 2

0

私自身の質問に答えるために、これが私がやったことです。(申し訳ありませんが、この質問に戻って回答を投稿するのに時間がかかりました。)

double frequency = findFrequency(lowPassFilter(signal));

private double findFrequency(double[] signal) {
    int[] signs = new int[signal.length];
    for (int i = 0; i < signal.length - 1; i++) {
        double diff = signal[i+1] - signal[i];
        if (diff < 0) {
            signs[i] = -1;
        } else if (diff == 0) {
            signs[i] = 0;
        } else {
            signs[i] = 1;
        }
    }
    int[] secondDerivatives = new int[signs.length];
    for (int i = 0; i < signs.length - 1; i++) {
        secondDerivatives[i] = signs[i+1] - signs[i];
    }
    double biggestSoFar = 0.0;
    int indexOfBiggestSoFar = 0;
    for (int i = 0; i < secondDerivatives.length; i++) {
        if (secondDerivatives[i] == -2 && signal[i] > biggestSoFar) {
            biggestSoFar = signal[i];
            indexOfBiggestSoFar = i;
        }
    }
    return 1 / (double)indexOfBiggestSoFar * AudioListener.SAMPLE_RATE;
}

private double[] lowPassFilter(double[] signal) {
    double alpha = 0.15;
    for (int i = 1; i < signal.length; i++ ) {
        signal[i] = signal[i] + alpha * (signal[i] - signal[i-1]);
    }
    return signal;
}
于 2015-10-29T08:38:28.510 に答える