JTransforms FFT in Android from PCM data

前端 未结 4 2149
孤街浪徒
孤街浪徒 2021-01-01 03:38

I\'ve been playing with this now for sometime, I cant work out what I am meant to be doing here.

I am reading in PCM audio data into an audioData array:



        
4条回答
  •  猫巷女王i
    2021-01-01 03:54

    Since I've spent some hours on getting this to work here's a complete implementation in 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 will return an approximation of the dominant frequency in the presented sample data. It applies a Hamming window to it's input to allow passing in arbitrary samples from an audio stream. Precision is achieved by internally zero padding the sample data before doing the FFT transform. (I know there are better - and far more complex - ways to do this but the padding approach is sufficient for my personal needs).

    I testet it against raw 16bit PCM samples created from reference sounds for 220hz and 440hz and the results match.

提交回复
热议问题