我目前正在开发适用于Android的Java。我尝试实现FFT以实现一种频率观察器。
实际上我能够做到,但显示器根本不流畅。 我添加了一些跟踪以检查代码的每个部分的处理时间,事实是FFT需要大约300ms才能应用于我的复杂阵列,它拥有4096个元素。我需要它不到100毫秒,因为我的线程(显示频率)每100毫秒刷新一次。我减少了初始数组,以便FFT结果只拥有1028个元素,并且它可以工作,但结果已被弃用。
有人有想法吗?
我使用了可在互联网上找到的默认fft.java和Complex.java类。
有关信息,我的计算FFT的代码如下:
int bytesPerSample = 2;
Complex[] x = new Complex[bufferSize/2] ;
for (int index = 0 ; index < bufferReadResult - bytesPerSample + 1; index += bytesPerSample)
{
// 16BITS = 2BYTES
float asFloat = Float.intBitsToFloat(asInt);
double sample = 0;
for (int b = 0; b < bytesPerSample; b++) {
int v = buffer[index + b];
if (b < bytesPerSample - 1 || bytesPerSample == 1) {
v &= 0xFF;
}
sample += v << (b * 8);
}
double sample32 = 100 * (sample / 32768.0); // don't know the use of this compute...
x[index/bytesPerSample] = new Complex(sample32, 0);
}
Complex[] tx = new Complex[1024]; // size = 2048
///// reduction of the size of the signal in order to improve the fft traitment time
for (int i = 0; i < x.length/4; i++)
{
tx[i] = new Complex(x[i*4].re(), 0);
}
// Signal retrieval thanks to the FFT
fftRes = FFT.fft(tx);
答案 0 :(得分:2)
我不了解Java,但是你在输入数据和一系列复杂值之间进行转换似乎非常复杂。您正在构建两个复杂数据阵列,其中只需要一个。
它也闻起来像你的复杂的真实和想象的价值是双倍的。对于你需要的东西而言,这是最重要的,而且无论如何,ARM在双算术方面都很慢。是否存在基于单精度浮点数的复杂类?
第三,你通过用零填充复数的虚部来对真实数据执行复杂的fft。虽然结果是正确的,但是它的工作量是两倍(除非常规足够聪明地发现它,我怀疑)。如果可能的话,对您的数据执行真正的fft并节省一半的时间。
然后正如西蒙所说,存在避免垃圾收集和内存分配的整个问题。
看起来您的FFT没有预备步骤。这意味着例程FFT.fft()每次都在计算复指数。 FFT计算的最长部分是计算复指数,这是一种耻辱,因为对于任何给定的FFT长度,指数都是常数。它们根本不依赖于您的输入数据。在实时世界中,我们使用FFT例程,我们在程序开始时计算一次指数,然后实际的fft本身将该const数组作为其输入之一。不知道你的FFT类是否可以做类似的事情。
如果你最终去了像FFTW这样的东西,那么你将不得不习惯从Java中调用C代码。还要确保你得到的版本支持(我认为)NEON,ARM对SSE,AVX和Altivec的回答。值得仔细阅读他们的发行说明进行检查。另外,我强烈怀疑,如果要求它对单精度浮点数执行FFT而不是双精度,FFTW只能提供显着的加速。
Google运气!
- 编辑 -
我的意思当然是“祝你好运”。快速给我一个真正的键盘,这些触摸屏是不可靠的......
答案 1 :(得分:0)
首先,感谢您的所有答案。 我跟着他们做了两次测试:
第一个,我用float替换我的Complex类中使用的double。结果只是好一点,但还不够。
然后我重新编写了fft方法,以便不再使用Complex,而是使用二维float数组。对于此数组的每一行,第一列包含实部,第二列包含虚部。 我还更改了我的代码,以便只在onCreate方法上实例化一次float数组。
结果......最糟糕!!现在它需要超过500毫秒而不是300毫秒。 我现在不知道该怎么做。
你可以在下面找到最初的fft功能,然后是我重新改写的功能。 谢谢你的帮助。
// compute the FFT of x[], assuming its length is a power of 2
public static Complex[] fft(Complex[] x) {
int N = x.length;
// base case
if (N == 1) return new Complex[] { x[0] };
// radix 2 Cooley-Tukey FFT
if (N % 2 != 0) { throw new RuntimeException("N is not a power of 2 : " + N); }
// fft of even terms
Complex[] even = new Complex[N/2];
for (int k = 0; k < N/2; k++) {
even[k] = x[2*k];
}
Complex[] q = fft(even);
// fft of odd terms
Complex[] odd = even; // reuse the array
for (int k = 0; k < N/2; k++) {
odd[k] = x[2*k + 1];
}
Complex[] r = fft(odd);
// combine
Complex[] y = new Complex[N];
for (int k = 0; k < N/2; k++) {
double kth = -2 * k * Math.PI / N;
Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
y[k] = q[k].plus(wk.times(r[k]));
y[k + N/2] = q[k].minus(wk.times(r[k]));
}
return y;
}
public static float[][] fftf(float[][] x) {
/**
* x[][0] = real part
* x[][1] = imaginary part
*/
int N = x.length;
// base case
if (N == 1) return new float[][] { x[0] };
// radix 2 Cooley-Tukey FFT
if (N % 2 != 0) { throw new RuntimeException("N is not a power of 2 : " + N); }
// fft of even terms
float[][] even = new float[N/2][2];
for (int k = 0; k < N/2; k++) {
even[k] = x[2*k];
}
float[][] q = fftf(even);
// fft of odd terms
float[][] odd = even; // reuse the array
for (int k = 0; k < N/2; k++) {
odd[k] = x[2*k + 1];
}
float[][] r = fftf(odd);
// combine
float[][] y = new float[N][2];
double kth, wkcos, wksin ;
for (int k = 0; k < N/2; k++) {
kth = -2 * k * Math.PI / N;
//Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
wkcos = Math.cos(kth) ; // real part
wksin = Math.sin(kth) ; // imaginary part
// y[k] = q[k].plus(wk.times(r[k]));
y[k][0] = (float) (q[k][0] + wkcos * r[k][0] - wksin * r[k][1]);
y[k][1] = (float) (q[k][1] + wkcos * r[k][1] + wksin * r[k][0]);
// y[k + N/2] = q[k].minus(wk.times(r[k]));
y[k + N/2][0] = (float) (q[k][0] - (wkcos * r[k][0] - wksin * r[k][1]));
y[k + N/2][1] = (float) (q[k][1] - (wkcos * r[k][1] + wksin * r[k][0]));
}
return y;
}
答案 2 :(得分:0)
谢谢!