fast_fourier_transform

Performs an in-place complex FFT.

package;

/**
 * Performs an in-place complex FFT.
 *
 * Released under the MIT License
 *
 * Copyright (c) 2010 Gerald T. Beauregard
 *
 * @haxe Rigondo
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

class FFT
{
    public static inline var FORWARD = false;
    public static inline var INVERSE = true;

    var m_logN:Int;            // log2 of FFT size
    var m_N:Int;                // FFT size
    var m_invN:Float;                // Inverse of FFT length

    var m_X:FFTArray<FFTElement>;    // Vector of linked list elements

    /**
     *
     */
    public function new()
    {
    }

    /**
     * Initialize class to perform FFT of specified size.
     *
     * @param    logN    Log2 of FFT length. e.g. for 512 pt FFT, logN = 9.
     */
    public inline function init(
        logN:Int )
    {
        m_logN = logN;
        m_N = 1 << m_logN;
        m_invN = 1.0/m_N;

        // Allocate elements for linked list of complex numbers.
        m_X = newArray(m_N);
        for (k in 0...m_N)
            m_X[k] = new FFTElement();

        // Set up "next" pointers.
        for (k in 0...m_N-1)
            m_X[k].next = m_X[k+1];

        // Specify target for bit reversal re-ordering.
        for (k in 0...m_N)
            m_X[k].revTgt = BitReverse(k,logN);
    }

    /**
     * Performs in-place complex FFT.
     *
     * @param    xRe        Real part of input/output
     * @param    xIm        Imaginary part of input/output
     * @param    inverse    If true (INVERSE), do an inverse FFT
     */
    public inline function run(
        xRe:FFTArray<Float>,
        xIm:FFTArray<Float>,
        inverse:Bool = false )
    {
        var numFlies:Int = m_N >> 1;    // Float of butterflies per sub-FFT
        var span:Int = m_N >> 1;        // Width of the butterfly
        var spacing:Int = m_N;            // Distance between start of sub-FFTs
        var wIndexStep:Int = 1;         // Increment for twiddle table index

        // Copy data into linked complex number objects
        // If it's an IFFT, we divide by N while we're at it
        var x = m_X[0];
        var k:Int = 0;
        var scale = inverse ? m_invN : 1.0;
        while (x!=null)
        {
            x.re = scale*xRe[k];
            x.im = scale*xIm[k];
            x = x.next;
            k++;
        }
        var math = Math;
        // For each stage of the FFT
        for (stage in 0...m_logN)
        {
            // Compute a multiplier factor for the "twiddle factors".
            // The twiddle factors are complex unit vectors spaced at
            // regular angular intervals. The angle by which the twiddle
            // factor advances depends on the FFT stage. In many FFT
            // implementations the twiddle factors are cached, but because
            // vector lookup is relatively slow in ActionScript, it's just
            // as fast to compute them on the fly.
            var wAngleInc = wIndexStep * 2.0*math.PI/m_N;
            if ( !inverse )
                wAngleInc *= -1;
            var wMulRe = math.cos(wAngleInc);
            var wMulIm = math.sin(wAngleInc);

            var start = 0;
            while(start < m_N){
                var xTop = m_X[start];
                var xBot = m_X[start+span];

                var wRe = 1.0;
                var wIm = 0.0;

                // For each butterfly in this stage
                for (flyCount in 0...numFlies)
                {
                    // Get the top & bottom values
                    var xTopRe = xTop.re;
                    var xTopIm = xTop.im;
                    var xBotRe = xBot.re;
                    var xBotIm = xBot.im;

                    // Top branch of butterfly has addition
                    xTop.re = xTopRe + xBotRe;
                    xTop.im = xTopIm + xBotIm;

                    // Bottom branch of butterly has subtraction,
                    // followed by multiplication by twiddle factor
                    xBotRe = xTopRe - xBotRe;
                    xBotIm = xTopIm - xBotIm;
                    xBot.re = xBotRe*wRe - xBotIm*wIm;
                    xBot.im = xBotRe*wIm + xBotIm*wRe;

                    // Advance butterfly to next top & bottom positions
                    xTop = xTop.next;
                    xBot = xBot.next;

                    // Update the twiddle factor, via complex multiply
                    // by unit vector with the appropriate angle
                    // (wRe + j wIm) = (wRe + j wIm) x (wMulRe + j wMulIm)
                    var tRe = wRe;
                    wRe = wRe*wMulRe - wIm*wMulIm;
                    wIm = tRe*wMulIm + wIm*wMulRe;
                }
                start += spacing;
            }

            numFlies >>= 1;     // Divide by 2 by right shift
            span >>= 1;
            spacing >>= 1;
            wIndexStep <<= 1;      // Multiply by 2 by left shift
        }

        // The algorithm leaves the result in a scrambled order.
        // Unscramble while copying values from the complex
        // linked list elements back to the input/output vectors.
        x = m_X[0];
        while (x!=null)
        {
            var target = x.revTgt;
            xRe[target] = x.re;
            xIm[target] = x.im;
            x = x.next;
        }
    }

    /**
     * Do bit reversal of specified number of places of an int
     * For example, 1101 bit-reversed is 1011
     *
     * @param    x        Float to be bit-reverse.
     * @param    numBits    Float of bits in the number.
     */
    inline function BitReverse(
        x,
        numBits)
    {
        var y:Int = 0;
        for (i in 0...numBits)
        {
            y <<= 1;
            y |= x & 0x0001;
            x >>= 1;
        }
        return y;
    }

    public static inline function newArray<T>(len=0, fixed=false) {
        return new
            #if flash
                flash.Vector<T>(len, fixed)
            #else
                Array<T>()
            #end
        ;
    }
}

class FFTElementEssentials{
    public function new() {
        re = 0;
        im = 0;
    }
    public var re:Float;            // Real component
    public var im:Float;            // Imaginary component
    public var revTgt:Int;                // Target position post bit-reversal
}

class FFTElement extends FFTElementEssentials{
    //public function new() {        super();    }
    public var next:FFTElement;    // Next element in linked list
}

read more here

version #11645, modified 2011-10-11 05:34:30 by shalmoo