android Sdk中的FFT库

时间:2012-02-14 05:36:50

标签: java android accelerometer fft

我正在使用android项目。我需要FFT算法来处理android加速度计数据。在android sdk中有FFT库吗?

7 个答案:

答案 0 :(得分:40)

您可以使用此类,该类足够快以进行实时音频分析

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许可。

答案 1 :(得分:11)

使用以下课程:https://www.ee.columbia.edu/~ronw/code/MEAPsoft/doc/html/FFT_8java-source.html

简短说明:在函数返回后,调用 fft()在调整数据时调用 x y 作为全零数组第一个答案是a [0] = x [0] ^ 2 + y [0] ^ 2.

完整解释: FFT 是复数变换,它需要 N 复数并产生 N 复数。所以x [0]是第一个数字的实部,y [0]是复数部分。此函数就地计算,因此当函数返回x时,y将具有变换的实部和复杂部分。

一种典型用法是计算音频的功率谱。你的音频样本只有实部,你的复杂部分是0.要计算功率谱,你需要加上实部和复数部分的平方P [0] = x [0] ^ 2 + y [0] ^ 2。< / p>

同样重要的是要注意傅立叶变换,当应用于实数时,会产生对称结果(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
  4. 设置y [n] = 0
  5. 计算fft(x,y)
  6. 计算所有n = 0到512的p [n] + = x [n + 512] ^ 2 + y [n + 512] ^ 2
  7. 去2再进行另一批(50批次进入下一步)
  8. plot p
  9. 转到1
  10. 根据您的口味调整固定数量。

    数字512定义了采样窗口,我不解释它。只是避免减少太多。

    1024的数字必须始终是最后一个数字的两倍。

    数字50定义了更新率。如果您的采样率是每秒44000个样本,您的更新率将是:R = 44000/1024/50 = 0.85秒。

答案 2 :(得分:7)

kissfft是一个足够体面的库,可以在android上编译。它具有比FFTW更通用的许可证(即使FFTW无疑更好)。

你可以在libgdx https://github.com/libgdx/libgdx/blob/0.9.9/extensions/gdx-audio/src/com/badlogic/gdx/audio/analysis/KissFFT.java

中找到一个用于kissfft的android绑定

或者,如果您想要基于纯Java的解决方案,请尝试jTransforms https://sites.google.com/site/piotrwendykier/software/jtransforms

答案 3 :(得分:4)

使用此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;
}

如果您的原始输入是幅度与时间的关系,请参阅this StackOverflow answer了解如何获取频率。

答案 4 :(得分:1)

@J王 您的输出幅度似乎比您链接的线程上给出的答案更好但是仍然是幅度平方...复数的大小

z = a + ib

计算为

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

链接线程中的答案表明,对于纯粹的实际输入,输出 应该使用 a 2 a 作为输出,因为

的值
a_(i+N/2) = -a_(i),
带有b_(i) = a_(i+N/2)

表示其表中的复杂部分位于第二个中 输出表的一半。

,实数输入表的输出表的后半部分是真实的共轭...

所以z = a-ia给出幅度

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

因此值得注意的是缩放因子...... 我建议在书中或维基上查看所有内容以确定。

答案 5 :(得分:0)

是的,有JTransforms保留在 github here上,可以作为 Maven 插件here使用。

用于:

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

但是对于最近的Gradle版本,您需要使用类似以下内容:

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

答案 6 :(得分:0)

不幸的是,最上面的答案仅适用于大小为2的幂的Array,这是非常有限的。

我使用了Jtransforms库,它运行完美,可以将其与Matlab使用的函数进行比较。

这是我的代码,其中带有注释,其引用了matlab如何转换任何信号并获取频率幅度(https://la.mathworks.com/help/matlab/ref/fft.html

首先,在build.gradle(应用程序)中添加以下内容

implementation 'com.github.wendykierp:JTransforms:3.1'

这是用于转换简单正弦波的代码,其工作原理就像一个魅力

    double Fs = 8000;
    double T = 1/Fs;
    int L = 1600;

    double freq = 338;

    double sinValue_re_im[] = new double[L*2]; // because FFT takes an array where its positions alternate between real and imaginary
    for( int i = 0; i < L; i++)
    {
        sinValue_re_im[2*i] = Math.sin( 2*Math.PI*freq*(i * T) ); // real part
        sinValue_re_im[2*i+1] = 0; //imaginary part
    }

    // matlab
    // tf = fft(y1);

    DoubleFFT_1D fft = new DoubleFFT_1D(L);
    fft.complexForward(sinValue_re_im);
    double[] tf = sinValue_re_im.clone();

    // matlab
    // P2 = abs(tf/L);
    double[] P2 = new double[L];
    for(int i=0; i<L; i++){

        double re = tf[2*i]/L;
        double im = tf[2*i+1]/L;
        P2[i] = sqrt(re*re+im*im);
    }

    // P1 = P2(1:L/2+1);
    double[] P1 = new double[L/2]; // single-sided: the second half of P2 has the same values as the first half
    System.arraycopy(P2, 0, P1, 0, L/2);
    // P1(2:end-1) = 2*P1(2:end-1);
    System.arraycopy(P1, 1, P1, 1, L/2-2);
    for(int i=1; i<P1.length-1; i++){
        P1[i] = 2*P1[i];
    }
    // f = Fs*(0:(L/2))/L;
    double[] f = new double[L/2 + 1];
    for(int i=0; i<L/2+1;i++){
        f[i] = Fs*((double) i)/L;
    }