web-dev-qa-db-ja.com

Android SdkのFFTライブラリ

Androidプロジェクトで作業しています。Android加速度計データを処理するためにFFTアルゴリズムが必要です。Android SDKにFFTライブラリがありますか?

33
stefy abraham

このクラスを使用できます。これは、リアルタイムのオーディオ分析に十分な速度です。

public class FFT {

  int n, m;

  // Lookup tables. Only need to recompute when size of FFT changes.
  double[] cos;
  double[] sin;

  public FFT(int n) {
      this.n = n;
      this.m = (int) (Math.log(n) / Math.log(2));

      // Make sure n is a power of 2
      if (n != (1 << m))
          throw new RuntimeException("FFT length must be power of 2");

      // precompute tables
      cos = new double[n / 2];
      sin = new double[n / 2];

      for (int i = 0; i < n / 2; i++) {
          cos[i] = Math.cos(-2 * Math.PI * i / n);
          sin[i] = Math.sin(-2 * Math.PI * i / n);
      }

  }

  public void fft(double[] x, double[] y) {
      int i, j, k, n1, n2, a;
      double c, s, t1, t2;

      // Bit-reverse
      j = 0;
      n2 = n / 2;
      for (i = 1; i < n - 1; i++) {
          n1 = n2;
          while (j >= n1) {
              j = j - n1;
              n1 = n1 / 2;
          }
          j = j + n1;

          if (i < j) {
              t1 = x[i];
              x[i] = x[j];
              x[j] = t1;
              t1 = y[i];
              y[i] = y[j];
              y[j] = t1;
          }
      }

      // FFT
      n1 = 0;
      n2 = 1;

      for (i = 0; i < m; i++) {
          n1 = n2;
          n2 = n2 + n2;
          a = 0;

          for (j = 0; j < n1; j++) {
              c = cos[a];
              s = sin[a];
              a += 1 << (m - i - 1);

              for (k = j; k < n; k = k + n2) {
                  t1 = c * x[k + n1] - s * y[k + n1];
                  t2 = s * x[k + n1] + c * y[k + n1];
                  x[k + n1] = x[k] - t1;
                  y[k + n1] = y[k] - t2;
                  x[k] = x[k] + t1;
                  y[k] = y[k] + t2;
              }
          }
      }
  }
}

警告:このコードは here から派生したようで、GPLv2ライセンスがあります。

40
EricLarch

次のクラスを使用: https://www.ee.columbia.edu/~ronw/code/MEAPsoft/doc/html/FFT_8Java-source.html

簡単な説明:fft()を呼び出して、振幅データとしてxyすべてゼロの配列として、関数が返した後、最初の答えはa [0] = x [0] ^ 2 + y [0] ^ 2になります。

完全な説明:[〜#〜] fft [〜#〜]は複雑な変換であり、[〜# 〜] n [〜#〜]複素数および生成[〜#〜] n [〜#〜]複素数。したがって、x [0]は最初の数値の実数部、y [0]は複素数部です。この関数はインプレースで計算するため、関数がxとyを返す場合、変換の実部と複素部があります。

典型的な使用法の1つは、オーディオのパワースペクトルを計算することです。オーディオサンプルには実部のみがあり、複素部は0です。パワースペクトルを計算するには、実部と複素部の2乗P [0] = x [0] ^ 2 + y [0] ^ 2を追加します。

また、フーリエ変換を実数に適用すると、対称的な結果(x [0] == x [x.lenth-1])になることに注意することが重要です。 x [x.length/2]のデータには、周波数f = 0Hzのデータがあります。 x [0] == x [x.length-1]には、サンプリングレートに等しい周波数のデータがあります(たとえば、サンプリングが44000Hzである場合、f [0]は22kHzに戻ります)。

完全な手順:

  1. ゼロの512サンプルで配列p [n]を作成します
  2. 1024個のオーディオサンプルを収集し、xに書き込みます
  3. すべてのnに対してy [n] = 0を設定します
  4. fft(x、y)を計算します
  5. すべてのn = 0〜512に対してp [n] + = x [n + 512] ^ 2 + y [n + 512] ^ 2を計算する
  6. 2に移動して別のバッチを取得します(50バッチが次のステップに移動した後)
  7. プロットp
  8. 1に行く

好みに合わせて固定数を調整するよりも。

512という数字はサンプリングウィンドウを定義するもので、説明しません。減らしすぎないようにしてください。

1024という数値は、常に最後の数値の2倍でなければなりません。

数値50は、更新レートを定義します。サンプリングレートが1秒あたり44000サンプルの場合、更新レートは次のようになります。R= 44000/1024/50 = 0.85秒。

kissfftは、Androidでコンパイルできる十分なライブラリです。 FFTWよりも汎用性の高いライセンスを持っています(FFTWの方が確かに優れていますが)。

LibgdxでkissfftのAndroidバインディングを見つけることができます https://github.com/libgdx/libgdx/blob/0.9.9/extensions/gdx-audio/src/com /badlogic/gdx/audio/analysis/KissFFT.Java

または、純粋なJavaベースのソリューションが必要な場合はjTransformsを試してください https://sites.google.com/site/piotrwendykier/software/jtransforms

7
Matt Esch

これを使用してください class (EricLarchの答えが由来するもの)。

使用上の注意

この関数は、入力配列をFFT出力に置き換えます。

入力

  • N =データポイントの数(入力配列のサイズ、2のべき乗でなければなりません)
  • X =変換されるデータの実部
  • Y =変換されるデータの虚数部

つまり、入力が(1 + 8i、2 + 3j、7-i、-10-3i)の場合

  • N = 4
  • X =(1、2、7、-10)
  • Y =(8、3、-1、-3)

出力

  • X = FFT出力の実部
  • Y = FFT出力の虚数部

従来のFFTグラフを取得するには、実部と虚部の大きさを計算する必要があります。

何かのようなもの:

public double[] fftCalculator(double[] re, double[] im) {
    if (re.length != im.length) return null;
    FFT fft = new FFT(re.length);
    fft.fft(re, im);
    double[] fftMag = new double[re.length];
    for (int i = 0; i < re.length; i++) {
       fftMag[i] = Math.pow(re[i], 2) + Math.pow(im[i], 2);
    }
    return fftMag;
}

また、元の入力が大きさ対時間であった場合に周波数を取得する方法については、 このStackOverflowの答え を参照してください。

4
J Wang

はい、あります JTransformsgithubhere およびMavenプラグインとして利用可能 here .

で使用:

compile group: 'com.github.wendykierp', name: 'JTransforms', version: '3.1'

しかし、最近のGradleバージョンでは、次のようなものを使用する必要があります。

dependencies {
    ... 
    implementation 'com.github.wendykierp:JTransforms:3.1'
}
1
not2qubit

@J Wang出力の大きさは、リンクしたスレッドに与えられた答えよりも優れているようですが、それでも大きさの2乗です...複素数の大きさ

_z = a + ib
_

として計算されます

_|z|=sqrt(a^2+b^2)
_

リンクされたスレッドの答えは、純粋な実入力の場合、出力はa2またはa

_a_(i+N/2) = -a_(i),
_

b_(i) = a_(i+N/2)は、テーブル内の複雑な部分が出力テーブルの後半にあることを意味します。

つまり、実数の入力テーブルの出力テーブルの後半は実数の共役です...

だから_z = a-ia_は大きさを与える

_|z|=sqrt(2a^2) = sqrt(2)a
_

そのため、スケーリング係数に注目する価値があります...これをすべて本またはWikiで確認することをお勧めします。

1
david nash