Android Sdk 中的 FFT 库

发布于 2025-01-06 02:20:44 字数 1539 浏览 0 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

梦初启 2025-01-13 02:20:44

您可以使用此类,该类的速度足以进行实时音频分析

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

警告:此代码似乎源自 此处,并拥有 GPLv2 许可证。

You can use this class, which is fast enough for real time audio analysis

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

Warning: this code appears to be derived from here, and has a GPLv2 license.

梦里泪两行 2025-01-13 02:20:44

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

简短说明:调用 fft() 提供 x 作为幅度数据, 作为全零数组,函数返回后您的第一个答案将是 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. 计算 p[n]+=x[n +512]^2+y[n+512]^2 对于所有 n=0 到 512
  6. 去 2 采取另一批(50 批后进入下一步)
  7. 情节 p
  8. 去 1

比调整根据您的口味固定数量。

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

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

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

Using the class at: https://www.ee.columbia.edu/~ronw/code/MEAPsoft/doc/html/FFT_8java-source.html

Short explanation: call fft() providing x as you amplitude data, y as all-zeros array, after the function returns your first answer will be a[0]=x[0]^2+y[0]^2.

Complete explanation: FFT is complex transform, it takes N complex numbers and produces N complex numbers. So x[0] is the real part of the first number, y[0] is the complex part. This function computes in-place, so when the function returns x and y will have the real and complex parts of the transform.

One typical usage is to calculate the power spectrum of audio. Your audio samples only have real part, you your complex part is 0. To calculate the power spectrum you add the square of the real and complex parts P[0]=x[0]^2+y[0]^2.

Also it's important to notice that the Fourier transform, when applied over real numbers, result in symmetrical result (x[0]==x[x.lenth-1]). The data at x[x.length/2] have the data from frequency f=0Hz. x[0]==x[x.length-1] has the data for a frequency equals to have the sampling rate (eg if you sampling was 44000Hz than it means f[0] refeers to 22kHz).

Full procedure:

  1. create array p[n] with 512 samples with zeros
  2. Collect 1024 audio samples, write them on x
  3. Set y[n]=0 for all n
  4. calculate fft(x,y)
  5. calculate p[n]+=x[n+512]^2+y[n+512]^2 for all n=0 to 512
  6. to go 2 to take another batch (after 50 batches go to next step)
  7. plot p
  8. go to 1

Than adjust the fixed number for your taste.

The number 512 defines the sampling window, I won't explain it. Just avoid reducing it too much.

The number 1024 must be always the double of the last number.

The number 50 defines you update rate. If your sampling rate is 44000 samples per second you update rate will be: R=44000/1024/50 = 0.85 seconds.

勿挽旧人 2025-01-13 02:20:44

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

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

kissfft is a decent enough library that compiles on android. It has a more versatile license than FFTW (even though FFTW is admittedly better).

You can find an android binding for kissfft in libgdx https://github.com/libgdx/libgdx/blob/0.9.9/extensions/gdx-audio/src/com/badlogic/gdx/audio/analysis/KissFFT.java

Or if you would like a pure Java based solution try jTransforms
https://sites.google.com/site/piotrwendykier/software/jtransforms

甜`诱少女 2025-01-13 02:20:44

使用这个( 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 答案,了解如果您的原始输入是幅度与时间的关系,如何获取频率。

Use this class (the one that EricLarch's answer is derived from).

Usage Notes

This function replaces your inputs arrays with the FFT output.

Input

  • N = the number of data points (the size of your input array, must be a power of 2)
  • X = the real part of your data to be transformed
  • Y = the imaginary part of the data to be transformed

i.e. if your input is
(1+8i, 2+3j, 7-i, -10-3i)

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

Output

  • X = the real part of the FFT output
  • Y = the imaginary part of the FFT output

To get your classic FFT graph, you will want to calculate the magnitude of the real and imaginary parts.

Something like:

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

Also see this StackOverflow answer for how to get frequencies if your original input was magnitude vs. time.

岁吢 2025-01-13 02:20:44

是的,有 JTransforms 维护在 github 此处 和可作为 Maven 插件此处使用。

使用:

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

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

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

Yes, there is the JTransforms that is maintained on github here and avaiable as a Maven plugin here.

Use with:

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

But with more recent, Gradle versions you need to use something like:

dependencies {
    ... 
    implementation 'com.github.wendykierp:JTransforms:3.1'
}
2025-01-13 02:20:44

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

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

,因此值得注意的是缩放因子...
我建议您在书或维基上查找所有这些内容以确保确定。

@J Wang
Your output magnitude seems better than the answer given on the thread you have linked however that is still magnitude squared ... the magnitude of a complex number

z = a + ib

is calculated as

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

the answer in the linked thread suggests that for pure real inputs the outputs
should be using a2 or a for the output because the values for

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

with b_(i) = a_(i+N/2) meaning the complex part in their table is in the second
half of the output table.

i.e the second half of the output table for an input table of reals is the conjugate of the real ...

so z = a-ia giving a magnitude

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

so it is worth noting the scaling factors ...
I would recommend looking all this up in a book or on wiki to be sure.

谈情不如逗狗 2025-01-13 02:20:44

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

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

Unfortunately the top answer only works for Array that its size is a power of 2, which is very limiting.

I used the Jtransforms library and it works perfectly, you can compare it to the function used by Matlab.

here is my code with comments referencing how matlab transforms any signal and gets the frequency amplitudes (https://la.mathworks.com/help/matlab/ref/fft.html)

first, add the following in the build.gradle (app)

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

and here it is the code for for transforming a simple sine wave, works like a charm

    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;
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文