/** * @file llfft.cpp * @brief FFT function implementations * * Copyright (c) 2003-2007, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlife.com/developers/opensource/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlife.com/developers/opensource/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. */ /* * Fast Fourier Transform * */ #include "llviewerprecompiledheaders.h" #include "llfft.h" #include "llerror.h" /* ** ********************************************************************* ** Forward and inverse discrete Fourier transforms on complex data ** ********************************************************************* ** ** ** forward_fft(array, rows, cols) ** COMPLEX *array; ** S32 rows, cols; ** ** inverse_fft(array, rows, cols) ** COMPLEX *array; ** S32 rows, cols; ** ** These entry points compute the forward and inverse DFT's, respectively, ** of a single-precision COMPLEX array. ** ** The result is a COMPLEX array of the same size, returned in ** the same space as the input array. That is, the original array is ** overwritten and destroyed. ** ** Rows and columns must each be an integral power of 2. ** ** These routines return integer value -1 if an error was detected, ** 0 otherwise ** ** This implementation of the DFT uses the transform pair defined as follows. ** ** Let there be two COMPLEX arrays each with n rows and m columns ** Index them as ** f(x,y): 0 <= x <= m - 1, 0 <= y <= n - 1 ** F(u,v): -m/2 <= u <= m/2 - 1, -n/2 <= v <= n/2 - 1 ** ** Then the forward and inverse transforms are related as ** ** Forward: ** ** F(u,v) = \sum_{x=0}^{m-1} \sum_{y=0}^{n-1} ** f(x,y) \exp{-2\pi i (ux/m + vy/n)} ** ** ** Inverse: ** ** f(x,y) = 1/(mn) \sum_{u=-m/2}^{m/2-1} \sum_{v=-n/2}^{n/2-1} ** F(u,v) \exp{2\pi i (ux/m + vy/n)} ** ** Therefore, the transforms have these properties: ** 1. \sum_x \sum_y f(x,y) = F(0,0) ** 2. m n \sum_x \sum_y |f(x,y)|^2 = \sum_u \sum_v |F(u,v)|^2 ** */ //DPCOMPLEX *stageBuff; /* buffer to hold a row or column at a time */ //COMPLEX *bigBuff; /* a pointer to the input array */ /* * These macros move complex data between bigBuff and * stageBuff */ inline void LoadRow(DPCOMPLEX* stageBuff, COMPLEX* bigBuff, U32 row, U32 cols) { for (U32 j = row*cols, k = 0 ; k < cols ; j++, k++) { stageBuff[k].re = bigBuff[j].re; stageBuff[k].im = bigBuff[j].im; } } inline void StoreRow(DPCOMPLEX* stageBuff, COMPLEX* bigBuff, U32 row, U32 cols) { for (U32 j = row*cols, k = 0 ; k < cols ; j++, k++) { bigBuff[j].re = (F32)stageBuff[k].re; bigBuff[j].im = (F32)stageBuff[k].im; } } inline void LoadCol(DPCOMPLEX* stageBuff, COMPLEX* bigBuff, U32 col, U32 rows, U32 cols) { for (U32 j = col,k = 0 ; k < rows ; j+=cols, k++) { stageBuff[k].re = bigBuff[j].re; stageBuff[k].im = bigBuff[j].im; } } inline void StoreCol(DPCOMPLEX* stageBuff, COMPLEX* bigBuff, U32 col, U32 rows, U32 cols) { for (U32 j = col,k = 0 ; k < rows ; j+=cols, k++) { bigBuff[j].re = (F32)stageBuff[k].re; bigBuff[j].im = (F32)stageBuff[k].im; } } /* do something with an error message */ inline void handle_error(S8* msg) { llerrs << msg << llendl; } /* ** compute DFT: forward if direction==0, inverse if direction==1 ** array must be COMPLEX */ BOOL fft(const LLFFTPlan& plan, COMPLEX *array, S32 rows, S32 cols, S32 direction) { S32 i; if (!plan.valid() || plan.rows() != rows || plan.cols() != cols) return FALSE; /* compute transform row by row */ if(cols>1) { for(i=0;i 1) { cr1[kk] = c4*(br0-br1) - s4*(bi0-bi1); cr2[kk] = c2*(br2-bi3) - s2*(bi2+br3); cr3[kk] = c6*(br2+bi3) - s6*(bi2-br3); ci1[kk] = c4*(bi0-bi1) + s4*(br0-br1); ci2[kk] = c2*(bi2+br3) + s2*(br2-bi3); ci3[kk] = c6*(bi2-br3) + s6*(br2+bi3); tr = OO_SQRT2*(br5-bi5); ti = OO_SQRT2*(br5+bi5); cr4[kk] = c1*(br4+tr) - s1*(bi4+ti); ci4[kk] = c1*(bi4+ti) + s1*(br4+tr); cr5[kk] = c5*(br4-tr) - s5*(bi4-ti); ci5[kk] = c5*(bi4-ti) + s5*(br4-tr); tr = -OO_SQRT2*(br7+bi7); ti = OO_SQRT2*(br7-bi7); cr6[kk] = c3*(br6+tr) - s3*(bi6+ti); ci6[kk] = c3*(bi6+ti) + s3*(br6+tr); cr7[kk] = c7*(br6-tr) - s7*(bi6-ti); ci7[kk] = c7*(bi6-ti) + s7*(br6-tr); } else { cr1[kk] = br0 - br1; cr2[kk] = br2 - bi3; cr3[kk] = br2 + bi3; ci1[kk] = bi0 - bi1; ci2[kk] = bi2 + br3; ci3[kk] = bi2 - br3; tr = OO_SQRT2*(br5-bi5); ti = OO_SQRT2*(br5+bi5); cr4[kk] = br4 + tr; ci4[kk] = bi4 + ti; cr5[kk] = br4 - tr; ci5[kk] = bi4 - ti; tr = -OO_SQRT2*(br7+bi7); ti = OO_SQRT2*(br7-bi7); cr6[kk] = br6 + tr; ci6[kk] = bi6 + ti; cr7[kk] = br6 - tr; ci7[kk] = bi6 - ti; } } } } /* see if exactly one bit is set in integer argument */ S32 power_of_2(S32 n) { S32 bits=0; while(n) { bits += n & 1; n >>= 1; } return(bits==1); } /* get binary log of integer argument; exact if n a power of 2 */ S32 fastlog2(S32 n) { S32 log = -1; while(n) { log++; n >>= 1; } return(log); }