加入频谱
This commit is contained in:
parent
8c0b908ce9
commit
016641f04d
167
Source/Cut5/FFT/_kiss_fft_guts.h
Normal file
167
Source/Cut5/FFT/_kiss_fft_guts.h
Normal file
@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
/* kiss_fft.h
|
||||
defines kiss_fft_scalar as either short or a float type
|
||||
and defines
|
||||
typedef struct { kiss_fft_scalar r; kiss_fft_scalar i; }kiss_fft_cpx; */
|
||||
|
||||
#ifndef _kiss_fft_guts_h
|
||||
#define _kiss_fft_guts_h
|
||||
|
||||
#include "kiss_fft.h"
|
||||
#include "kiss_fft_log.h"
|
||||
#include <limits.h>
|
||||
|
||||
#define MAXFACTORS 32
|
||||
/* e.g. an fft of length 128 has 4 factors
|
||||
as far as kissfft is concerned
|
||||
4*4*4*2
|
||||
*/
|
||||
|
||||
struct kiss_fft_state{
|
||||
int nfft;
|
||||
int inverse;
|
||||
int factors[2*MAXFACTORS];
|
||||
kiss_fft_cpx twiddles[1];
|
||||
};
|
||||
|
||||
/*
|
||||
Explanation of macros dealing with complex math:
|
||||
|
||||
C_MUL(m,a,b) : m = a*b
|
||||
C_FIXDIV( c , div ) : if a fixed point impl., c /= div. noop otherwise
|
||||
C_SUB( res, a,b) : res = a - b
|
||||
C_SUBFROM( res , a) : res -= a
|
||||
C_ADDTO( res , a) : res += a
|
||||
* */
|
||||
#ifdef FIXED_POINT
|
||||
#include <stdint.h>
|
||||
#if (FIXED_POINT==32)
|
||||
# define FRACBITS 31
|
||||
# define SAMPPROD int64_t
|
||||
#define SAMP_MAX INT32_MAX
|
||||
#define SAMP_MIN INT32_MIN
|
||||
#else
|
||||
# define FRACBITS 15
|
||||
# define SAMPPROD int32_t
|
||||
#define SAMP_MAX INT16_MAX
|
||||
#define SAMP_MIN INT16_MIN
|
||||
#endif
|
||||
|
||||
#if defined(CHECK_OVERFLOW)
|
||||
# define CHECK_OVERFLOW_OP(a,op,b) \
|
||||
if ( (SAMPPROD)(a) op (SAMPPROD)(b) > SAMP_MAX || (SAMPPROD)(a) op (SAMPPROD)(b) < SAMP_MIN ) { \
|
||||
KISS_FFT_WARNING("overflow (%d " #op" %d) = %ld", (a),(b),(SAMPPROD)(a) op (SAMPPROD)(b)); }
|
||||
#endif
|
||||
|
||||
|
||||
# define smul(a,b) ( (SAMPPROD)(a)*(b) )
|
||||
# define sround( x ) (kiss_fft_scalar)( ( (x) + (1<<(FRACBITS-1)) ) >> FRACBITS )
|
||||
|
||||
# define S_MUL(a,b) sround( smul(a,b) )
|
||||
|
||||
# define C_MUL(m,a,b) \
|
||||
do{ (m).r = sround( smul((a).r,(b).r) - smul((a).i,(b).i) ); \
|
||||
(m).i = sround( smul((a).r,(b).i) + smul((a).i,(b).r) ); }while(0)
|
||||
|
||||
# define DIVSCALAR(x,k) \
|
||||
(x) = sround( smul( x, SAMP_MAX/k ) )
|
||||
|
||||
# define C_FIXDIV(c,div) \
|
||||
do { DIVSCALAR( (c).r , div); \
|
||||
DIVSCALAR( (c).i , div); }while (0)
|
||||
|
||||
# define C_MULBYSCALAR( c, s ) \
|
||||
do{ (c).r = sround( smul( (c).r , s ) ) ;\
|
||||
(c).i = sround( smul( (c).i , s ) ) ; }while(0)
|
||||
|
||||
#else /* not FIXED_POINT*/
|
||||
|
||||
# define S_MUL(a,b) ( (a)*(b) )
|
||||
#define C_MUL(m,a,b) \
|
||||
do{ (m).r = (a).r*(b).r - (a).i*(b).i;\
|
||||
(m).i = (a).r*(b).i + (a).i*(b).r; }while(0)
|
||||
# define C_FIXDIV(c,div) /* NOOP */
|
||||
# define C_MULBYSCALAR( c, s ) \
|
||||
do{ (c).r *= (s);\
|
||||
(c).i *= (s); }while(0)
|
||||
#endif
|
||||
|
||||
#ifndef CHECK_OVERFLOW_OP
|
||||
# define CHECK_OVERFLOW_OP(a,op,b) /* noop */
|
||||
#endif
|
||||
|
||||
#define C_ADD( res, a,b)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r,+,(b).r)\
|
||||
CHECK_OVERFLOW_OP((a).i,+,(b).i)\
|
||||
(res).r=(a).r+(b).r; (res).i=(a).i+(b).i; \
|
||||
}while(0)
|
||||
#define C_SUB( res, a,b)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((a).r,-,(b).r)\
|
||||
CHECK_OVERFLOW_OP((a).i,-,(b).i)\
|
||||
(res).r=(a).r-(b).r; (res).i=(a).i-(b).i; \
|
||||
}while(0)
|
||||
#define C_ADDTO( res , a)\
|
||||
do { \
|
||||
CHECK_OVERFLOW_OP((res).r,+,(a).r)\
|
||||
CHECK_OVERFLOW_OP((res).i,+,(a).i)\
|
||||
(res).r += (a).r; (res).i += (a).i;\
|
||||
}while(0)
|
||||
|
||||
#define C_SUBFROM( res , a)\
|
||||
do {\
|
||||
CHECK_OVERFLOW_OP((res).r,-,(a).r)\
|
||||
CHECK_OVERFLOW_OP((res).i,-,(a).i)\
|
||||
(res).r -= (a).r; (res).i -= (a).i; \
|
||||
}while(0)
|
||||
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
# define KISS_FFT_COS(phase) floor(.5+SAMP_MAX * cos (phase))
|
||||
# define KISS_FFT_SIN(phase) floor(.5+SAMP_MAX * sin (phase))
|
||||
# define HALF_OF(x) ((x)>>1)
|
||||
#elif defined(USE_SIMD)
|
||||
# define KISS_FFT_COS(phase) _mm_set1_ps( cos(phase) )
|
||||
# define KISS_FFT_SIN(phase) _mm_set1_ps( sin(phase) )
|
||||
# define HALF_OF(x) ((x)*_mm_set1_ps(.5))
|
||||
#else
|
||||
# define KISS_FFT_COS(phase) (kiss_fft_scalar) cos(phase)
|
||||
# define KISS_FFT_SIN(phase) (kiss_fft_scalar) sin(phase)
|
||||
# define HALF_OF(x) ((x)*((kiss_fft_scalar).5))
|
||||
#endif
|
||||
|
||||
#define kf_cexp(x,phase) \
|
||||
do{ \
|
||||
(x)->r = KISS_FFT_COS(phase);\
|
||||
(x)->i = KISS_FFT_SIN(phase);\
|
||||
}while(0)
|
||||
|
||||
|
||||
/* a debugging function */
|
||||
#define pcpx(c)\
|
||||
KISS_FFT_DEBUG("%g + %gi\n",(double)((c)->r),(double)((c)->i))
|
||||
|
||||
|
||||
#ifdef KISS_FFT_USE_ALLOCA
|
||||
// define this to allow use of alloca instead of malloc for temporary buffers
|
||||
// Temporary buffers are used in two case:
|
||||
// 1. FFT sizes that have "bad" factors. i.e. not 2,3 and 5
|
||||
// 2. "in-place" FFTs. Notice the quotes, since kissfft does not really do an in-place transform.
|
||||
#include <alloca.h>
|
||||
#define KISS_FFT_TMP_ALLOC(nbytes) alloca(nbytes)
|
||||
#define KISS_FFT_TMP_FREE(ptr)
|
||||
#else
|
||||
#define KISS_FFT_TMP_ALLOC(nbytes) KISS_FFT_MALLOC(nbytes)
|
||||
#define KISS_FFT_TMP_FREE(ptr) KISS_FFT_FREE(ptr)
|
||||
#endif
|
||||
|
||||
#endif /* _kiss_fft_guts_h */
|
||||
|
109
Source/Cut5/FFT/kfc.c
Normal file
109
Source/Cut5/FFT/kfc.c
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#include "kfc.h"
|
||||
|
||||
typedef struct cached_fft *kfc_cfg;
|
||||
|
||||
struct cached_fft
|
||||
{
|
||||
int nfft;
|
||||
int inverse;
|
||||
kiss_fft_cfg cfg;
|
||||
kfc_cfg next;
|
||||
};
|
||||
|
||||
static kfc_cfg cache_root=NULL;
|
||||
static int ncached=0;
|
||||
|
||||
static kiss_fft_cfg find_cached_fft(int nfft,int inverse)
|
||||
{
|
||||
size_t len;
|
||||
kfc_cfg cur=cache_root;
|
||||
kfc_cfg prev=NULL;
|
||||
while ( cur ) {
|
||||
if ( cur->nfft == nfft && inverse == cur->inverse )
|
||||
break;/*found the right node*/
|
||||
prev = cur;
|
||||
cur = prev->next;
|
||||
}
|
||||
if (cur== NULL) {
|
||||
/* no cached node found, need to create a new one*/
|
||||
kiss_fft_alloc(nfft,inverse,0,&len);
|
||||
#ifdef USE_SIMD
|
||||
int padding = (16-sizeof(struct cached_fft)) & 15;
|
||||
// make sure the cfg aligns on a 16 byte boundary
|
||||
len += padding;
|
||||
#endif
|
||||
cur = (kfc_cfg)KISS_FFT_MALLOC((sizeof(struct cached_fft) + len ));
|
||||
if (cur == NULL)
|
||||
return NULL;
|
||||
cur->cfg = (kiss_fft_cfg)(cur+1);
|
||||
#ifdef USE_SIMD
|
||||
cur->cfg = (kiss_fft_cfg) ((char*)(cur+1)+padding);
|
||||
#endif
|
||||
kiss_fft_alloc(nfft,inverse,cur->cfg,&len);
|
||||
cur->nfft=nfft;
|
||||
cur->inverse=inverse;
|
||||
cur->next = NULL;
|
||||
if ( prev )
|
||||
prev->next = cur;
|
||||
else
|
||||
cache_root = cur;
|
||||
++ncached;
|
||||
}
|
||||
return cur->cfg;
|
||||
}
|
||||
|
||||
void kfc_cleanup(void)
|
||||
{
|
||||
kfc_cfg cur=cache_root;
|
||||
kfc_cfg next=NULL;
|
||||
while (cur){
|
||||
next = cur->next;
|
||||
free(cur);
|
||||
cur=next;
|
||||
}
|
||||
ncached=0;
|
||||
cache_root = NULL;
|
||||
}
|
||||
void kfc_fft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout)
|
||||
{
|
||||
kiss_fft( find_cached_fft(nfft,0),fin,fout );
|
||||
}
|
||||
|
||||
void kfc_ifft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout)
|
||||
{
|
||||
kiss_fft( find_cached_fft(nfft,1),fin,fout );
|
||||
}
|
||||
|
||||
#ifdef KFC_TEST
|
||||
static void check(int nc)
|
||||
{
|
||||
if (ncached != nc) {
|
||||
fprintf(stderr,"ncached should be %d,but it is %d\n",nc,ncached);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
kiss_fft_cpx buf1[1024],buf2[1024];
|
||||
memset(buf1,0,sizeof(buf1));
|
||||
check(0);
|
||||
kfc_fft(512,buf1,buf2);
|
||||
check(1);
|
||||
kfc_fft(512,buf1,buf2);
|
||||
check(1);
|
||||
kfc_ifft(512,buf1,buf2);
|
||||
check(2);
|
||||
kfc_cleanup();
|
||||
check(0);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
54
Source/Cut5/FFT/kfc.h
Normal file
54
Source/Cut5/FFT/kfc.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef KFC_H
|
||||
#define KFC_H
|
||||
#include "kiss_fft.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
KFC -- Kiss FFT Cache
|
||||
|
||||
Not needing to deal with kiss_fft_alloc and a config
|
||||
object may be handy for a lot of programs.
|
||||
|
||||
KFC uses the underlying KISS FFT functions, but caches the config object.
|
||||
The first time kfc_fft or kfc_ifft for a given FFT size, the cfg
|
||||
object is created for it. All subsequent calls use the cached
|
||||
configuration object.
|
||||
|
||||
NOTE:
|
||||
You should probably not use this if your program will be using a lot
|
||||
of various sizes of FFTs. There is a linear search through the
|
||||
cached objects. If you are only using one or two FFT sizes, this
|
||||
will be negligible. Otherwise, you may want to use another method
|
||||
of managing the cfg objects.
|
||||
|
||||
There is no automated cleanup of the cached objects. This could lead
|
||||
to large memory usage in a program that uses a lot of *DIFFERENT*
|
||||
sized FFTs. If you want to force all cached cfg objects to be freed,
|
||||
call kfc_cleanup.
|
||||
|
||||
*/
|
||||
|
||||
/*forward complex FFT */
|
||||
void KISS_FFT_API kfc_fft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout);
|
||||
/*reverse complex FFT */
|
||||
void KISS_FFT_API kfc_ifft(int nfft, const kiss_fft_cpx * fin,kiss_fft_cpx * fout);
|
||||
|
||||
/*free all cached objects*/
|
||||
void KISS_FFT_API kfc_cleanup(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
420
Source/Cut5/FFT/kiss_fft.c
Normal file
420
Source/Cut5/FFT/kiss_fft.c
Normal file
@ -0,0 +1,420 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
|
||||
#include "_kiss_fft_guts.h"
|
||||
/* The guts header contains all the multiplication and addition macros that are defined for
|
||||
fixed or floating point complex numbers. It also delares the kf_ internal functions.
|
||||
*/
|
||||
|
||||
static void kf_bfly2(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
int m
|
||||
)
|
||||
{
|
||||
kiss_fft_cpx * Fout2;
|
||||
kiss_fft_cpx * tw1 = st->twiddles;
|
||||
kiss_fft_cpx t;
|
||||
Fout2 = Fout + m;
|
||||
do{
|
||||
C_FIXDIV(*Fout,2); C_FIXDIV(*Fout2,2);
|
||||
|
||||
C_MUL (t, *Fout2 , *tw1);
|
||||
tw1 += fstride;
|
||||
C_SUB( *Fout2 , *Fout , t );
|
||||
C_ADDTO( *Fout , t );
|
||||
++Fout2;
|
||||
++Fout;
|
||||
}while (--m);
|
||||
}
|
||||
|
||||
static void kf_bfly4(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
const size_t m
|
||||
)
|
||||
{
|
||||
kiss_fft_cpx *tw1,*tw2,*tw3;
|
||||
kiss_fft_cpx scratch[6];
|
||||
size_t k=m;
|
||||
const size_t m2=2*m;
|
||||
const size_t m3=3*m;
|
||||
|
||||
|
||||
tw3 = tw2 = tw1 = st->twiddles;
|
||||
|
||||
do {
|
||||
C_FIXDIV(*Fout,4); C_FIXDIV(Fout[m],4); C_FIXDIV(Fout[m2],4); C_FIXDIV(Fout[m3],4);
|
||||
|
||||
C_MUL(scratch[0],Fout[m] , *tw1 );
|
||||
C_MUL(scratch[1],Fout[m2] , *tw2 );
|
||||
C_MUL(scratch[2],Fout[m3] , *tw3 );
|
||||
|
||||
C_SUB( scratch[5] , *Fout, scratch[1] );
|
||||
C_ADDTO(*Fout, scratch[1]);
|
||||
C_ADD( scratch[3] , scratch[0] , scratch[2] );
|
||||
C_SUB( scratch[4] , scratch[0] , scratch[2] );
|
||||
C_SUB( Fout[m2], *Fout, scratch[3] );
|
||||
tw1 += fstride;
|
||||
tw2 += fstride*2;
|
||||
tw3 += fstride*3;
|
||||
C_ADDTO( *Fout , scratch[3] );
|
||||
|
||||
if(st->inverse) {
|
||||
Fout[m].r = scratch[5].r - scratch[4].i;
|
||||
Fout[m].i = scratch[5].i + scratch[4].r;
|
||||
Fout[m3].r = scratch[5].r + scratch[4].i;
|
||||
Fout[m3].i = scratch[5].i - scratch[4].r;
|
||||
}else{
|
||||
Fout[m].r = scratch[5].r + scratch[4].i;
|
||||
Fout[m].i = scratch[5].i - scratch[4].r;
|
||||
Fout[m3].r = scratch[5].r - scratch[4].i;
|
||||
Fout[m3].i = scratch[5].i + scratch[4].r;
|
||||
}
|
||||
++Fout;
|
||||
}while(--k);
|
||||
}
|
||||
|
||||
static void kf_bfly3(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
size_t m
|
||||
)
|
||||
{
|
||||
size_t k=m;
|
||||
const size_t m2 = 2*m;
|
||||
kiss_fft_cpx *tw1,*tw2;
|
||||
kiss_fft_cpx scratch[5];
|
||||
kiss_fft_cpx epi3;
|
||||
epi3 = st->twiddles[fstride*m];
|
||||
|
||||
tw1=tw2=st->twiddles;
|
||||
|
||||
do{
|
||||
C_FIXDIV(*Fout,3); C_FIXDIV(Fout[m],3); C_FIXDIV(Fout[m2],3);
|
||||
|
||||
C_MUL(scratch[1],Fout[m] , *tw1);
|
||||
C_MUL(scratch[2],Fout[m2] , *tw2);
|
||||
|
||||
C_ADD(scratch[3],scratch[1],scratch[2]);
|
||||
C_SUB(scratch[0],scratch[1],scratch[2]);
|
||||
tw1 += fstride;
|
||||
tw2 += fstride*2;
|
||||
|
||||
Fout[m].r = Fout->r - HALF_OF(scratch[3].r);
|
||||
Fout[m].i = Fout->i - HALF_OF(scratch[3].i);
|
||||
|
||||
C_MULBYSCALAR( scratch[0] , epi3.i );
|
||||
|
||||
C_ADDTO(*Fout,scratch[3]);
|
||||
|
||||
Fout[m2].r = Fout[m].r + scratch[0].i;
|
||||
Fout[m2].i = Fout[m].i - scratch[0].r;
|
||||
|
||||
Fout[m].r -= scratch[0].i;
|
||||
Fout[m].i += scratch[0].r;
|
||||
|
||||
++Fout;
|
||||
}while(--k);
|
||||
}
|
||||
|
||||
static void kf_bfly5(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
int m
|
||||
)
|
||||
{
|
||||
kiss_fft_cpx *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
|
||||
int u;
|
||||
kiss_fft_cpx scratch[13];
|
||||
kiss_fft_cpx * twiddles = st->twiddles;
|
||||
kiss_fft_cpx *tw;
|
||||
kiss_fft_cpx ya,yb;
|
||||
ya = twiddles[fstride*m];
|
||||
yb = twiddles[fstride*2*m];
|
||||
|
||||
Fout0=Fout;
|
||||
Fout1=Fout0+m;
|
||||
Fout2=Fout0+2*m;
|
||||
Fout3=Fout0+3*m;
|
||||
Fout4=Fout0+4*m;
|
||||
|
||||
tw=st->twiddles;
|
||||
for ( u=0; u<m; ++u ) {
|
||||
C_FIXDIV( *Fout0,5); C_FIXDIV( *Fout1,5); C_FIXDIV( *Fout2,5); C_FIXDIV( *Fout3,5); C_FIXDIV( *Fout4,5);
|
||||
scratch[0] = *Fout0;
|
||||
|
||||
C_MUL(scratch[1] ,*Fout1, tw[u*fstride]);
|
||||
C_MUL(scratch[2] ,*Fout2, tw[2*u*fstride]);
|
||||
C_MUL(scratch[3] ,*Fout3, tw[3*u*fstride]);
|
||||
C_MUL(scratch[4] ,*Fout4, tw[4*u*fstride]);
|
||||
|
||||
C_ADD( scratch[7],scratch[1],scratch[4]);
|
||||
C_SUB( scratch[10],scratch[1],scratch[4]);
|
||||
C_ADD( scratch[8],scratch[2],scratch[3]);
|
||||
C_SUB( scratch[9],scratch[2],scratch[3]);
|
||||
|
||||
Fout0->r += scratch[7].r + scratch[8].r;
|
||||
Fout0->i += scratch[7].i + scratch[8].i;
|
||||
|
||||
scratch[5].r = scratch[0].r + S_MUL(scratch[7].r,ya.r) + S_MUL(scratch[8].r,yb.r);
|
||||
scratch[5].i = scratch[0].i + S_MUL(scratch[7].i,ya.r) + S_MUL(scratch[8].i,yb.r);
|
||||
|
||||
scratch[6].r = S_MUL(scratch[10].i,ya.i) + S_MUL(scratch[9].i,yb.i);
|
||||
scratch[6].i = -S_MUL(scratch[10].r,ya.i) - S_MUL(scratch[9].r,yb.i);
|
||||
|
||||
C_SUB(*Fout1,scratch[5],scratch[6]);
|
||||
C_ADD(*Fout4,scratch[5],scratch[6]);
|
||||
|
||||
scratch[11].r = scratch[0].r + S_MUL(scratch[7].r,yb.r) + S_MUL(scratch[8].r,ya.r);
|
||||
scratch[11].i = scratch[0].i + S_MUL(scratch[7].i,yb.r) + S_MUL(scratch[8].i,ya.r);
|
||||
scratch[12].r = - S_MUL(scratch[10].i,yb.i) + S_MUL(scratch[9].i,ya.i);
|
||||
scratch[12].i = S_MUL(scratch[10].r,yb.i) - S_MUL(scratch[9].r,ya.i);
|
||||
|
||||
C_ADD(*Fout2,scratch[11],scratch[12]);
|
||||
C_SUB(*Fout3,scratch[11],scratch[12]);
|
||||
|
||||
++Fout0;++Fout1;++Fout2;++Fout3;++Fout4;
|
||||
}
|
||||
}
|
||||
|
||||
/* perform the butterfly for one stage of a mixed radix FFT */
|
||||
static void kf_bfly_generic(
|
||||
kiss_fft_cpx * Fout,
|
||||
const size_t fstride,
|
||||
const kiss_fft_cfg st,
|
||||
int m,
|
||||
int p
|
||||
)
|
||||
{
|
||||
int u,k,q1,q;
|
||||
kiss_fft_cpx * twiddles = st->twiddles;
|
||||
kiss_fft_cpx t;
|
||||
int Norig = st->nfft;
|
||||
|
||||
kiss_fft_cpx * scratch = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC(sizeof(kiss_fft_cpx)*p);
|
||||
if (scratch == NULL){
|
||||
KISS_FFT_ERROR("Memory allocation failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
for ( u=0; u<m; ++u ) {
|
||||
k=u;
|
||||
for ( q1=0 ; q1<p ; ++q1 ) {
|
||||
scratch[q1] = Fout[ k ];
|
||||
C_FIXDIV(scratch[q1],p);
|
||||
k += m;
|
||||
}
|
||||
|
||||
k=u;
|
||||
for ( q1=0 ; q1<p ; ++q1 ) {
|
||||
int twidx=0;
|
||||
Fout[ k ] = scratch[0];
|
||||
for (q=1;q<p;++q ) {
|
||||
twidx += fstride * k;
|
||||
if (twidx>=Norig) twidx-=Norig;
|
||||
C_MUL(t,scratch[q] , twiddles[twidx] );
|
||||
C_ADDTO( Fout[ k ] ,t);
|
||||
}
|
||||
k += m;
|
||||
}
|
||||
}
|
||||
KISS_FFT_TMP_FREE(scratch);
|
||||
}
|
||||
|
||||
static
|
||||
void kf_work(
|
||||
kiss_fft_cpx * Fout,
|
||||
const kiss_fft_cpx * f,
|
||||
const size_t fstride,
|
||||
int in_stride,
|
||||
int * factors,
|
||||
const kiss_fft_cfg st
|
||||
)
|
||||
{
|
||||
kiss_fft_cpx * Fout_beg=Fout;
|
||||
const int p=*factors++; /* the radix */
|
||||
const int m=*factors++; /* stage's fft length/p */
|
||||
const kiss_fft_cpx * Fout_end = Fout + p*m;
|
||||
|
||||
#ifdef _OPENMP
|
||||
// use openmp extensions at the
|
||||
// top-level (not recursive)
|
||||
if (fstride==1 && p<=5 && m!=1)
|
||||
{
|
||||
int k;
|
||||
|
||||
// execute the p different work units in different threads
|
||||
# pragma omp parallel for
|
||||
for (k=0;k<p;++k)
|
||||
kf_work( Fout +k*m, f+ fstride*in_stride*k,fstride*p,in_stride,factors,st);
|
||||
// all threads have joined by this point
|
||||
|
||||
switch (p) {
|
||||
case 2: kf_bfly2(Fout,fstride,st,m); break;
|
||||
case 3: kf_bfly3(Fout,fstride,st,m); break;
|
||||
case 4: kf_bfly4(Fout,fstride,st,m); break;
|
||||
case 5: kf_bfly5(Fout,fstride,st,m); break;
|
||||
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m==1) {
|
||||
do{
|
||||
*Fout = *f;
|
||||
f += fstride*in_stride;
|
||||
}while(++Fout != Fout_end );
|
||||
}else{
|
||||
do{
|
||||
// recursive call:
|
||||
// DFT of size m*p performed by doing
|
||||
// p instances of smaller DFTs of size m,
|
||||
// each one takes a decimated version of the input
|
||||
kf_work( Fout , f, fstride*p, in_stride, factors,st);
|
||||
f += fstride*in_stride;
|
||||
}while( (Fout += m) != Fout_end );
|
||||
}
|
||||
|
||||
Fout=Fout_beg;
|
||||
|
||||
// recombine the p smaller DFTs
|
||||
switch (p) {
|
||||
case 2: kf_bfly2(Fout,fstride,st,m); break;
|
||||
case 3: kf_bfly3(Fout,fstride,st,m); break;
|
||||
case 4: kf_bfly4(Fout,fstride,st,m); break;
|
||||
case 5: kf_bfly5(Fout,fstride,st,m); break;
|
||||
default: kf_bfly_generic(Fout,fstride,st,m,p); break;
|
||||
}
|
||||
}
|
||||
|
||||
/* facbuf is populated by p1,m1,p2,m2, ...
|
||||
where
|
||||
p[i] * m[i] = m[i-1]
|
||||
m0 = n */
|
||||
static
|
||||
void kf_factor(int n,int * facbuf)
|
||||
{
|
||||
int p=4;
|
||||
double floor_sqrt;
|
||||
floor_sqrt = floor( sqrt((double)n) );
|
||||
|
||||
/*factor out powers of 4, powers of 2, then any remaining primes */
|
||||
do {
|
||||
while (n % p) {
|
||||
switch (p) {
|
||||
case 4: p = 2; break;
|
||||
case 2: p = 3; break;
|
||||
default: p += 2; break;
|
||||
}
|
||||
if (p > floor_sqrt)
|
||||
p = n; /* no more factors, skip to end */
|
||||
}
|
||||
n /= p;
|
||||
*facbuf++ = p;
|
||||
*facbuf++ = n;
|
||||
} while (n > 1);
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* User-callable function to allocate all necessary storage space for the fft.
|
||||
*
|
||||
* The return value is a contiguous block of memory, allocated with malloc. As such,
|
||||
* It can be freed with free(), rather than a kiss_fft-specific function.
|
||||
* */
|
||||
kiss_fft_cfg kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem )
|
||||
{
|
||||
KISS_FFT_ALIGN_CHECK(mem)
|
||||
|
||||
kiss_fft_cfg st=NULL;
|
||||
size_t memneeded = KISS_FFT_ALIGN_SIZE_UP(sizeof(struct kiss_fft_state)
|
||||
+ sizeof(kiss_fft_cpx)*(nfft-1)); /* twiddle factors*/
|
||||
|
||||
if ( lenmem==NULL ) {
|
||||
st = ( kiss_fft_cfg)KISS_FFT_MALLOC( memneeded );
|
||||
}else{
|
||||
if (mem != NULL && *lenmem >= memneeded)
|
||||
st = (kiss_fft_cfg)mem;
|
||||
*lenmem = memneeded;
|
||||
}
|
||||
if (st) {
|
||||
int i;
|
||||
st->nfft=nfft;
|
||||
st->inverse = inverse_fft;
|
||||
|
||||
for (i=0;i<nfft;++i) {
|
||||
const double pi=3.141592653589793238462643383279502884197169399375105820974944;
|
||||
double phase = -2*pi*i / nfft;
|
||||
if (st->inverse)
|
||||
phase *= -1;
|
||||
kf_cexp(st->twiddles+i, phase );
|
||||
}
|
||||
|
||||
kf_factor(nfft,st->factors);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
|
||||
void kiss_fft_stride(kiss_fft_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int in_stride)
|
||||
{
|
||||
if (fin == fout) {
|
||||
//NOTE: this is not really an in-place FFT algorithm.
|
||||
//It just performs an out-of-place FFT into a temp buffer
|
||||
if (fout == NULL){
|
||||
KISS_FFT_ERROR("fout buffer NULL.");
|
||||
return;
|
||||
}
|
||||
|
||||
kiss_fft_cpx * tmpbuf = (kiss_fft_cpx*)KISS_FFT_TMP_ALLOC( sizeof(kiss_fft_cpx)*st->nfft);
|
||||
if (tmpbuf == NULL){
|
||||
KISS_FFT_ERROR("Memory allocation error.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
kf_work(tmpbuf,fin,1,in_stride, st->factors,st);
|
||||
memcpy(fout,tmpbuf,sizeof(kiss_fft_cpx)*st->nfft);
|
||||
KISS_FFT_TMP_FREE(tmpbuf);
|
||||
}else{
|
||||
kf_work( fout, fin, 1,in_stride, st->factors,st );
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
|
||||
{
|
||||
kiss_fft_stride(cfg,fin,fout,1);
|
||||
}
|
||||
|
||||
|
||||
void kiss_fft_cleanup(void)
|
||||
{
|
||||
// nothing needed any more
|
||||
}
|
||||
|
||||
int kiss_fft_next_fast_size(int n)
|
||||
{
|
||||
while(1) {
|
||||
int m=n;
|
||||
while ( (m%2) == 0 ) m/=2;
|
||||
while ( (m%3) == 0 ) m/=3;
|
||||
while ( (m%5) == 0 ) m/=5;
|
||||
if (m<=1)
|
||||
break; /* n is completely factorable by twos, threes, and fives */
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
160
Source/Cut5/FFT/kiss_fft.h
Normal file
160
Source/Cut5/FFT/kiss_fft.h
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef KISS_FFT_H
|
||||
#define KISS_FFT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <math.h>
|
||||
#include <string.h>
|
||||
|
||||
// Define KISS_FFT_SHARED macro to properly export symbols
|
||||
#ifdef KISS_FFT_SHARED
|
||||
# ifdef _WIN32
|
||||
# ifdef KISS_FFT_BUILD
|
||||
# define KISS_FFT_API __declspec(dllexport)
|
||||
# else
|
||||
# define KISS_FFT_API __declspec(dllimport)
|
||||
# endif
|
||||
# else
|
||||
# define KISS_FFT_API __attribute__ ((visibility ("default")))
|
||||
# endif
|
||||
#else
|
||||
# define KISS_FFT_API
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*
|
||||
ATTENTION!
|
||||
If you would like a :
|
||||
-- a utility that will handle the caching of fft objects
|
||||
-- real-only (no imaginary time component ) FFT
|
||||
-- a multi-dimensional FFT
|
||||
-- a command-line utility to perform ffts
|
||||
-- a command-line utility to perform fast-convolution filtering
|
||||
|
||||
Then see kfc.h kiss_fftr.h kiss_fftnd.h fftutil.c kiss_fastfir.c
|
||||
in the tools/ directory.
|
||||
*/
|
||||
|
||||
/* User may override KISS_FFT_MALLOC and/or KISS_FFT_FREE. */
|
||||
#ifdef USE_SIMD
|
||||
# include <xmmintrin.h>
|
||||
# define kiss_fft_scalar __m128
|
||||
# ifndef KISS_FFT_MALLOC
|
||||
# define KISS_FFT_MALLOC(nbytes) _mm_malloc(nbytes,16)
|
||||
# define KISS_FFT_ALIGN_CHECK(ptr)
|
||||
# define KISS_FFT_ALIGN_SIZE_UP(size) ((size + 15UL) & ~0xFUL)
|
||||
# endif
|
||||
# ifndef KISS_FFT_FREE
|
||||
# define KISS_FFT_FREE _mm_free
|
||||
# endif
|
||||
#else
|
||||
# define KISS_FFT_ALIGN_CHECK(ptr)
|
||||
# define KISS_FFT_ALIGN_SIZE_UP(size) (size)
|
||||
# ifndef KISS_FFT_MALLOC
|
||||
# define KISS_FFT_MALLOC malloc
|
||||
# endif
|
||||
# ifndef KISS_FFT_FREE
|
||||
# define KISS_FFT_FREE free
|
||||
# endif
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef FIXED_POINT
|
||||
#include <stdint.h>
|
||||
# if (FIXED_POINT == 32)
|
||||
# define kiss_fft_scalar int32_t
|
||||
# else
|
||||
# define kiss_fft_scalar int16_t
|
||||
# endif
|
||||
#else
|
||||
# ifndef kiss_fft_scalar
|
||||
/* default is float */
|
||||
# define kiss_fft_scalar float
|
||||
# endif
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
kiss_fft_scalar r;
|
||||
kiss_fft_scalar i;
|
||||
}kiss_fft_cpx;
|
||||
|
||||
typedef struct kiss_fft_state* kiss_fft_cfg;
|
||||
|
||||
/*
|
||||
* kiss_fft_alloc
|
||||
*
|
||||
* Initialize a FFT (or IFFT) algorithm's cfg/state buffer.
|
||||
*
|
||||
* typical usage: kiss_fft_cfg mycfg=kiss_fft_alloc(1024,0,NULL,NULL);
|
||||
*
|
||||
* The return value from fft_alloc is a cfg buffer used internally
|
||||
* by the fft routine or NULL.
|
||||
*
|
||||
* If lenmem is NULL, then kiss_fft_alloc will allocate a cfg buffer using malloc.
|
||||
* The returned value should be free()d when done to avoid memory leaks.
|
||||
*
|
||||
* The state can be placed in a user supplied buffer 'mem':
|
||||
* If lenmem is not NULL and mem is not NULL and *lenmem is large enough,
|
||||
* then the function places the cfg in mem and the size used in *lenmem
|
||||
* and returns mem.
|
||||
*
|
||||
* If lenmem is not NULL and ( mem is NULL or *lenmem is not large enough),
|
||||
* then the function returns NULL and places the minimum cfg
|
||||
* buffer size in *lenmem.
|
||||
* */
|
||||
|
||||
kiss_fft_cfg KISS_FFT_API kiss_fft_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem);
|
||||
|
||||
/*
|
||||
* kiss_fft(cfg,in_out_buf)
|
||||
*
|
||||
* Perform an FFT on a complex input buffer.
|
||||
* for a forward FFT,
|
||||
* fin should be f[0] , f[1] , ... ,f[nfft-1]
|
||||
* fout will be F[0] , F[1] , ... ,F[nfft-1]
|
||||
* Note that each element is complex and can be accessed like
|
||||
f[k].r and f[k].i
|
||||
* */
|
||||
void KISS_FFT_API kiss_fft(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
|
||||
|
||||
/*
|
||||
A more generic version of the above function. It reads its input from every Nth sample.
|
||||
* */
|
||||
void KISS_FFT_API kiss_fft_stride(kiss_fft_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout,int fin_stride);
|
||||
|
||||
/* If kiss_fft_alloc allocated a buffer, it is one contiguous
|
||||
buffer and can be simply free()d when no longer needed*/
|
||||
#define kiss_fft_free KISS_FFT_FREE
|
||||
|
||||
/*
|
||||
Cleans up some memory that gets managed internally. Not necessary to call, but it might clean up
|
||||
your compiler output to call this before you exit.
|
||||
*/
|
||||
void KISS_FFT_API kiss_fft_cleanup(void);
|
||||
|
||||
|
||||
/*
|
||||
* Returns the smallest integer k, such that k>=n and k has only "fast" factors (2,3,5)
|
||||
*/
|
||||
int KISS_FFT_API kiss_fft_next_fast_size(int n);
|
||||
|
||||
/* for real ffts, we need an even size */
|
||||
#define kiss_fftr_next_fast_size_real(n) \
|
||||
(kiss_fft_next_fast_size( ((n)+1)>>1)<<1)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
36
Source/Cut5/FFT/kiss_fft_log.h
Normal file
36
Source/Cut5/FFT/kiss_fft_log.h
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef kiss_fft_log_h
|
||||
#define kiss_fft_log_h
|
||||
|
||||
#define ERROR 1
|
||||
#define WARNING 2
|
||||
#define INFO 3
|
||||
#define DEBUG 4
|
||||
|
||||
#define STRINGIFY(x) #x
|
||||
#define TOSTRING(x) STRINGIFY(x)
|
||||
|
||||
#if defined(NDEBUG)
|
||||
# define KISS_FFT_LOG_MSG(severity, ...) ((void)0)
|
||||
#else
|
||||
# define KISS_FFT_LOG_MSG(severity, ...) \
|
||||
fprintf(stderr, "[" #severity "] " __FILE__ ":" TOSTRING(__LINE__) " "); \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fprintf(stderr, "\n")
|
||||
#endif
|
||||
|
||||
#define KISS_FFT_ERROR(...) KISS_FFT_LOG_MSG(ERROR, __VA_ARGS__)
|
||||
#define KISS_FFT_WARNING(...) KISS_FFT_LOG_MSG(WARNING, __VA_ARGS__)
|
||||
#define KISS_FFT_INFO(...) KISS_FFT_LOG_MSG(INFO, __VA_ARGS__)
|
||||
#define KISS_FFT_DEBUG(...) KISS_FFT_LOG_MSG(DEBUG, __VA_ARGS__)
|
||||
|
||||
|
||||
|
||||
#endif /* kiss_fft_log_h */
|
188
Source/Cut5/FFT/kiss_fftnd.c
Normal file
188
Source/Cut5/FFT/kiss_fftnd.c
Normal file
@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#include "kiss_fftnd.h"
|
||||
#include "_kiss_fft_guts.h"
|
||||
|
||||
struct kiss_fftnd_state{
|
||||
int dimprod; /* dimsum would be mighty tasty right now */
|
||||
int ndims;
|
||||
int *dims;
|
||||
kiss_fft_cfg *states; /* cfg states for each dimension */
|
||||
kiss_fft_cpx * tmpbuf; /*buffer capable of hold the entire input */
|
||||
};
|
||||
|
||||
kiss_fftnd_cfg kiss_fftnd_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem)
|
||||
{
|
||||
KISS_FFT_ALIGN_CHECK(mem)
|
||||
|
||||
kiss_fftnd_cfg st = NULL;
|
||||
int i;
|
||||
int dimprod=1;
|
||||
size_t memneeded = KISS_FFT_ALIGN_SIZE_UP(sizeof(struct kiss_fftnd_state));
|
||||
char * ptr = NULL;
|
||||
|
||||
for (i=0;i<ndims;++i) {
|
||||
size_t sublen=0;
|
||||
kiss_fft_alloc (dims[i], inverse_fft, NULL, &sublen);
|
||||
memneeded += sublen; /* st->states[i] */
|
||||
dimprod *= dims[i];
|
||||
}
|
||||
memneeded += KISS_FFT_ALIGN_SIZE_UP(sizeof(int) * ndims);/* st->dims */
|
||||
memneeded += KISS_FFT_ALIGN_SIZE_UP(sizeof(void*) * ndims);/* st->states */
|
||||
memneeded += KISS_FFT_ALIGN_SIZE_UP(sizeof(kiss_fft_cpx) * dimprod); /* st->tmpbuf */
|
||||
|
||||
if (lenmem == NULL) {/* allocate for the caller*/
|
||||
ptr = (char *) malloc (memneeded);
|
||||
} else { /* initialize supplied buffer if big enough */
|
||||
if (*lenmem >= memneeded)
|
||||
ptr = (char *) mem;
|
||||
*lenmem = memneeded; /*tell caller how big struct is (or would be) */
|
||||
}
|
||||
if (!ptr)
|
||||
return NULL; /*malloc failed or buffer too small */
|
||||
|
||||
st = (kiss_fftnd_cfg) ptr;
|
||||
st->dimprod = dimprod;
|
||||
st->ndims = ndims;
|
||||
ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(struct kiss_fftnd_state));
|
||||
|
||||
st->states = (kiss_fft_cfg *)ptr;
|
||||
ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(void*) * ndims);
|
||||
|
||||
st->dims = (int*)ptr;
|
||||
ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(int) * ndims);
|
||||
|
||||
st->tmpbuf = (kiss_fft_cpx*)ptr;
|
||||
ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(kiss_fft_cpx) * dimprod);
|
||||
|
||||
for (i=0;i<ndims;++i) {
|
||||
size_t len;
|
||||
st->dims[i] = dims[i];
|
||||
kiss_fft_alloc (st->dims[i], inverse_fft, NULL, &len);
|
||||
st->states[i] = kiss_fft_alloc (st->dims[i], inverse_fft, ptr,&len);
|
||||
ptr += len;
|
||||
}
|
||||
/*
|
||||
Hi there!
|
||||
|
||||
If you're looking at this particular code, it probably means you've got a brain-dead bounds checker
|
||||
that thinks the above code overwrites the end of the array.
|
||||
|
||||
It doesn't.
|
||||
|
||||
-- Mark
|
||||
|
||||
P.S.
|
||||
The below code might give you some warm fuzzies and help convince you.
|
||||
*/
|
||||
if ( ptr - (char*)st != (int)memneeded ) {
|
||||
fprintf(stderr,
|
||||
"################################################################################\n"
|
||||
"Internal error! Memory allocation miscalculation\n"
|
||||
"################################################################################\n"
|
||||
);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
/*
|
||||
This works by tackling one dimension at a time.
|
||||
|
||||
In effect,
|
||||
Each stage starts out by reshaping the matrix into a DixSi 2d matrix.
|
||||
A Di-sized fft is taken of each column, transposing the matrix as it goes.
|
||||
|
||||
Here's a 3-d example:
|
||||
Take a 2x3x4 matrix, laid out in memory as a contiguous buffer
|
||||
[ [ [ a b c d ] [ e f g h ] [ i j k l ] ]
|
||||
[ [ m n o p ] [ q r s t ] [ u v w x ] ] ]
|
||||
|
||||
Stage 0 ( D=2): treat the buffer as a 2x12 matrix
|
||||
[ [a b ... k l]
|
||||
[m n ... w x] ]
|
||||
|
||||
FFT each column with size 2.
|
||||
Transpose the matrix at the same time using kiss_fft_stride.
|
||||
|
||||
[ [ a+m a-m ]
|
||||
[ b+n b-n]
|
||||
...
|
||||
[ k+w k-w ]
|
||||
[ l+x l-x ] ]
|
||||
|
||||
Note fft([x y]) == [x+y x-y]
|
||||
|
||||
Stage 1 ( D=3) treats the buffer (the output of stage D=2) as an 3x8 matrix,
|
||||
[ [ a+m a-m b+n b-n c+o c-o d+p d-p ]
|
||||
[ e+q e-q f+r f-r g+s g-s h+t h-t ]
|
||||
[ i+u i-u j+v j-v k+w k-w l+x l-x ] ]
|
||||
|
||||
And perform FFTs (size=3) on each of the columns as above, transposing
|
||||
the matrix as it goes. The output of stage 1 is
|
||||
(Legend: ap = [ a+m e+q i+u ]
|
||||
am = [ a-m e-q i-u ] )
|
||||
|
||||
[ [ sum(ap) fft(ap)[0] fft(ap)[1] ]
|
||||
[ sum(am) fft(am)[0] fft(am)[1] ]
|
||||
[ sum(bp) fft(bp)[0] fft(bp)[1] ]
|
||||
[ sum(bm) fft(bm)[0] fft(bm)[1] ]
|
||||
[ sum(cp) fft(cp)[0] fft(cp)[1] ]
|
||||
[ sum(cm) fft(cm)[0] fft(cm)[1] ]
|
||||
[ sum(dp) fft(dp)[0] fft(dp)[1] ]
|
||||
[ sum(dm) fft(dm)[0] fft(dm)[1] ] ]
|
||||
|
||||
Stage 2 ( D=4) treats this buffer as a 4*6 matrix,
|
||||
[ [ sum(ap) fft(ap)[0] fft(ap)[1] sum(am) fft(am)[0] fft(am)[1] ]
|
||||
[ sum(bp) fft(bp)[0] fft(bp)[1] sum(bm) fft(bm)[0] fft(bm)[1] ]
|
||||
[ sum(cp) fft(cp)[0] fft(cp)[1] sum(cm) fft(cm)[0] fft(cm)[1] ]
|
||||
[ sum(dp) fft(dp)[0] fft(dp)[1] sum(dm) fft(dm)[0] fft(dm)[1] ] ]
|
||||
|
||||
Then FFTs each column, transposing as it goes.
|
||||
|
||||
The resulting matrix is the 3d FFT of the 2x3x4 input matrix.
|
||||
|
||||
Note as a sanity check that the first element of the final
|
||||
stage's output (DC term) is
|
||||
sum( [ sum(ap) sum(bp) sum(cp) sum(dp) ] )
|
||||
, i.e. the summation of all 24 input elements.
|
||||
|
||||
*/
|
||||
void kiss_fftnd(kiss_fftnd_cfg st,const kiss_fft_cpx *fin,kiss_fft_cpx *fout)
|
||||
{
|
||||
int i,k;
|
||||
const kiss_fft_cpx * bufin=fin;
|
||||
kiss_fft_cpx * bufout;
|
||||
|
||||
/*arrange it so the last bufout == fout*/
|
||||
if ( st->ndims & 1 ) {
|
||||
bufout = fout;
|
||||
if (fin==fout) {
|
||||
memcpy( st->tmpbuf, fin, sizeof(kiss_fft_cpx) * st->dimprod );
|
||||
bufin = st->tmpbuf;
|
||||
}
|
||||
}else
|
||||
bufout = st->tmpbuf;
|
||||
|
||||
for ( k=0; k < st->ndims; ++k) {
|
||||
int curdim = st->dims[k];
|
||||
int stride = st->dimprod / curdim;
|
||||
|
||||
for ( i=0 ; i<stride ; ++i )
|
||||
kiss_fft_stride( st->states[k], bufin+i , bufout+i*curdim, stride );
|
||||
|
||||
/*toggle back and forth between the two buffers*/
|
||||
if (bufout == st->tmpbuf){
|
||||
bufout = fout;
|
||||
bufin = st->tmpbuf;
|
||||
}else{
|
||||
bufout = st->tmpbuf;
|
||||
bufin = fout;
|
||||
}
|
||||
}
|
||||
}
|
26
Source/Cut5/FFT/kiss_fftnd.h
Normal file
26
Source/Cut5/FFT/kiss_fftnd.h
Normal file
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef KISS_FFTND_H
|
||||
#define KISS_FFTND_H
|
||||
|
||||
#include "kiss_fft.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct kiss_fftnd_state * kiss_fftnd_cfg;
|
||||
|
||||
kiss_fftnd_cfg KISS_FFT_API kiss_fftnd_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem);
|
||||
void KISS_FFT_API kiss_fftnd(kiss_fftnd_cfg cfg,const kiss_fft_cpx *fin,kiss_fft_cpx *fout);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
120
Source/Cut5/FFT/kiss_fftndr.c
Normal file
120
Source/Cut5/FFT/kiss_fftndr.c
Normal file
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#include "kiss_fftndr.h"
|
||||
#include "_kiss_fft_guts.h"
|
||||
#define MAX(x,y) ( ( (x)<(y) )?(y):(x) )
|
||||
|
||||
struct kiss_fftndr_state
|
||||
{
|
||||
int dimReal;
|
||||
int dimOther;
|
||||
kiss_fftr_cfg cfg_r;
|
||||
kiss_fftnd_cfg cfg_nd;
|
||||
void * tmpbuf;
|
||||
};
|
||||
|
||||
static int prod(const int *dims, int ndims)
|
||||
{
|
||||
int x=1;
|
||||
while (ndims--)
|
||||
x *= *dims++;
|
||||
return x;
|
||||
}
|
||||
|
||||
kiss_fftndr_cfg kiss_fftndr_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem)
|
||||
{
|
||||
KISS_FFT_ALIGN_CHECK(mem)
|
||||
|
||||
kiss_fftndr_cfg st = NULL;
|
||||
size_t nr=0 , nd=0,ntmp=0;
|
||||
int dimReal = dims[ndims-1];
|
||||
int dimOther = prod(dims,ndims-1);
|
||||
size_t memneeded;
|
||||
char * ptr = NULL;
|
||||
|
||||
(void)kiss_fftr_alloc(dimReal,inverse_fft,NULL,&nr);
|
||||
(void)kiss_fftnd_alloc(dims,ndims-1,inverse_fft,NULL,&nd);
|
||||
ntmp =
|
||||
MAX( 2*dimOther , dimReal+2) * sizeof(kiss_fft_scalar) // freq buffer for one pass
|
||||
+ dimOther*(dimReal+2) * sizeof(kiss_fft_scalar); // large enough to hold entire input in case of in-place
|
||||
|
||||
memneeded = KISS_FFT_ALIGN_SIZE_UP(sizeof( struct kiss_fftndr_state )) + KISS_FFT_ALIGN_SIZE_UP(nr) + KISS_FFT_ALIGN_SIZE_UP(nd) + KISS_FFT_ALIGN_SIZE_UP(ntmp);
|
||||
|
||||
if (lenmem==NULL) {
|
||||
ptr = (char*) malloc(memneeded);
|
||||
}else{
|
||||
if (*lenmem >= memneeded)
|
||||
ptr = (char *)mem;
|
||||
*lenmem = memneeded;
|
||||
}
|
||||
if (ptr==NULL)
|
||||
return NULL;
|
||||
|
||||
st = (kiss_fftndr_cfg) ptr;
|
||||
memset( st , 0 , memneeded);
|
||||
ptr += KISS_FFT_ALIGN_SIZE_UP(sizeof(struct kiss_fftndr_state));
|
||||
|
||||
st->dimReal = dimReal;
|
||||
st->dimOther = dimOther;
|
||||
st->cfg_r = kiss_fftr_alloc( dimReal,inverse_fft,ptr,&nr);
|
||||
ptr += KISS_FFT_ALIGN_SIZE_UP(nr);
|
||||
st->cfg_nd = kiss_fftnd_alloc(dims,ndims-1,inverse_fft, ptr,&nd);
|
||||
ptr += KISS_FFT_ALIGN_SIZE_UP(nd);
|
||||
st->tmpbuf = ptr;
|
||||
|
||||
return st;
|
||||
}
|
||||
|
||||
void kiss_fftndr(kiss_fftndr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
|
||||
{
|
||||
int k1,k2;
|
||||
int dimReal = st->dimReal;
|
||||
int dimOther = st->dimOther;
|
||||
int nrbins = dimReal/2+1;
|
||||
|
||||
kiss_fft_cpx * tmp1 = (kiss_fft_cpx*)st->tmpbuf;
|
||||
kiss_fft_cpx * tmp2 = tmp1 + MAX(nrbins,dimOther);
|
||||
|
||||
// timedata is N0 x N1 x ... x Nk real
|
||||
|
||||
// take a real chunk of data, fft it and place the output at correct intervals
|
||||
for (k1=0;k1<dimOther;++k1) {
|
||||
kiss_fftr( st->cfg_r, timedata + k1*dimReal , tmp1 ); // tmp1 now holds nrbins complex points
|
||||
for (k2=0;k2<nrbins;++k2)
|
||||
tmp2[ k2*dimOther+k1 ] = tmp1[k2];
|
||||
}
|
||||
|
||||
for (k2=0;k2<nrbins;++k2) {
|
||||
kiss_fftnd(st->cfg_nd, tmp2+k2*dimOther, tmp1); // tmp1 now holds dimOther complex points
|
||||
for (k1=0;k1<dimOther;++k1)
|
||||
freqdata[ k1*(nrbins) + k2] = tmp1[k1];
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_fftndri(kiss_fftndr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
|
||||
{
|
||||
int k1,k2;
|
||||
int dimReal = st->dimReal;
|
||||
int dimOther = st->dimOther;
|
||||
int nrbins = dimReal/2+1;
|
||||
kiss_fft_cpx * tmp1 = (kiss_fft_cpx*)st->tmpbuf;
|
||||
kiss_fft_cpx * tmp2 = tmp1 + MAX(nrbins,dimOther);
|
||||
|
||||
for (k2=0;k2<nrbins;++k2) {
|
||||
for (k1=0;k1<dimOther;++k1)
|
||||
tmp1[k1] = freqdata[ k1*(nrbins) + k2 ];
|
||||
kiss_fftnd(st->cfg_nd, tmp1, tmp2+k2*dimOther);
|
||||
}
|
||||
|
||||
for (k1=0;k1<dimOther;++k1) {
|
||||
for (k2=0;k2<nrbins;++k2)
|
||||
tmp1[k2] = tmp2[ k2*dimOther+k1 ];
|
||||
kiss_fftri( st->cfg_r,tmp1,timedata + k1*dimReal);
|
||||
}
|
||||
}
|
55
Source/Cut5/FFT/kiss_fftndr.h
Normal file
55
Source/Cut5/FFT/kiss_fftndr.h
Normal file
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef KISS_NDR_H
|
||||
#define KISS_NDR_H
|
||||
|
||||
#include "kiss_fft.h"
|
||||
#include "kiss_fftr.h"
|
||||
#include "kiss_fftnd.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct kiss_fftndr_state *kiss_fftndr_cfg;
|
||||
|
||||
|
||||
kiss_fftndr_cfg KISS_FFT_API kiss_fftndr_alloc(const int *dims,int ndims,int inverse_fft,void*mem,size_t*lenmem);
|
||||
/*
|
||||
dims[0] must be even
|
||||
|
||||
If you don't care to allocate space, use mem = lenmem = NULL
|
||||
*/
|
||||
|
||||
|
||||
void KISS_FFT_API kiss_fftndr(
|
||||
kiss_fftndr_cfg cfg,
|
||||
const kiss_fft_scalar *timedata,
|
||||
kiss_fft_cpx *freqdata);
|
||||
/*
|
||||
input timedata has dims[0] X dims[1] X ... X dims[ndims-1] scalar points
|
||||
output freqdata has dims[0] X dims[1] X ... X dims[ndims-1]/2+1 complex points
|
||||
*/
|
||||
|
||||
void KISS_FFT_API kiss_fftndri(
|
||||
kiss_fftndr_cfg cfg,
|
||||
const kiss_fft_cpx *freqdata,
|
||||
kiss_fft_scalar *timedata);
|
||||
/*
|
||||
input and output dimensions are the exact opposite of kiss_fftndr
|
||||
*/
|
||||
|
||||
|
||||
#define kiss_fftndr_free free
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
155
Source/Cut5/FFT/kiss_fftr.c
Normal file
155
Source/Cut5/FFT/kiss_fftr.c
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#include "kiss_fftr.h"
|
||||
#include "_kiss_fft_guts.h"
|
||||
|
||||
struct kiss_fftr_state{
|
||||
kiss_fft_cfg substate;
|
||||
kiss_fft_cpx * tmpbuf;
|
||||
kiss_fft_cpx * super_twiddles;
|
||||
#ifdef USE_SIMD
|
||||
void * pad;
|
||||
#endif
|
||||
};
|
||||
|
||||
kiss_fftr_cfg kiss_fftr_alloc(int nfft,int inverse_fft,void * mem,size_t * lenmem)
|
||||
{
|
||||
KISS_FFT_ALIGN_CHECK(mem)
|
||||
|
||||
int i;
|
||||
kiss_fftr_cfg st = NULL;
|
||||
size_t subsize = 0, memneeded;
|
||||
|
||||
if (nfft & 1) {
|
||||
KISS_FFT_ERROR("Real FFT optimization must be even.");
|
||||
return NULL;
|
||||
}
|
||||
nfft >>= 1;
|
||||
|
||||
kiss_fft_alloc (nfft, inverse_fft, NULL, &subsize);
|
||||
memneeded = sizeof(struct kiss_fftr_state) + subsize + sizeof(kiss_fft_cpx) * ( nfft * 3 / 2);
|
||||
|
||||
if (lenmem == NULL) {
|
||||
st = (kiss_fftr_cfg) KISS_FFT_MALLOC (memneeded);
|
||||
} else {
|
||||
if (*lenmem >= memneeded)
|
||||
st = (kiss_fftr_cfg) mem;
|
||||
*lenmem = memneeded;
|
||||
}
|
||||
if (!st)
|
||||
return NULL;
|
||||
|
||||
st->substate = (kiss_fft_cfg) (st + 1); /*just beyond kiss_fftr_state struct */
|
||||
st->tmpbuf = (kiss_fft_cpx *) (((char *) st->substate) + subsize);
|
||||
st->super_twiddles = st->tmpbuf + nfft;
|
||||
kiss_fft_alloc(nfft, inverse_fft, st->substate, &subsize);
|
||||
|
||||
for (i = 0; i < nfft/2; ++i) {
|
||||
double phase =
|
||||
-3.14159265358979323846264338327 * ((double) (i+1) / nfft + .5);
|
||||
if (inverse_fft)
|
||||
phase *= -1;
|
||||
kf_cexp (st->super_twiddles+i,phase);
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
void kiss_fftr(kiss_fftr_cfg st,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata)
|
||||
{
|
||||
/* input buffer timedata is stored row-wise */
|
||||
int k,ncfft;
|
||||
kiss_fft_cpx fpnk,fpk,f1k,f2k,tw,tdc;
|
||||
|
||||
if ( st->substate->inverse) {
|
||||
KISS_FFT_ERROR("kiss fft usage error: improper alloc");
|
||||
return;/* The caller did not call the correct function */
|
||||
}
|
||||
|
||||
ncfft = st->substate->nfft;
|
||||
|
||||
/*perform the parallel fft of two real signals packed in real,imag*/
|
||||
kiss_fft( st->substate , (const kiss_fft_cpx*)timedata, st->tmpbuf );
|
||||
/* The real part of the DC element of the frequency spectrum in st->tmpbuf
|
||||
* contains the sum of the even-numbered elements of the input time sequence
|
||||
* The imag part is the sum of the odd-numbered elements
|
||||
*
|
||||
* The sum of tdc.r and tdc.i is the sum of the input time sequence.
|
||||
* yielding DC of input time sequence
|
||||
* The difference of tdc.r - tdc.i is the sum of the input (dot product) [1,-1,1,-1...
|
||||
* yielding Nyquist bin of input time sequence
|
||||
*/
|
||||
|
||||
tdc.r = st->tmpbuf[0].r;
|
||||
tdc.i = st->tmpbuf[0].i;
|
||||
C_FIXDIV(tdc,2);
|
||||
CHECK_OVERFLOW_OP(tdc.r ,+, tdc.i);
|
||||
CHECK_OVERFLOW_OP(tdc.r ,-, tdc.i);
|
||||
freqdata[0].r = tdc.r + tdc.i;
|
||||
freqdata[ncfft].r = tdc.r - tdc.i;
|
||||
#ifdef USE_SIMD
|
||||
freqdata[ncfft].i = freqdata[0].i = _mm_set1_ps(0);
|
||||
#else
|
||||
freqdata[ncfft].i = freqdata[0].i = 0;
|
||||
#endif
|
||||
|
||||
for ( k=1;k <= ncfft/2 ; ++k ) {
|
||||
fpk = st->tmpbuf[k];
|
||||
fpnk.r = st->tmpbuf[ncfft-k].r;
|
||||
fpnk.i = - st->tmpbuf[ncfft-k].i;
|
||||
C_FIXDIV(fpk,2);
|
||||
C_FIXDIV(fpnk,2);
|
||||
|
||||
C_ADD( f1k, fpk , fpnk );
|
||||
C_SUB( f2k, fpk , fpnk );
|
||||
C_MUL( tw , f2k , st->super_twiddles[k-1]);
|
||||
|
||||
freqdata[k].r = HALF_OF(f1k.r + tw.r);
|
||||
freqdata[k].i = HALF_OF(f1k.i + tw.i);
|
||||
freqdata[ncfft-k].r = HALF_OF(f1k.r - tw.r);
|
||||
freqdata[ncfft-k].i = HALF_OF(tw.i - f1k.i);
|
||||
}
|
||||
}
|
||||
|
||||
void kiss_fftri(kiss_fftr_cfg st,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata)
|
||||
{
|
||||
/* input buffer timedata is stored row-wise */
|
||||
int k, ncfft;
|
||||
|
||||
if (st->substate->inverse == 0) {
|
||||
KISS_FFT_ERROR("kiss fft usage error: improper alloc");
|
||||
return;/* The caller did not call the correct function */
|
||||
}
|
||||
|
||||
ncfft = st->substate->nfft;
|
||||
|
||||
st->tmpbuf[0].r = freqdata[0].r + freqdata[ncfft].r;
|
||||
st->tmpbuf[0].i = freqdata[0].r - freqdata[ncfft].r;
|
||||
C_FIXDIV(st->tmpbuf[0],2);
|
||||
|
||||
for (k = 1; k <= ncfft / 2; ++k) {
|
||||
kiss_fft_cpx fk, fnkc, fek, fok, tmp;
|
||||
fk = freqdata[k];
|
||||
fnkc.r = freqdata[ncfft - k].r;
|
||||
fnkc.i = -freqdata[ncfft - k].i;
|
||||
C_FIXDIV( fk , 2 );
|
||||
C_FIXDIV( fnkc , 2 );
|
||||
|
||||
C_ADD (fek, fk, fnkc);
|
||||
C_SUB (tmp, fk, fnkc);
|
||||
C_MUL (fok, tmp, st->super_twiddles[k-1]);
|
||||
C_ADD (st->tmpbuf[k], fek, fok);
|
||||
C_SUB (st->tmpbuf[ncfft - k], fek, fok);
|
||||
#ifdef USE_SIMD
|
||||
st->tmpbuf[ncfft - k].i *= _mm_set1_ps(-1.0);
|
||||
#else
|
||||
st->tmpbuf[ncfft - k].i *= -1;
|
||||
#endif
|
||||
}
|
||||
kiss_fft (st->substate, st->tmpbuf, (kiss_fft_cpx *) timedata);
|
||||
}
|
54
Source/Cut5/FFT/kiss_fftr.h
Normal file
54
Source/Cut5/FFT/kiss_fftr.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2004, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef KISS_FTR_H
|
||||
#define KISS_FTR_H
|
||||
|
||||
#include "kiss_fft.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
|
||||
Real optimized version can save about 45% cpu time vs. complex fft of a real seq.
|
||||
|
||||
|
||||
|
||||
*/
|
||||
|
||||
typedef struct kiss_fftr_state *kiss_fftr_cfg;
|
||||
|
||||
|
||||
kiss_fftr_cfg KISS_FFT_API kiss_fftr_alloc(int nfft,int inverse_fft,void * mem, size_t * lenmem);
|
||||
/*
|
||||
nfft must be even
|
||||
|
||||
If you don't care to allocate space, use mem = lenmem = NULL
|
||||
*/
|
||||
|
||||
|
||||
void KISS_FFT_API kiss_fftr(kiss_fftr_cfg cfg,const kiss_fft_scalar *timedata,kiss_fft_cpx *freqdata);
|
||||
/*
|
||||
input timedata has nfft scalar points
|
||||
output freqdata has nfft/2+1 complex points
|
||||
*/
|
||||
|
||||
void KISS_FFT_API kiss_fftri(kiss_fftr_cfg cfg,const kiss_fft_cpx *freqdata,kiss_fft_scalar *timedata);
|
||||
/*
|
||||
input freqdata has nfft/2+1 complex points
|
||||
output timedata has nfft scalar points
|
||||
*/
|
||||
|
||||
#define kiss_fftr_free KISS_FFT_FREE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
361
Source/Cut5/FFT/kissfft.hh
Normal file
361
Source/Cut5/FFT/kissfft.hh
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* Copyright (c) 2003-2010, Mark Borgerding. All rights reserved.
|
||||
* This file is part of KISS FFT - https://github.com/mborgerding/kissfft
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
* See COPYING file for more information.
|
||||
*/
|
||||
|
||||
#ifndef KISSFFT_CLASS_HH
|
||||
#define KISSFFT_CLASS_HH
|
||||
#include <complex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
|
||||
template <typename scalar_t>
|
||||
class kissfft
|
||||
{
|
||||
public:
|
||||
|
||||
typedef std::complex<scalar_t> cpx_t;
|
||||
|
||||
kissfft( const std::size_t nfft,
|
||||
const bool inverse )
|
||||
:_nfft(nfft)
|
||||
,_inverse(inverse)
|
||||
{
|
||||
// fill twiddle factors
|
||||
_twiddles.resize(_nfft);
|
||||
const scalar_t phinc = (_inverse?2:-2)* std::acos( (scalar_t) -1) / _nfft;
|
||||
for (std::size_t i=0;i<_nfft;++i)
|
||||
_twiddles[i] = std::exp( cpx_t(0,i*phinc) );
|
||||
|
||||
//factorize
|
||||
//start factoring out 4's, then 2's, then 3,5,7,9,...
|
||||
std::size_t n= _nfft;
|
||||
std::size_t p=4;
|
||||
do {
|
||||
while (n % p) {
|
||||
switch (p) {
|
||||
case 4: p = 2; break;
|
||||
case 2: p = 3; break;
|
||||
default: p += 2; break;
|
||||
}
|
||||
if (p*p>n)
|
||||
p = n;// no more factors
|
||||
}
|
||||
n /= p;
|
||||
_stageRadix.push_back(p);
|
||||
_stageRemainder.push_back(n);
|
||||
}while(n>1);
|
||||
}
|
||||
|
||||
|
||||
/// Changes the FFT-length and/or the transform direction.
|
||||
///
|
||||
/// @post The @c kissfft object will be in the same state as if it
|
||||
/// had been newly constructed with the passed arguments.
|
||||
/// However, the implementation may be faster than constructing a
|
||||
/// new fft object.
|
||||
void assign( const std::size_t nfft,
|
||||
const bool inverse )
|
||||
{
|
||||
if ( nfft != _nfft )
|
||||
{
|
||||
kissfft tmp( nfft, inverse ); // O(n) time.
|
||||
std::swap( tmp, *this ); // this is O(1) in C++11, O(n) otherwise.
|
||||
}
|
||||
else if ( inverse != _inverse )
|
||||
{
|
||||
// conjugate the twiddle factors.
|
||||
for ( typename std::vector<cpx_t>::iterator it = _twiddles.begin();
|
||||
it != _twiddles.end(); ++it )
|
||||
it->imag( -it->imag() );
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the complex Discrete Fourier Transform.
|
||||
///
|
||||
/// The size of the passed arrays must be passed in the constructor.
|
||||
/// The sum of the squares of the absolute values in the @c dst
|
||||
/// array will be @c N times the sum of the squares of the absolute
|
||||
/// values in the @c src array, where @c N is the size of the array.
|
||||
/// In other words, the l_2 norm of the resulting array will be
|
||||
/// @c sqrt(N) times as big as the l_2 norm of the input array.
|
||||
/// This is also the case when the inverse flag is set in the
|
||||
/// constructor. Hence when applying the same transform twice, but with
|
||||
/// the inverse flag changed the second time, then the result will
|
||||
/// be equal to the original input times @c N.
|
||||
void transform(const cpx_t * fft_in, cpx_t * fft_out, const std::size_t stage = 0, const std::size_t fstride = 1, const std::size_t in_stride = 1) const
|
||||
{
|
||||
const std::size_t p = _stageRadix[stage];
|
||||
const std::size_t m = _stageRemainder[stage];
|
||||
cpx_t * const Fout_beg = fft_out;
|
||||
cpx_t * const Fout_end = fft_out + p*m;
|
||||
|
||||
if (m==1) {
|
||||
do{
|
||||
*fft_out = *fft_in;
|
||||
fft_in += fstride*in_stride;
|
||||
}while(++fft_out != Fout_end );
|
||||
}else{
|
||||
do{
|
||||
// recursive call:
|
||||
// DFT of size m*p performed by doing
|
||||
// p instances of smaller DFTs of size m,
|
||||
// each one takes a decimated version of the input
|
||||
transform(fft_in, fft_out, stage+1, fstride*p,in_stride);
|
||||
fft_in += fstride*in_stride;
|
||||
}while( (fft_out += m) != Fout_end );
|
||||
}
|
||||
|
||||
fft_out=Fout_beg;
|
||||
|
||||
// recombine the p smaller DFTs
|
||||
switch (p) {
|
||||
case 2: kf_bfly2(fft_out,fstride,m); break;
|
||||
case 3: kf_bfly3(fft_out,fstride,m); break;
|
||||
case 4: kf_bfly4(fft_out,fstride,m); break;
|
||||
case 5: kf_bfly5(fft_out,fstride,m); break;
|
||||
default: kf_bfly_generic(fft_out,fstride,m,p); break;
|
||||
}
|
||||
}
|
||||
|
||||
/// Calculates the Discrete Fourier Transform (DFT) of a real input
|
||||
/// of size @c 2*N.
|
||||
///
|
||||
/// The 0-th and N-th value of the DFT are real numbers. These are
|
||||
/// stored in @c dst[0].real() and @c dst[0].imag() respectively.
|
||||
/// The remaining DFT values up to the index N-1 are stored in
|
||||
/// @c dst[1] to @c dst[N-1].
|
||||
/// The other half of the DFT values can be calculated from the
|
||||
/// symmetry relation
|
||||
/// @code
|
||||
/// DFT(src)[2*N-k] == conj( DFT(src)[k] );
|
||||
/// @endcode
|
||||
/// The same scaling factors as in @c transform() apply.
|
||||
///
|
||||
/// @note For this to work, the types @c scalar_t and @c cpx_t
|
||||
/// must fulfill the following requirements:
|
||||
///
|
||||
/// For any object @c z of type @c cpx_t,
|
||||
/// @c reinterpret_cast<scalar_t(&)[2]>(z)[0] is the real part of @c z and
|
||||
/// @c reinterpret_cast<scalar_t(&)[2]>(z)[1] is the imaginary part of @c z.
|
||||
/// For any pointer to an element of an array of @c cpx_t named @c p
|
||||
/// and any valid array index @c i, @c reinterpret_cast<T*>(p)[2*i]
|
||||
/// is the real part of the complex number @c p[i], and
|
||||
/// @c reinterpret_cast<T*>(p)[2*i+1] is the imaginary part of the
|
||||
/// complex number @c p[i].
|
||||
///
|
||||
/// Since C++11, these requirements are guaranteed to be satisfied for
|
||||
/// @c scalar_ts being @c float, @c double or @c long @c double
|
||||
/// together with @c cpx_t being @c std::complex<scalar_t>.
|
||||
void transform_real( const scalar_t * const src,
|
||||
cpx_t * const dst ) const
|
||||
{
|
||||
const std::size_t N = _nfft;
|
||||
if ( N == 0 )
|
||||
return;
|
||||
|
||||
// perform complex FFT
|
||||
transform( reinterpret_cast<const cpx_t*>(src), dst );
|
||||
|
||||
// post processing for k = 0 and k = N
|
||||
dst[0] = cpx_t( dst[0].real() + dst[0].imag(),
|
||||
dst[0].real() - dst[0].imag() );
|
||||
|
||||
// post processing for all the other k = 1, 2, ..., N-1
|
||||
const scalar_t pi = std::acos( (scalar_t) -1);
|
||||
const scalar_t half_phi_inc = ( _inverse ? pi : -pi ) / N;
|
||||
const cpx_t twiddle_mul = std::exp( cpx_t(0, half_phi_inc) );
|
||||
for ( std::size_t k = 1; 2*k < N; ++k )
|
||||
{
|
||||
const cpx_t w = (scalar_t)0.5 * cpx_t(
|
||||
dst[k].real() + dst[N-k].real(),
|
||||
dst[k].imag() - dst[N-k].imag() );
|
||||
const cpx_t z = (scalar_t)0.5 * cpx_t(
|
||||
dst[k].imag() + dst[N-k].imag(),
|
||||
-dst[k].real() + dst[N-k].real() );
|
||||
const cpx_t twiddle =
|
||||
k % 2 == 0 ?
|
||||
_twiddles[k/2] :
|
||||
_twiddles[k/2] * twiddle_mul;
|
||||
dst[ k] = w + twiddle * z;
|
||||
dst[N-k] = std::conj( w - twiddle * z );
|
||||
}
|
||||
if ( N % 2 == 0 )
|
||||
dst[N/2] = std::conj( dst[N/2] );
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void kf_bfly2( cpx_t * Fout, const size_t fstride, const std::size_t m) const
|
||||
{
|
||||
for (std::size_t k=0;k<m;++k) {
|
||||
const cpx_t t = Fout[m+k] * _twiddles[k*fstride];
|
||||
Fout[m+k] = Fout[k] - t;
|
||||
Fout[k] += t;
|
||||
}
|
||||
}
|
||||
|
||||
void kf_bfly3( cpx_t * Fout, const std::size_t fstride, const std::size_t m) const
|
||||
{
|
||||
std::size_t k=m;
|
||||
const std::size_t m2 = 2*m;
|
||||
const cpx_t *tw1,*tw2;
|
||||
cpx_t scratch[5];
|
||||
const cpx_t epi3 = _twiddles[fstride*m];
|
||||
|
||||
tw1=tw2=&_twiddles[0];
|
||||
|
||||
do{
|
||||
scratch[1] = Fout[m] * *tw1;
|
||||
scratch[2] = Fout[m2] * *tw2;
|
||||
|
||||
scratch[3] = scratch[1] + scratch[2];
|
||||
scratch[0] = scratch[1] - scratch[2];
|
||||
tw1 += fstride;
|
||||
tw2 += fstride*2;
|
||||
|
||||
Fout[m] = Fout[0] - scratch[3]*scalar_t(0.5);
|
||||
scratch[0] *= epi3.imag();
|
||||
|
||||
Fout[0] += scratch[3];
|
||||
|
||||
Fout[m2] = cpx_t( Fout[m].real() + scratch[0].imag() , Fout[m].imag() - scratch[0].real() );
|
||||
|
||||
Fout[m] += cpx_t( -scratch[0].imag(),scratch[0].real() );
|
||||
++Fout;
|
||||
}while(--k);
|
||||
}
|
||||
|
||||
void kf_bfly4( cpx_t * const Fout, const std::size_t fstride, const std::size_t m) const
|
||||
{
|
||||
cpx_t scratch[7];
|
||||
const scalar_t negative_if_inverse = _inverse ? -1 : +1;
|
||||
for (std::size_t k=0;k<m;++k) {
|
||||
scratch[0] = Fout[k+ m] * _twiddles[k*fstride ];
|
||||
scratch[1] = Fout[k+2*m] * _twiddles[k*fstride*2];
|
||||
scratch[2] = Fout[k+3*m] * _twiddles[k*fstride*3];
|
||||
scratch[5] = Fout[k] - scratch[1];
|
||||
|
||||
Fout[k] += scratch[1];
|
||||
scratch[3] = scratch[0] + scratch[2];
|
||||
scratch[4] = scratch[0] - scratch[2];
|
||||
scratch[4] = cpx_t( scratch[4].imag()*negative_if_inverse ,
|
||||
-scratch[4].real()*negative_if_inverse );
|
||||
|
||||
Fout[k+2*m] = Fout[k] - scratch[3];
|
||||
Fout[k ]+= scratch[3];
|
||||
Fout[k+ m] = scratch[5] + scratch[4];
|
||||
Fout[k+3*m] = scratch[5] - scratch[4];
|
||||
}
|
||||
}
|
||||
|
||||
void kf_bfly5( cpx_t * const Fout, const std::size_t fstride, const std::size_t m) const
|
||||
{
|
||||
cpx_t *Fout0,*Fout1,*Fout2,*Fout3,*Fout4;
|
||||
cpx_t scratch[13];
|
||||
const cpx_t ya = _twiddles[fstride*m];
|
||||
const cpx_t yb = _twiddles[fstride*2*m];
|
||||
|
||||
Fout0=Fout;
|
||||
Fout1=Fout0+m;
|
||||
Fout2=Fout0+2*m;
|
||||
Fout3=Fout0+3*m;
|
||||
Fout4=Fout0+4*m;
|
||||
|
||||
for ( std::size_t u=0; u<m; ++u ) {
|
||||
scratch[0] = *Fout0;
|
||||
|
||||
scratch[1] = *Fout1 * _twiddles[ u*fstride];
|
||||
scratch[2] = *Fout2 * _twiddles[2*u*fstride];
|
||||
scratch[3] = *Fout3 * _twiddles[3*u*fstride];
|
||||
scratch[4] = *Fout4 * _twiddles[4*u*fstride];
|
||||
|
||||
scratch[7] = scratch[1] + scratch[4];
|
||||
scratch[10]= scratch[1] - scratch[4];
|
||||
scratch[8] = scratch[2] + scratch[3];
|
||||
scratch[9] = scratch[2] - scratch[3];
|
||||
|
||||
*Fout0 += scratch[7];
|
||||
*Fout0 += scratch[8];
|
||||
|
||||
scratch[5] = scratch[0] + cpx_t(
|
||||
scratch[7].real()*ya.real() + scratch[8].real()*yb.real(),
|
||||
scratch[7].imag()*ya.real() + scratch[8].imag()*yb.real()
|
||||
);
|
||||
|
||||
scratch[6] = cpx_t(
|
||||
scratch[10].imag()*ya.imag() + scratch[9].imag()*yb.imag(),
|
||||
-scratch[10].real()*ya.imag() - scratch[9].real()*yb.imag()
|
||||
);
|
||||
|
||||
*Fout1 = scratch[5] - scratch[6];
|
||||
*Fout4 = scratch[5] + scratch[6];
|
||||
|
||||
scratch[11] = scratch[0] +
|
||||
cpx_t(
|
||||
scratch[7].real()*yb.real() + scratch[8].real()*ya.real(),
|
||||
scratch[7].imag()*yb.real() + scratch[8].imag()*ya.real()
|
||||
);
|
||||
|
||||
scratch[12] = cpx_t(
|
||||
-scratch[10].imag()*yb.imag() + scratch[9].imag()*ya.imag(),
|
||||
scratch[10].real()*yb.imag() - scratch[9].real()*ya.imag()
|
||||
);
|
||||
|
||||
*Fout2 = scratch[11] + scratch[12];
|
||||
*Fout3 = scratch[11] - scratch[12];
|
||||
|
||||
++Fout0;
|
||||
++Fout1;
|
||||
++Fout2;
|
||||
++Fout3;
|
||||
++Fout4;
|
||||
}
|
||||
}
|
||||
|
||||
/* perform the butterfly for one stage of a mixed radix FFT */
|
||||
void kf_bfly_generic(
|
||||
cpx_t * const Fout,
|
||||
const size_t fstride,
|
||||
const std::size_t m,
|
||||
const std::size_t p
|
||||
) const
|
||||
{
|
||||
const cpx_t * twiddles = &_twiddles[0];
|
||||
|
||||
if(p > _scratchbuf.size()) _scratchbuf.resize(p);
|
||||
|
||||
for ( std::size_t u=0; u<m; ++u ) {
|
||||
std::size_t k = u;
|
||||
for ( std::size_t q1=0 ; q1<p ; ++q1 ) {
|
||||
_scratchbuf[q1] = Fout[ k ];
|
||||
k += m;
|
||||
}
|
||||
|
||||
k=u;
|
||||
for ( std::size_t q1=0 ; q1<p ; ++q1 ) {
|
||||
std::size_t twidx=0;
|
||||
Fout[ k ] = _scratchbuf[0];
|
||||
for ( std::size_t q=1;q<p;++q ) {
|
||||
twidx += fstride * k;
|
||||
if (twidx>=_nfft)
|
||||
twidx-=_nfft;
|
||||
Fout[ k ] += _scratchbuf[q] * twiddles[twidx];
|
||||
}
|
||||
k += m;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::size_t _nfft;
|
||||
bool _inverse;
|
||||
std::vector<cpx_t> _twiddles;
|
||||
std::vector<std::size_t> _stageRadix;
|
||||
std::vector<std::size_t> _stageRemainder;
|
||||
mutable std::vector<cpx_t> _scratchbuf;
|
||||
};
|
||||
#endif
|
304
Source/Cut5/FFT/kissfft_i32.hh
Normal file
304
Source/Cut5/FFT/kissfft_i32.hh
Normal file
@ -0,0 +1,304 @@
|
||||
#ifndef KISSFFT_I32_CLASS_HH
|
||||
#define KISSFFT_I32_CLASS_HH
|
||||
|
||||
#include <complex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
// TODO1: substitute complex<type> (behaviour not defined for nonfloats), should be faster
|
||||
// TODO2: use std:: namespace
|
||||
// TODO3: make unittests for all ffts (c, cpp, i32)
|
||||
|
||||
template <typename DType>
|
||||
struct complex_s
|
||||
{
|
||||
DType real;
|
||||
DType imag;
|
||||
};
|
||||
|
||||
class kissfft_i32
|
||||
{
|
||||
private:
|
||||
|
||||
using scalar_type = int32_t;
|
||||
using cpx_type = complex<int32_t>;
|
||||
|
||||
scalar_type _scale_factor;
|
||||
std::size_t _nfft;
|
||||
bool _inverse;
|
||||
std::vector<cpx_type> _twiddles;
|
||||
std::vector<std::size_t> _stageRadix;
|
||||
std::vector<std::size_t> _stageRemainder;
|
||||
|
||||
public:
|
||||
|
||||
// scale_factor: upscale twiddle-factors otherwise they lie between 0..1 (out of range for integer) --> fixed point math
|
||||
kissfft_i32(const std::size_t nfft, const bool inverse, const double scale_factor = 1024.0)
|
||||
: _scale_factor(scalar_type(scale_factor)), _nfft(nfft), _inverse(inverse)
|
||||
{
|
||||
// fill twiddle factors
|
||||
_twiddles.resize(_nfft);
|
||||
const double phinc = (_inverse ? 2 : -2) * acos(-1.0) / _nfft;
|
||||
for (std::size_t i = 0; i < _nfft; ++i)
|
||||
{
|
||||
_twiddles[i] = scale_factor * exp(complex<double>(0, i * phinc));
|
||||
}
|
||||
//factorize
|
||||
//start factoring out 4's, then 2's, then 3,5,7,9,...
|
||||
std::size_t n = _nfft;
|
||||
std::size_t p = 4;
|
||||
do
|
||||
{
|
||||
while (n % p)
|
||||
{
|
||||
switch (p)
|
||||
{
|
||||
case 4:
|
||||
p = 2;
|
||||
break;
|
||||
case 2:
|
||||
p = 3;
|
||||
break;
|
||||
default:
|
||||
p += 2;
|
||||
break;
|
||||
}
|
||||
if (p * p > n) p = n;// no more factors
|
||||
}
|
||||
n /= p;
|
||||
_stageRadix.push_back(p);
|
||||
_stageRemainder.push_back(n);
|
||||
} while (n > 1);
|
||||
}
|
||||
|
||||
/// Calculates the complex Discrete Fourier Transform.
|
||||
///
|
||||
/// The size of the passed arrays must be passed in the constructor.
|
||||
/// The sum of the squares of the absolute values in the @c dst
|
||||
/// array will be @c N times the sum of the squares of the absolute
|
||||
/// values in the @c src array, where @c N is the size of the array.
|
||||
/// In other words, the l_2 norm of the resulting array will be
|
||||
/// @c sqrt(N) times as big as the l_2 norm of the input array.
|
||||
/// This is also the case when the inverse flag is set in the
|
||||
/// constructor. Hence when applying the same transform twice, but with
|
||||
/// the inverse flag changed the second time, then the result will
|
||||
/// be equal to the original input times @c N.
|
||||
void transform(const cpx_type * FSrc,
|
||||
cpx_type * FDst,
|
||||
const std::size_t stage = 0,
|
||||
const std::size_t fstride = 1,
|
||||
const std::size_t in_stride = 1) const
|
||||
{
|
||||
const std::size_t p = _stageRadix[stage];
|
||||
const std::size_t m = _stageRemainder[stage];
|
||||
cpx_type *const Fout_beg = FDst;
|
||||
cpx_type *const Fout_end = FDst + p * m;
|
||||
|
||||
if (m == 1)
|
||||
{
|
||||
do
|
||||
{
|
||||
*FDst = *FSrc;
|
||||
FSrc += fstride * in_stride;
|
||||
} while (++FDst != Fout_end);
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
// recursive call:
|
||||
// DFT of size m*p performed by doing
|
||||
// p instances of smaller DFTs of size m,
|
||||
// each one takes a decimated version of the input
|
||||
transform(FSrc, FDst, stage + 1, fstride * p, in_stride);
|
||||
FSrc += fstride * in_stride;
|
||||
} while ((FDst += m) != Fout_end);
|
||||
}
|
||||
|
||||
FDst = Fout_beg;
|
||||
|
||||
// recombine the p smaller DFTs
|
||||
switch (p)
|
||||
{
|
||||
case 2:
|
||||
kf_bfly2(FDst, fstride, m);
|
||||
break;
|
||||
case 3:
|
||||
kf_bfly3(FDst, fstride, m);
|
||||
break;
|
||||
case 4:
|
||||
kf_bfly4(FDst, fstride, m);
|
||||
break;
|
||||
case 5:
|
||||
kf_bfly5(FDst, fstride, m);
|
||||
break;
|
||||
default:
|
||||
kf_bfly_generic(FDst, fstride, m, p);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void kf_bfly2(cpx_type *const Fout, const size_t fstride, const std::size_t m) const
|
||||
{
|
||||
for (std::size_t k = 0; k < m; ++k)
|
||||
{
|
||||
const cpx_type t = (Fout[m + k] * _twiddles[k * fstride]) / _scale_factor;
|
||||
Fout[m + k] = Fout[k] - t;
|
||||
Fout[k] += t;
|
||||
}
|
||||
}
|
||||
|
||||
void kf_bfly3(cpx_type *Fout, const std::size_t fstride, const std::size_t m) const
|
||||
{
|
||||
std::size_t k = m;
|
||||
const std::size_t m2 = 2 * m;
|
||||
const cpx_type *tw1, *tw2;
|
||||
cpx_type scratch[5];
|
||||
const cpx_type epi3 = _twiddles[fstride * m];
|
||||
|
||||
tw1 = tw2 = &_twiddles[0];
|
||||
|
||||
do
|
||||
{
|
||||
scratch[1] = (Fout[m] * *tw1) / _scale_factor;
|
||||
scratch[2] = (Fout[m2] * *tw2) / _scale_factor;
|
||||
|
||||
scratch[3] = scratch[1] + scratch[2];
|
||||
scratch[0] = scratch[1] - scratch[2];
|
||||
tw1 += fstride;
|
||||
tw2 += fstride * 2;
|
||||
|
||||
Fout[m] = Fout[0] - (scratch[3] / 2);
|
||||
scratch[0] *= epi3.imag();
|
||||
scratch[0] /= _scale_factor;
|
||||
|
||||
Fout[0] += scratch[3];
|
||||
|
||||
Fout[m2] = cpx_type(Fout[m].real() + scratch[0].imag(), Fout[m].imag() - scratch[0].real());
|
||||
|
||||
Fout[m] += cpx_type(-scratch[0].imag(), scratch[0].real());
|
||||
++Fout;
|
||||
} while (--k);
|
||||
}
|
||||
|
||||
void kf_bfly4(cpx_type *const Fout, const std::size_t fstride, const std::size_t m) const
|
||||
{
|
||||
cpx_type scratch[7];
|
||||
const scalar_type negative_if_inverse = _inverse ? -1 : +1;
|
||||
|
||||
for (std::size_t k = 0; k < m; ++k)
|
||||
{
|
||||
scratch[0] = (Fout[k + m] * _twiddles[k * fstride]) / _scale_factor;
|
||||
scratch[1] = (Fout[k + 2 * m] * _twiddles[k * fstride * 2]) / _scale_factor;
|
||||
scratch[2] = (Fout[k + 3 * m] * _twiddles[k * fstride * 3]) / _scale_factor;
|
||||
scratch[5] = Fout[k] - scratch[1];
|
||||
|
||||
Fout[k] += scratch[1];
|
||||
scratch[3] = scratch[0] + scratch[2];
|
||||
scratch[4] = scratch[0] - scratch[2];
|
||||
scratch[4] = cpx_type(scratch[4].imag() * negative_if_inverse,
|
||||
-scratch[4].real() * negative_if_inverse);
|
||||
|
||||
Fout[k + 2 * m] = Fout[k] - scratch[3];
|
||||
Fout[k] += scratch[3];
|
||||
Fout[k + m] = scratch[5] + scratch[4];
|
||||
Fout[k + 3 * m] = scratch[5] - scratch[4];
|
||||
}
|
||||
}
|
||||
|
||||
void kf_bfly5(cpx_type *const Fout, const std::size_t fstride, const std::size_t m) const
|
||||
{
|
||||
cpx_type *Fout0, *Fout1, *Fout2, *Fout3, *Fout4;
|
||||
cpx_type scratch[13];
|
||||
const cpx_type ya = _twiddles[fstride * m];
|
||||
const cpx_type yb = _twiddles[fstride * 2 * m];
|
||||
|
||||
Fout0 = Fout;
|
||||
Fout1 = Fout0 + m;
|
||||
Fout2 = Fout0 + 2 * m;
|
||||
Fout3 = Fout0 + 3 * m;
|
||||
Fout4 = Fout0 + 4 * m;
|
||||
|
||||
for (std::size_t u = 0; u < m; ++u)
|
||||
{
|
||||
scratch[0] = *Fout0;
|
||||
|
||||
scratch[1] = (*Fout1 * _twiddles[u * fstride]) / _scale_factor;
|
||||
scratch[2] = (*Fout2 * _twiddles[2 * u * fstride]) / _scale_factor;
|
||||
scratch[3] = (*Fout3 * _twiddles[3 * u * fstride]) / _scale_factor;
|
||||
scratch[4] = (*Fout4 * _twiddles[4 * u * fstride]) / _scale_factor;
|
||||
|
||||
scratch[7] = scratch[1] + scratch[4];
|
||||
scratch[10] = scratch[1] - scratch[4];
|
||||
scratch[8] = scratch[2] + scratch[3];
|
||||
scratch[9] = scratch[2] - scratch[3];
|
||||
|
||||
*Fout0 += scratch[7];
|
||||
*Fout0 += scratch[8];
|
||||
|
||||
scratch[5] = scratch[0] + (cpx_type(
|
||||
scratch[7].real() * ya.real() + scratch[8].real() * yb.real(),
|
||||
scratch[7].imag() * ya.real() + scratch[8].imag() * yb.real() ) / _scale_factor);
|
||||
|
||||
scratch[6] = cpx_type(
|
||||
scratch[10].imag() * ya.imag() + scratch[9].imag() * yb.imag(),
|
||||
-scratch[10].real() * ya.imag() - scratch[9].real() * yb.imag() ) / _scale_factor;
|
||||
|
||||
*Fout1 = scratch[5] - scratch[6];
|
||||
*Fout4 = scratch[5] + scratch[6];
|
||||
|
||||
scratch[11] = scratch[0] + (cpx_type(
|
||||
scratch[7].real() * yb.real() + scratch[8].real() * ya.real(),
|
||||
scratch[7].imag() * yb.real() + scratch[8].imag() * ya.real() ) / _scale_factor);
|
||||
|
||||
scratch[12] = cpx_type(
|
||||
-scratch[10].imag() * yb.imag() + scratch[9].imag() * ya.imag(),
|
||||
scratch[10].real() * yb.imag() - scratch[9].real() * ya.imag() ) / _scale_factor;
|
||||
|
||||
*Fout2 = scratch[11] + scratch[12];
|
||||
*Fout3 = scratch[11] - scratch[12];
|
||||
|
||||
++Fout0;
|
||||
++Fout1;
|
||||
++Fout2;
|
||||
++Fout3;
|
||||
++Fout4;
|
||||
}
|
||||
}
|
||||
|
||||
/* perform the butterfly for one stage of a mixed radix FFT */
|
||||
void kf_bfly_generic(cpx_type * const Fout, const size_t fstride, const std::size_t m, const std::size_t p) const
|
||||
{
|
||||
const cpx_type *twiddles = &_twiddles[0];
|
||||
cpx_type scratchbuf[p];
|
||||
|
||||
for (std::size_t u = 0; u < m; ++u)
|
||||
{
|
||||
std::size_t k = u;
|
||||
for (std::size_t q1 = 0; q1 < p; ++q1)
|
||||
{
|
||||
scratchbuf[q1] = Fout[k];
|
||||
k += m;
|
||||
}
|
||||
|
||||
k = u;
|
||||
for (std::size_t q1 = 0; q1 < p; ++q1)
|
||||
{
|
||||
std::size_t twidx = 0;
|
||||
Fout[k] = scratchbuf[0];
|
||||
for (std::size_t q = 1; q < p; ++q)
|
||||
{
|
||||
twidx += fstride * k;
|
||||
if (twidx >= _nfft)
|
||||
twidx -= _nfft;
|
||||
Fout[k] += (scratchbuf[q] * twiddles[twidx]) / _scale_factor;
|
||||
}
|
||||
k += m;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
@ -3,6 +3,7 @@
|
||||
#include "CanvasTypes.h"
|
||||
#include "ImageUtils.h"
|
||||
#include "Utils.h"
|
||||
#include "Cut5/FFT/kiss_fft.h"
|
||||
#include "Engine/Canvas.h"
|
||||
#include "Engine/TextureRenderTarget2D.h"
|
||||
#include "Kismet/KismetRenderingLibrary.h"
|
||||
@ -326,11 +327,50 @@ TArray<FSlateBrush> FFFMPEGUtils::GetAudioBrush(FClipData* ClipData)
|
||||
{
|
||||
const float TimeLength = (ClipData->ClipEndFrame - ClipData->ClipStartFrame) * FGlobalData::DefaultTimeTickSpace;
|
||||
|
||||
float MaxValue = 0;
|
||||
float MinValue = 0;
|
||||
const int32 PicLength = TimeLength / 8.0;
|
||||
|
||||
|
||||
int32 DownSampleSpace = 128;
|
||||
TArray<float> DownSampledData;
|
||||
DownSampledData.SetNumZeroed(ClipData->ResourcePropertyDataPtr->AudioData.Num() / 4 / DownSampleSpace);
|
||||
for (int32 i = 0; i < ClipData->ResourcePropertyDataPtr->AudioData.Num() / 4; i++)
|
||||
{
|
||||
float NewFloat = *reinterpret_cast<float*>(ClipData->ResourcePropertyDataPtr->AudioData.GetData() + (i * 4));
|
||||
if (i % DownSampleSpace == 0)
|
||||
{
|
||||
DownSampledData[i / DownSampleSpace] = NewFloat;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const float FFTSize = DownSampledData.Num();
|
||||
kiss_fft_cfg Cfg = kiss_fft_alloc(FFTSize, 0, nullptr, nullptr);
|
||||
|
||||
TArray<kiss_fft_cpx> KissIn, KissOut;
|
||||
KissIn.SetNumZeroed(FFTSize);
|
||||
KissOut.SetNumZeroed(FFTSize);
|
||||
|
||||
for (int32 i = 0; i < DownSampledData.Num(); i++)
|
||||
{
|
||||
float NewFloat = DownSampledData[i];
|
||||
KissIn[i].r = NewFloat;
|
||||
KissIn[i].i = 0;
|
||||
}
|
||||
|
||||
kiss_fft(Cfg, KissIn.GetData(), KissOut.GetData());
|
||||
|
||||
TArray<float> Spectrum;
|
||||
Spectrum.SetNumZeroed(FFTSize);
|
||||
for (int32 i = 0; i < DownSampledData.Num(); i++)
|
||||
{
|
||||
Spectrum[i] = sqrt(KissOut[i].r * KissOut[i].r + KissOut[i].i * KissOut[i].i);
|
||||
}
|
||||
|
||||
float MaxValue = 0;
|
||||
float MinValue = 0;
|
||||
for (int32 i = 0; i < Spectrum.Num(); i++)
|
||||
{
|
||||
float NewFloat = ClipData->ResourcePropertyDataPtr->AudioData[i];
|
||||
|
||||
if (NewFloat >= MaxValue)
|
||||
{
|
||||
@ -352,21 +392,12 @@ TArray<FSlateBrush> FFFMPEGUtils::GetAudioBrush(FClipData* ClipData)
|
||||
ClipData->AudioBrushes.Empty();
|
||||
int32 Index = 0;
|
||||
int32 TotalLength = TimeLength;
|
||||
while (TotalLength > 0)
|
||||
float LastPoint = 0.0;
|
||||
for (int32 i = 0; i < TotalLength / PicLength; i++)
|
||||
{
|
||||
int32 CurrentLength = 0;
|
||||
if (TotalLength > 4096)
|
||||
{
|
||||
TotalLength -= 4096;
|
||||
CurrentLength = 4096;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentLength = TotalLength;
|
||||
TotalLength = 0;
|
||||
}
|
||||
FLinearColor RenderColor(i * (360.0 / 8.0), 1.0, 1.0);
|
||||
UTextureRenderTarget2D* TextureRenderTarget2D = NewObject<UTextureRenderTarget2D>();
|
||||
TextureRenderTarget2D->InitCustomFormat(int32(CurrentLength), int32(FGlobalData::DefaultTrackHeight), PF_B8G8R8A8, false);
|
||||
TextureRenderTarget2D->InitCustomFormat(PicLength, int32(FGlobalData::DefaultTrackHeight), PF_B8G8R8A8, false);
|
||||
TextureRenderTarget2D->UpdateResourceImmediate();
|
||||
UKismetRenderingLibrary::ClearRenderTarget2D(GWorld->GetWorld(), TextureRenderTarget2D, FLinearColor(0, 0, 0, 0));
|
||||
UCanvas* Canvas;
|
||||
@ -374,19 +405,16 @@ TArray<FSlateBrush> FFFMPEGUtils::GetAudioBrush(FClipData* ClipData)
|
||||
FDrawToRenderTargetContext RenderTargetContext;
|
||||
UKismetRenderingLibrary::BeginDrawCanvasToRenderTarget(GWorld->GetWorld(), TextureRenderTarget2D, Canvas, Size, RenderTargetContext);
|
||||
|
||||
int32 StartIndex = (Index * PicLength) / 16;
|
||||
|
||||
float LastPoint = 0.0;
|
||||
int32 StartIndex = (Index * 4096 + CurrentLength) * 4;
|
||||
|
||||
for (int32 i = 0; i < CurrentLength; i++)
|
||||
for (int32 j = 0; j < PicLength; j++)
|
||||
{
|
||||
float CurrentData = *reinterpret_cast<float*>(ClipData->ResourcePropertyDataPtr->AudioData.GetData() + StartIndex);
|
||||
float CurrentData = Spectrum[StartIndex];
|
||||
float NormalizedData = Normalize(CurrentData);
|
||||
Canvas->K2_DrawLine(FVector2D(i - 1, LastPoint), FVector2D(i, NormalizedData), 1, FLinearColor::White);
|
||||
|
||||
Canvas->K2_DrawLine(FVector2D(j - 1, LastPoint), FVector2D(j, NormalizedData), 1, RenderColor.HSVToLinearRGB());
|
||||
LastPoint = NormalizedData;
|
||||
|
||||
|
||||
StartIndex += 4;
|
||||
StartIndex += 1;
|
||||
}
|
||||
|
||||
|
||||
@ -400,11 +428,11 @@ TArray<FSlateBrush> FFFMPEGUtils::GetAudioBrush(FClipData* ClipData)
|
||||
FImageUtils::ExportRenderTarget2DAsPNG(TextureRenderTarget2D, Buffer);
|
||||
FFileHelper::SaveArrayToFile(Buffer, *FPaths::Combine(FUtils::GetTempPath(), ClipData->ClipGuid.ToString(), FString::FromInt(Index) + ".png"));
|
||||
|
||||
FSlateDynamicImageBrush SlateBrush = FSlateDynamicImageBrush(*FPaths::Combine(FUtils::GetTempPath(), ClipData->ClipGuid.ToString(), FString::FromInt(Index) + ".png"), FVector2D(CurrentLength, FGlobalData::DefaultTrackHeight));
|
||||
ClipData->AudioBrushLength.Add(CurrentLength);
|
||||
FSlateDynamicImageBrush SlateBrush = FSlateDynamicImageBrush(*FPaths::Combine(FUtils::GetTempPath(), ClipData->ClipGuid.ToString(), FString::FromInt(Index) + ".png"), FVector2D(PicLength, FGlobalData::DefaultTrackHeight));
|
||||
ClipData->AudioBrushLength.Add(PicLength);
|
||||
ClipData->AudioBrushes.Add(SlateBrush);
|
||||
Index++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -692,15 +692,19 @@ int32 STimelineClip::OnPaint(const FPaintArgs& Args, const FGeometry& AllottedGe
|
||||
|
||||
const float XLength = AllottedGeometry.GetLocalSize().X;
|
||||
{
|
||||
int32 i = 0;
|
||||
float RenderPos = 0;
|
||||
for (FSlateBrush& SlateBrush : ClipData->AudioBrushes)
|
||||
|
||||
const float TotalLength = ((ClipData->ClipEndFrame - ClipData->ClipStartFrame) * FGlobalData::DefaultTimeTickSpace);
|
||||
if (ClipData->AudioBrushes.Num() > 0)
|
||||
{
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId + 2, AllottedGeometry.ToPaintGeometry(FVector2f(ClipData->AudioBrushLength[i], AllottedGeometry.GetLocalSize().Y), FSlateLayoutTransform(FVector2f(RenderPos, 0))), &SlateBrush);
|
||||
|
||||
RenderPos += ClipData->AudioBrushLength[i];
|
||||
i++;
|
||||
int32 i = 0;
|
||||
const int32 PicLength = TotalLength / ClipData->AudioBrushes.Num();
|
||||
for (FSlateBrush& SlateBrush : ClipData->AudioBrushes)
|
||||
{
|
||||
FSlateDrawElement::MakeBox(OutDrawElements, LayerId + 2, AllottedGeometry.ToPaintGeometry(FVector2f(PicLength, AllottedGeometry.GetLocalSize().Y), FSlateLayoutTransform(FVector2f(i * PicLength, 0))), &SlateBrush);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user