demos/spectrum/3rdparty/fftreal/fftreal.pas
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 11 Jun 2010 14:24:45 +0300
changeset 25 e24348a560a6
permissions -rw-r--r--
Revision: 201021 Kit: 2010123

(*****************************************************************************

        DIGITAL SIGNAL PROCESSING TOOLS
        Version 1.03, 2001/06/15
        (c) 1999 - Laurent de Soras

        FFTReal.h
        Fourier transformation of real number arrays.
        Portable ISO C++

------------------------------------------------------------------------------

	LEGAL

	Source code may be freely used for any purpose, including commercial
	applications. Programs must display in their "About" dialog-box (or
	documentation) a text telling they use these routines by Laurent de Soras.
	Modified source code can be distributed, but modifications must be clearly
	indicated.

	CONTACT

	Laurent de Soras
	92 avenue Albert 1er
	92500 Rueil-Malmaison
	France

	ldesoras@club-internet.fr

------------------------------------------------------------------------------

        Translation to ObjectPascal by :
          Frederic Vanmol
          frederic@axiworld.be

*****************************************************************************)


unit
    FFTReal;

interface

uses
    Windows;

(* Change this typedef to use a different floating point type in your FFTs
	(i.e. float, double or long double). *)
type
    pflt_t = ^flt_t;
    flt_t = single;

    pflt_array = ^flt_array;
    flt_array = array[0..0] of flt_t;

    plongarray = ^longarray;
    longarray = array[0..0] of longint;

const
     sizeof_flt : longint = SizeOf(flt_t);



type
    // Bit reversed look-up table nested class
    TBitReversedLUT = class
    private
      _ptr : plongint;
    public
      constructor Create(const nbr_bits: integer);
      destructor Destroy; override;
      function get_ptr: plongint;
    end;

    // Trigonometric look-up table nested class
    TTrigoLUT = class
    private
      _ptr : pflt_t;
    public
      constructor Create(const nbr_bits: integer);
      destructor Destroy; override;
      function get_ptr(const level: integer): pflt_t;
    end;

    TFFTReal = class
    private
      _bit_rev_lut  : TBitReversedLUT;
      _trigo_lut    : TTrigoLUT;
      _sqrt2_2      : flt_t;
      _length       : longint;
      _nbr_bits     : integer;
      _buffer_ptr   : pflt_t;
    public
      constructor Create(const length: longint);
      destructor Destroy; override;

      procedure do_fft(f: pflt_array; const x: pflt_array);
      procedure do_ifft(const f: pflt_array; x: pflt_array);
      procedure rescale(x: pflt_array);
    end;







implementation

uses
    Math;

{ TBitReversedLUT }

constructor TBitReversedLUT.Create(const nbr_bits: integer);
var
   length   : longint;
   cnt      : longint;
   br_index : longint;
   bit      : longint;
begin
  inherited Create;

  length := 1 shl nbr_bits;
  GetMem(_ptr, length*SizeOf(longint));

  br_index := 0;
  plongarray(_ptr)^[0] := 0;
  for cnt := 1 to length-1 do
  begin
    // ++br_index (bit reversed)
    bit := length shr 1;
    br_index := br_index xor bit;
    while br_index and bit = 0 do
    begin
      bit := bit shr 1;
      br_index := br_index xor bit;
    end;

    plongarray(_ptr)^[cnt] := br_index;
  end;
end;

destructor TBitReversedLUT.Destroy;
begin
  FreeMem(_ptr);
  _ptr := nil;
  inherited;
end;

function TBitReversedLUT.get_ptr: plongint;
begin
  Result := _ptr;
end;

{ TTrigLUT }

constructor TTrigoLUT.Create(const nbr_bits: integer);
var
   total_len : longint;
   PI        : double;
   level     : integer;
   level_len : longint;
   level_ptr : pflt_array;
   mul       : double;
   i         : longint;
