【问题标题】:JTransforms FFT in Android from PCM dataJ从PCM数据转换Android中的FFT
【发布时间】:2011-11-30 17:32:03
【问题描述】:

我玩这个已经有一段时间了,我不知道我应该在这里做什么。

我正在将 PCM 音频数据读入 audioData 数组:

 recorder.read(audioData,0,bufferSize);     //read the PCM audio data into the audioData array

我想使用 Piotr Wendykier 的 JTransform 库对我的 PCM 数据执行 FFT 以获得频率。

import edu.emory.mathcs.jtransforms.fft.DoubleFFT_1D;

目前我有这个:

       DoubleFFT_1D fft = new DoubleFFT_1D(1024); // 1024 is size of array

for (int i = 0; i < 1023; i++) {
           a[i]= audioData[i];               
           if (audioData[i] != 0)
           Log.v(TAG, "audiodata=" + audioData[i] + " fft= " + a[i]);
       }
       fft.complexForward(a);

我无法理解如何工作,有人可以给我一些指示吗?之后我需要进行任何计算吗?

我确定我已经走了,任何事情都将不胜感激!

【问题讨论】:

  • 对不起,我应该说 complexForward 做了什么:计算复杂数据的一维正向 DFT,将结果留在 a。复数按顺序存储为两个双精度值:实部和虚部,即输入数组的大小必须大于或等于 2*n。输入数据的物理布局必须如下:a[2*k] = Re[k], a[2*k+1] = Im[k], 0
  • 举个例子……

标签: android fft pcm


【解决方案1】:

由于我花了几个小时来让它工作,这里有一个完整的 Java 实现:

import org.jtransforms.fft.DoubleFFT_1D;

public class FrequencyScanner {
    private double[] window;

    public FrequencyScanner() {
        window = null;
    }

    /** extract the dominant frequency from 16bit PCM data.
     * @param sampleData an array containing the raw 16bit PCM data.
     * @param sampleRate the sample rate (in HZ) of sampleData
     * @return an approximation of the dominant frequency in sampleData
     */
    public double extractFrequency(short[] sampleData, int sampleRate) {
        /* sampleData + zero padding */
        DoubleFFT_1D fft = new DoubleFFT_1D(sampleData.length + 24 * sampleData.length);
        double[] a = new double[(sampleData.length + 24 * sampleData.length) * 2];

        System.arraycopy(applyWindow(sampleData), 0, a, 0, sampleData.length);
        fft.realForward(a);

        /* find the peak magnitude and it's index */
        double maxMag = Double.NEGATIVE_INFINITY;
        int maxInd = -1;

        for(int i = 0; i < a.length / 2; ++i) {
            double re  = a[2*i];
            double im  = a[2*i+1];
            double mag = Math.sqrt(re * re + im * im);

            if(mag > maxMag) {
                maxMag = mag;
                maxInd = i;
            }
        }

        /* calculate the frequency */
        return (double)sampleRate * maxInd / (a.length / 2);
    }

    /** build a Hamming window filter for samples of a given size
     * See http://www.labbookpages.co.uk/audio/firWindowing.html#windows
     * @param size the sample size for which the filter will be created
     */
    private void buildHammWindow(int size) {
        if(window != null && window.length == size) {
            return;
        }
        window = new double[size];
        for(int i = 0; i < size; ++i) {
            window[i] = .54 - .46 * Math.cos(2 * Math.PI * i / (size - 1.0));
        }
    }

    /** apply a Hamming window filter to raw input data
     * @param input an array containing unfiltered input data
     * @return a double array containing the filtered data
     */
    private double[] applyWindow(short[] input) {
        double[] res = new double[input.length];

        buildHammWindow(input.length);
        for(int i = 0; i < input.length; ++i) {
            res[i] = (double)input[i] * window[i];
        }
        return res;
    }
}

FrequencyScanner 将返回所呈现样本数据中主频率的近似值。 它将Hamming window 应用于其输入,以允许从音频流中传入任意样本。 精度是通过在进行 FFT 变换之前对样本数据进行内部零填充来实现的。 (我知道有更好且更复杂的方法可以做到这一点,但填充方法足以满足我的个人需求)。

我用 220hz 和 440hz 的参考声音创建的原始 16 位 PCM 样本对其进行测试,结果匹配。

【讨论】:

  • 我认为你应该调用 realforwardfull 来计算 fft 结果。 Realforward 对结果有不同的解释。如果你检查 api 的文档。
【解决方案2】:

是的,您需要使用 realForward 函数而不是 complexForward,因为您向其传递的是实数数组而不是复数数组 from doc

编辑:

或者你可以得到实部并像这样执行复杂到复杂的fft:

double[] in = new double[N];
read ...
double[] fft = new double[N * 2];

for(int i = 0; i < ffsize; ++i)
{
  fft[2*i] = mic[i];
  fft[2*i+1] = 0.0;
}
fft1d.complexForward(fft);

我尝试将结果与 matlab 进行比较,但我没有得到相同的结果...(幅度)

【讨论】:

    【解决方案3】:

    如果您正在寻找音频输入(1D,真实数据)的 FFT,您不应该使用 1D REAL Fft 吗?

    【讨论】:

    • 这是我要试验的东西
    【解决方案4】:

    如果您只是在输入波形中寻找单个正弦音的频率,那么您需要找到幅度最大的 FFT 峰值,其中:

    Magnitude = sqrt(re*re + im*im)
    

    这个最大幅度峰值的索引i 将告诉您正弦曲线的大致频率:

    Frequency = Fs * i / N
    

    地点:

    Fs = sample rate (Hz)
    i = index of peak
    N = number of points in FFT (1024 in this case)
    

    【讨论】:

    • 这听起来很像我想要做的。但是我不确定如何从变换中获得实值和虚值。另外我不确定我是否应该使用 DoubleFFT_1D 类?与 DoubleFFT_2D 类或 FloatFFT_1D 类等相反
    • 您只需要一个 1D FFT 来处理音频(2D FFT 通常用于 2D 数据,例如图像等)。根据您在上面的问题中所说的,听起来实部和图像分量是交错的,因此 bin i 在索引​​ 2*i 处具有实部分量,在索引 2*i + 1 处具有图像分量。
    • 谢谢保罗,太好了,有几件事我不明白...我是否像 fft.complexForward(audioData); 那样传递 audioData 数组?如果我这样做了,它会操纵数组吗?它没有返回类型。谢谢
    • 我不熟悉这个特定的 FFT,但它听起来像是所谓的“就地”实现,所以你传入时域数据,它会被频域结果覆盖.对于输入,您将实部 (2*i) 设置为样本数据值,将虚部 (2*i+1) 设置为 0。
    • 那么在将音频数据数组中的每个索引传递给 FFT 之前,我应该将其加倍吗?即(2*i)?
    猜你喜欢
    • 2015-11-08
    • 1970-01-01
    • 2013-08-24
    • 2016-03-20
    • 1970-01-01
    • 2013-01-28
    • 2012-03-27
    • 2012-05-02
    相关资源
    最近更新 更多