【问题标题】:FFT library in android Sdk [closed]android Sdk中的FFT库[关闭]
【发布时间】:2012-03-05 13:06:31
【问题描述】:

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

【问题讨论】:

    标签: java android accelerometer fft


    【解决方案1】:

    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

    【讨论】:

      【解决方案2】:

      你可以使用这个类,它对于实时音频分析来说足够快

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

      【讨论】:

      • fft 函数的 x 和 y 参数是什么?我知道输入样本应该放在 x 数组中,但是 y 的目的是什么?
      • @Pompair 看起来 y 数组是输出表。
      • 这就像我们在这里有一个标志性的“如何不编写代码”示例。一个字符的变量,无用的 cmets,绝对没有解释实际发生的事情。
      • 最后回答数组 y 的含义:它是 FFT 通常复杂输入的虚部。在实数输入的情况下,数组 y 必须在每次调用 fft() 之前用 0 填充。还有关于许可的最后一点说明:此代码几乎与 1960 年代中期的 Cooley/Tukey 算法的标准实现相同(例如,作为four1.c 发表在“Numerical Recipies in C”中)。
      【解决方案3】:

      使用这个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】:

      使用课程: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。

      还需要注意的是,傅里叶变换在应用于实数时会产生对称结果 (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. 情节图
      8. 去1

      比根据你的口味调整固定数字。

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

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

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

      【讨论】:

        【解决方案5】:

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

        z = a + ib
        

        计算为

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

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

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

        b_(i) = a_(i+N/2) 表示他们表中的复杂部分在第二个 输出表的一半。

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

        所以z = a-ia 给出一个幅度

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

        所以值得注意的是比例因子... 我建议您在书中或 wiki 上查找所有这些内容。

        【讨论】:

          【解决方案6】:

          是的,JTransforms 维护在 github here 上,可作为 Maven 插件 here 使用。

          用于:

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

          但是对于更新的 Gradle 版本,您需要使用类似的东西:

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

          【讨论】:

            【解决方案7】:

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

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

            这是我的 cmets 代码,参考 matlab 如何转换任何信号并获取频率幅度 (https://la.mathworks.com/help/matlab/ref/fft.html)

            首先,在build.gradle(app)中添加以下内容

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

            【讨论】:

              猜你喜欢
              • 2012-02-16
              • 2010-12-13
              • 2011-12-10
              • 2011-12-21
              • 2013-05-22
              • 1970-01-01
              • 1970-01-01
              • 2011-02-02
              • 2013-02-20
              相关资源
              最近更新 更多