begin
  inherited Create;

  _ptr := nil;

  if (nbr_bits > 3) then
  begin
    total_len := (1 shl (nbr_bits - 1)) - 4;
    GetMem(_ptr, total_len * sizeof_flt);

    PI := ArcTan(1) * 4;
    for level := 3 to nbr_bits-1 do
    begin
      level_len := 1 shl (level - 1);
      level_ptr := pointer(get_ptr(level));
      mul := PI / (level_len shl 1);

      for i := 0 to level_len-1 do
        level_ptr^[i] := cos(i * mul);
    end;
  end;
end;

destructor TTrigoLUT.Destroy;
begin
  FreeMem(_ptr);
  _ptr := nil;
  inherited;
end;

function TTrigoLUT.get_ptr(const level: integer): pflt_t;
var
   tempp : pflt_t;
begin
  tempp := _ptr;
  inc(tempp, (1 shl (level-1)) - 4);
  Result := tempp;
end;

{ TFFTReal }

constructor TFFTReal.Create(const length: longint);
begin
  inherited Create;

  _length := length;
  _nbr_bits := Floor(Ln(length) / Ln(2) + 0.5);
  _bit_rev_lut := TBitReversedLUT.Create(Floor(Ln(length) / Ln(2) + 0.5));
  _trigo_lut := TTrigoLUT.Create(Floor(Ln(length) / Ln(2) + 0.05));
  _sqrt2_2 := Sqrt(2) * 0.5;

  _buffer_ptr := nil;
  if _nbr_bits > 2 then
    GetMem(_buffer_ptr, _length * sizeof_flt);
end;

destructor TFFTReal.Destroy;
begin
  if _buffer_ptr <> nil then
  begin
    FreeMem(_buffer_ptr);
    _buffer_ptr := nil;
  end;

  _bit_rev_lut.Free;
  _bit_rev_lut := nil;
  _trigo_lut.Free;
  _trigo_lut := nil;

  inherited;       
end;

