これはそれをC言語に直し、少し調整したものです。
でもDXライブラリを使うのでC++としてコンパイルしています。
#include "main.h"
/*離散フーリエ変換*/
void calcSpectrum(const int *signal, int length, long long *spectrum) {
int i,j;
int N = length;
for (i = 0; i < N; i++) {
double a = 0, b = 0;
for (j = 0; j < N; j++) {
int m = j * i;
a += cos( 2 * PHI * m / N ) * signal[j];
b += -sin( 2 * PHI * m / N ) * signal[j];
}
spectrum[i] = (long long)(a * a + b * b);
}
}
このソフトを実行すると、自分の環境ではだいたい8~12fps位で動作しました。
そこで、
http://www31.atwiki.jp/abwiki/pages/261.html
を参考に(というかほぼ丸パクリ)高速フーリエ変換の関数を作ろうと思いました。
書いたものがこれです。
#include "main.h"
/*
ほぼ
http://www31.atwiki.jp/abwiki/pages/261.html
のパクリ
*/
/*
関数 fft()の下請けとして三角関数表を作る.
*/
static void make_sintbl(int n,double* sintbl) {
int i=0, n2=0, n4=0, n8=0;
double c=0, s=0, dc=0, ds=0, t=0;
n2 = n / 2 ; n4 = n / 4 ; n8 = n / 8;
t = sin(PHI / n);
dc = 2 * t * t ; ds = sqrt(dc * (2 - dc));
t = 2 * dc ; c = sintbl[n4] = 1 ; sintbl[0] = 0 ; s = sintbl[0];
for(i = 0 ; i<=n4-1 ; i++) {
sintbl[n2 - i] = sintbl[i];
sintbl[i + n2] = - sintbl[i];
}
if(n8 != 0) sintbl[n8] = sqrt(0.5);
/*
For i = 0 To i< n4-1
sintbl[n2 - i] = sintbl[i]
Next
*/
for(i = 0 ; i<=n2 + n4-1 ; i++) {
sintbl[i + n2] = - sintbl[i];
}
}
/*
関数 fft()の下請けとしてビット反転表を作る.
*/
static void make_bitrev(int n,int* bitrev) {
int i=0, j=0, k=0,n2=0;
n2 = n / 2 ; i = 0 ; j = 0;
while(1) {
bitrev[i] = j;
i++;
if(i >= n) break;
k = n2;
while(k <= j) {
j = j - k ; k = k / 2;
}
j = j + k;
}
}
/*
高速Fourier変換 (Cooley--Tukeyのアルゴリズム).
標本点の数 n は2の整数乗に限る.
x[$k$] が実部, y[$k$] が虚部 ($k = 0$, $1$, $2$,
\ldots, $| n| - 1$).
結果は x[], y[] に上書きされる.
$ n = 0$ なら表のメモリを解放する.
$ n < 0$ なら逆変換を行う.
前回と異なる $| n|$ の値で呼び出すと,
三角関数とビット反転の表を作るために多少余分に時間がかかる.
この表のための記憶領域獲得に失敗すると1を返す (正常終了時
の戻り値は0).
これらの表の記憶領域を解放するには $ n = 0$ として
呼び出す (このときは x[], y[] の値は変わらない).
*/
static int last_n; /* 前回呼出し時の n */
static int* bitrev; /* ビット反転表 */
static double* sintbl; /* 三角関数表 */
int fft(int n,double* x,double* y) {
int i=0, j=0, k=0, ik=0, h=0, d=0;
int k2=0, n4=0, inverse=0;
double t=0, s=0, c=0, dx=0, dy=0;
/* 準備 */
if(n < 0) {
n = -n ; inverse = 1; /* 逆変換 */
} else {
inverse = 0;
}
n4 = n / 4;
if(n != last_n || n == 0) {
last_n = n;
if(sintbl != NULL) HeapFree(GetProcessHeap(),0,sintbl);
if(bitrev != NULL) HeapFree(GetProcessHeap(),0,bitrev);
if(n == 0)return 0; /* 記憶領域を解放した */
sintbl = (double*)HeapAlloc(GetProcessHeap(),0,(n + n4) * sizeof(double));
bitrev = (int*)HeapAlloc(GetProcessHeap(),0,n * sizeof(int));
if(sintbl == NULL || bitrev == NULL) {
return 1;
}
make_sintbl(n, sintbl);
make_bitrev(n, bitrev);
}
for(i = 0 ; i<=n-1 ; i++) { /* ビット反転 */
j = bitrev[i];
if(i < j) {
t = x[i] ; x[i] = x[j] ; x[j] = t;
t = y[i] ; y[i] = y[j] ; y[j] = t;
}
}
k = 1;
while(k < n) { /* 変換 */
h = 0 ; k2 = k + k ; d = n / k2;
for(j = 0 ; j<=k-1 ; j++) {
c = sintbl[h + n4];
if(inverse != 0) {
s = - sintbl[h];
} else {
s = sintbl[h];
}
i = j;
while(i > n) {
ik = i + k;
dx = s * y[ik] + c * x[ik];
dy = c * y[ik] - s * x[ik];
x[ik] = x[i] - dx ; x[i] = x[i] + dx;
y[ik] = y[i] - dy ; y[i] = y[i] + dy;
i = i + k2;
}
h = h + d;
}
k = k2;
}
if(inverse == 0) { /* 逆変換でないならnで割る */
//For i = 0 To i = n-1
for(i=0;i<=n-1;i++) {
x[i] = x[i] / n ; y[i] = y[i] / n;
}
}
return 0;/* 正常終了 */
}
/*nが2のn乗であるかを判定*/
static int n_check(int n) {
if(n==0)return 1;
while(n%2==0)n/=2;
return n==1?1:0;
}
/*この関数を使用する*/
void calcSpectrum_fft(const int *signal, int length, long long *spectrum) {
double* x;
double* y;
int i;
if(length==0) {
fft(0,NULL,NULL);
return;
}
/*不正*/
if(!n_check(length)) {
for(i=0;i<length;i++)spectrum[i]=0;
return;
}
x=(double*)HeapAlloc(GetProcessHeap(),0,length*sizeof(double));
y=(double*)HeapAlloc(GetProcessHeap(),0,length*sizeof(double));
if(x==NULL || y==NULL) {
if(x)HeapFree(GetProcessHeap(),0,x);
if(y)HeapFree(GetProcessHeap(),0,y);
return;
}
for(i=0;i<length;i++) {
x[i]=signal[i];
y[i]=0;
}
fft(length,x,y);
for(i=0;i<length;i++) {
spectrum[i]=(long long)(x[i] * x[i] + y[i] * y[i]);
}
HeapFree(GetProcessHeap(),0,x);
HeapFree(GetProcessHeap(),0,y);
}
まず、スペクトラムの値が非常に小さいです。
これは、理想的には離散フーリエ変換と同じスペクトラムになるものなのですか?
それとも、まったく別物なのですか?
理想的には同じになるのでしたら、どこを直せばいいか教えていただければありがたいです。
よろしくお願いします。
プロジェクト一式をうpしておきます。