(*==========================================================================*/
/*      Name: do_fft                                                        */
/*      Description: Compute the FFT of the array.                          */
/*      Input parameters:                                                   */
/*        - x: pointer on the source array (time).                          */
/*      Output parameters:                                                  */
/*        - f: pointer on the destination array (frequencies).              */
/*             f [0...length(x)/2] = real values,                           */
/*             f [length(x)/2+1...length(x)-1] = imaginary values of        */
/*               coefficents 1...length(x)/2-1.                             */
/*==========================================================================*)
procedure TFFTReal.do_fft(f: pflt_array; const x: pflt_array);
var
   sf, df     : pflt_array;
   pass       : integer;
   nbr_coef   : longint;
   h_nbr_coef : longint;
   d_nbr_coef : longint;
   coef_index : longint;
   bit_rev_lut_ptr : plongarray;
   rev_index_0 : longint;
   rev_index_1 : longint;
   rev_index_2 : longint;
   rev_index_3 : longint;
   df2         : pflt_array;
   n1, n2, n3  : integer;
   sf_0, sf_2  : flt_t;
   sqrt2_2     : flt_t;
   v           : flt_t;
   cos_ptr     : pflt_array;
   i           : longint;
   sf1r, sf2r  : pflt_array;
   dfr, dfi    : pflt_array;
   sf1i, sf2i  : pflt_array;
   c, s        : flt_t;
   temp_ptr    : pflt_array;
   b_0, b_2    : flt_t;
begin
  n1 := 1;
  n2 := 2;
  n3 := 3;

  (*______________________________________________
   *
   * General case
   *______________________________________________
   *)

  if _nbr_bits > 2 then
  begin
    if _nbr_bits and 1 <> 0 then
    begin
      df := pointer(_buffer_ptr);
      sf := f;
    end
    else
    begin
      df := f;
      sf := pointer(_buffer_ptr);
    end;

    //
    // Do the transformation in several passes
    //

    // First and second pass at once
    bit_rev_lut_ptr := pointer(_bit_rev_lut.get_ptr);
    coef_index := 0;

    repeat
      rev_index_0 := bit_rev_lut_ptr^[coef_index];
      rev_index_1 := bit_rev_lut_ptr^[coef_index + 1];
      rev_index_2 := bit_rev_lut_ptr^[coef_index + 2];
      rev_index_3 := bit_rev_lut_ptr^[coef_index + 3];

      df2 := pointer(longint(df) + (coef_index*sizeof_flt));
      df2^[n1] := x^[rev_index_0] - x^[rev_index_1];
      df2^[n3] := x^[rev_index_2] - x^[rev_index_3];

      sf_0 := x^[rev_index_0] + x^[rev_index_1];
      sf_2 := x^[rev_index_2] + x^[rev_index_3];

      df2^[0] := sf_0 + sf_2;
      df2^[n2] := sf_0 - sf_2;

      inc(coef_index, 4);
    until (coef_index >= _length);


    // Third pass
    coef_index := 0;
    sqrt2_2 := _sqrt2_2;

    repeat
      sf^[coef_index] := df^[coef_index] + df^[coef_index + 4];
      sf^[coef_index + 4] := df^[coef_index] - df^[coef_index + 4];
      sf^[coef_index + 2] := df^[coef_index + 2];
      sf^[coef_index + 6] := df^[coef_index + 6];

      v := (df [coef_index + 5] - df^[coef_index + 7]) * sqrt2_2;
      sf^[coef_index + 1] := df^[coef_index + 1] + v;
      sf^[coef_index + 3] := df^[coef_index + 1] - v;

      v := (df^[coef_index + 5] + df^[coef_index + 7]) * sqrt2_2;
      sf [coef_index + 5] := v + df^[coef_index + 3];
      sf [coef_index + 7] := v - df^[coef_index + 3];

      inc(coef_index, 8);
    until (coef_index >= _length);


    // Next pass
    for pass := 3 to _nbr_bits-1 do
    begin
      coef_index := 0;
      nbr_coef := 1 shl pass;
      h_nbr_coef := nbr_coef shr 1;
      d_nbr_coef := nbr_coef shl 1;

      cos_ptr := pointer(_trigo_lut.get_ptr(pass));
      repeat
        sf1r := pointer(longint(sf) + (coef_index * sizeof_flt));
        sf2r := pointer(longint(sf1r) + (nbr_coef * sizeof_flt));
        dfr := pointer(longint(df) + (coef_index * sizeof_flt));
        dfi := pointer(longint(dfr) + (nbr_coef * sizeof_flt));

        // Extreme coefficients are always real
        dfr^[0] := sf1r^[0] + sf2r^[0];
        dfi^[0] := sf1r^[0] - sf2r^[0];   // dfr [nbr_coef] =
        dfr^[h_nbr_coef] := sf1r^[h_nbr_coef];
        dfi^[h_nbr_coef] := sf2r^[h_nbr_coef];

        // Others are conjugate complex numbers
        sf1i := pointer(longint(sf1r) + (h_nbr_coef * sizeof_flt));
        sf2i := pointer(longint(sf1i) + (nbr_coef * sizeof_flt));

        for i := 1 to h_nbr_coef-1 do
        begin
          c := cos_ptr^[i];               // cos (i*PI/nbr_coef);
          s := cos_ptr^[h_nbr_coef - i];  // sin (i*PI/nbr_coef);

          v := sf2r^[i] * c - sf2i^[i] * s;
          dfr^[i] := sf1r^[i] + v;
          dfi^[-i] := sf1r^[i] - v;	// dfr [nbr_coef - i] =

          v := sf2r^[i] * s + sf2i^[i] * c;
          dfi^[i] := v + sf1i^[i];
          dfi^[nbr_coef - i] := v - sf1i^[i];
        end;

        inc(coef_index, d_nbr_coef);
      until (coef_index >= _length);

      // Prepare to the next pass
      temp_ptr := df;
      df := sf;
      sf := temp_ptr;
    end;
  end

  (*______________________________________________
   *
   * Special cases
   *______________________________________________
   *)

  // 4-point FFT
  else if _nbr_bits = 2 then
  begin
    f^[n1] := x^[0] - x^[n2];
    f^[n3] := x^[n1] - x^[n3];

    b_0 := x^[0] + x^[n2];
    b_2 := x^[n1] + x^[n3];

    f^[0] := b_0 + b_2;
    f^[n2] := b_0 - b_2;
  end

  // 2-point FFT
  else if _nbr_bits = 1 then
  begin
    f^[0] := x^[0] + x^[n1];
    f^[n1] := x^[0] - x^[n1];
  end

  // 1-point FFT
  else
    f^[0] := x^[0];
end;


(*==========================================================================*/
/*      Name: do_ifft                                                       */
/*      Description: Compute the inverse FFT of the array. Notice that      */
/*                   IFFT (FFT (x)) = x * length (x). Data must be          */
/*                   post-scaled.                                           */
/*      Input parameters:                                                   */
/*        - f: pointer on the source array (frequencies).                   */
/*             f [0...length(x)/2] = real values,                           */
/*             f [length(x)/2+1...length(x)-1] = imaginary values of        */
/*               coefficents 1...length(x)/2-1.                             */
/*      Output parameters:                                                  */
/*        - x: pointer on the destination array (time).                     */
/*==========================================================================*)
procedure TFFTReal.do_ifft(const f: pflt_array; x: pflt_array);
var
   n1, n2, n3      : integer;
   n4, n5, n6, n7  : integer;
   sf, df, df_temp : pflt_array;
   pass            : integer;
   nbr_coef        : longint;
   h_nbr_coef      : longint;
   d_nbr_coef      : longint;
   coef_index      : longint;
   cos_ptr         : pflt_array;
   i               : longint;
   sfr, sfi        : pflt_array;
   df1r, df2r      : pflt_array;
   df1i, df2i      : pflt_array;
   c, s, vr, vi    : flt_t;
   temp_ptr        : pflt_array;
   sqrt2_2         : flt_t;
   bit_rev_lut_ptr : plongarray;
   sf2             : pflt_array;
   b_0, b_1, b_2, b_3 : flt_t;
begin
  n1 := 1;
  n2 := 2;
  n3 := 3;
  n4 := 4;
  n5 := 5;
  n6 := 6;
  n7 := 7;

  (*______________________________________________
   *
   * General case
   *______________________________________________
   *)

  if _nbr_bits > 2 then
  begin
    sf := f;

    if _nbr_bits and 1 <> 0 then
    begin
      df := pointer(_buffer_ptr);
      df_temp := x;
    end
    else
    begin
      df := x;
      df_temp := pointer(_buffer_ptr);
    end;

    // Do the transformation in several pass

    // First pass
    for pass := _nbr_bits-1 downto 3 do
    begin
      coef_index := 0;
      nbr_coef := 1 shl pass;
      h_nbr_coef := nbr_coef shr 1;
      d_nbr_coef := nbr_coef shl 1;

      cos_ptr := pointer(_trigo_lut.get_ptr(pass));

      repeat
        sfr := pointer(longint(sf) + (coef_index*sizeof_flt));
        sfi := pointer(longint(sfr) + (nbr_coef*sizeof_flt));
        df1r := pointer(longint(df) + (coef_index*sizeof_flt));
        df2r := pointer(longint(df1r) + (nbr_coef*sizeof_flt));

        // Extreme coefficients are always real
        df1r^[0] := sfr^[0] + sfi^[0];		// + sfr [nbr_coef]
        df2r^[0] := sfr^[0] - sfi^[0];		// - sfr [nbr_coef]
        df1r^[h_nbr_coef] := sfr^[h_nbr_coef] * 2;
        df2r^[h_nbr_coef] := sfi^[h_nbr_coef] * 2;

        // Others are conjugate complex numbers
        df1i := pointer(longint(df1r) + (h_nbr_coef*sizeof_flt));
        df2i := pointer(longint(df1i) + (nbr_coef*sizeof_flt));

        for i := 1 to h_nbr_coef-1 do
        begin
          df1r^[i] := sfr^[i] + sfi^[-i];		// + sfr [nbr_coef - i]
          df1i^[i] := sfi^[i] - sfi^[nbr_coef - i];

          c := cos_ptr^[i];					// cos (i*PI/nbr_coef);
          s := cos_ptr^[h_nbr_coef - i];	// sin (i*PI/nbr_coef);
          vr := sfr^[i] - sfi^[-i];		// - sfr [nbr_coef - i]
          vi := sfi^[i] + sfi^[nbr_coef - i];

          df2r^[i] := vr * c + vi * s;
          df2i^[i] := vi * c - vr * s;
        end;

        inc(coef_index, d_nbr_coef);
      until (coef_index >= _length);


      // Prepare to the next pass 
      if (pass < _nbr_bits - 1) then
      begin
        temp_ptr := df;
        df := sf;
        sf := temp_ptr;
      end
      else
      begin
        sf := df;
        df := df_temp;
      end
    end;

    // Antepenultimate pass
    sqrt2_2 := _sqrt2_2;
    coef_index := 0;

    repeat
      df^[coef_index] := sf^[coef_index] + sf^[coef_index + 4];
      df^[coef_index + 4] := sf^[coef_index] - sf^[coef_index + 4];
      df^[coef_index + 2] := sf^[coef_index + 2] * 2;
      df^[coef_index + 6] := sf^[coef_index + 6] * 2;

      df^[coef_index + 1] := sf^[coef_index + 1] + sf^[coef_index + 3];
      df^[coef_index + 3] := sf^[coef_index + 5] - sf^[coef_index + 7];

      vr := sf^[coef_index + 1] - sf^[coef_index + 3];
      vi := sf^[coef_index + 5] + sf^[coef_index + 7];

      df^[coef_index + 5] := (vr + vi) * sqrt2_2;
      df^[coef_index + 7] := (vi - vr) * sqrt2_2;

      inc(coef_index, 8);
    until (coef_index >= _length);


    // Penultimate and last pass at once 
    coef_index := 0;
    bit_rev_lut_ptr := pointer(_bit_rev_lut.get_ptr);
    sf2 := df;

    repeat
      b_0 := sf2^[0] + sf2^[n2];
      b_2 := sf2^[0] - sf2^[n2];
      b_1 := sf2^[n1] * 2;
      b_3 := sf2^[n3] * 2;

      x^[bit_rev_lut_ptr^[0]] := b_0 + b_1;
      x^[bit_rev_lut_ptr^[n1]] := b_0 - b_1;
      x^[bit_rev_lut_ptr^[n2]] := b_2 + b_3;
      x^[bit_rev_lut_ptr^[n3]] := b_2 - b_3;

      b_0 := sf2^[n4] + sf2^[n6];
      b_2 := sf2^[n4] - sf2^[n6];
      b_1 := sf2^[n5] * 2;
      b_3 := sf2^[n7] * 2;

      x^[bit_rev_lut_ptr^[n4]] := b_0 + b_1;
      x^[bit_rev_lut_ptr^[n5]] := b_0 - b_1;
      x^[bit_rev_lut_ptr^[n6]] := b_2 + b_3;
      x^[bit_rev_lut_ptr^[n7]] := b_2 - b_3;

      inc(sf2, 8);
      inc(coef_index, 8);
      inc(bit_rev_lut_ptr, 8);
    until (coef_index >= _length);
  end

  (*______________________________________________
   *
   * Special cases
   *______________________________________________
   *)

  // 4-point IFFT
  else if _nbr_bits = 2 then
  begin
    b_0 := f^[0] + f [n2];
    b_2 := f^[0] - f [n2];

    x^[0] := b_0 + f [n1] * 2;
    x^[n2] := b_0 - f [n1] * 2;
    x^[n1] := b_2 + f [n3] * 2;
    x^[n3] := b_2 - f [n3] * 2;
  end

  // 2-point IFFT
  else if _nbr_bits = 1 then
  begin
    x^[0] := f^[0] + f^[n1];
    x^[n1] := f^[0] - f^[n1];
  end

  // 1-point IFFT
  else
    x^[0] := f^[0];
end;

(*==========================================================================*/
/*      Name: rescale                                                       */
/*      Description: Scale an array by divide each element by its length.   */
/*                   This function should be called after FFT + IFFT.       */
/*      Input/Output parameters:                                            */
/*        - x: pointer on array to rescale (time or frequency).             */
/*==========================================================================*)
procedure TFFTReal.rescale(x: pflt_array);
var
   mul  : flt_t;
   i    : longint;
begin
  mul := 1.0 / _length;
  i := _length - 1;

  repeat
    x^[i] := x^[i] * mul;
    dec(i);
  until (i < 0);
end;

end.