You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
270 lines
7.9 KiB
270 lines
7.9 KiB
#define _USE_MATH_DEFINES
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <windows.h>
|
|
|
|
#define LENGTH 512 /* 信号長 ただし,2の整数乗 */
|
|
|
|
typedef struct {
|
|
double r; /* 実部 */
|
|
double i; /* 虚部 */
|
|
} Complex; /* 複素数 */
|
|
|
|
/* ----------------------------------------------------
|
|
* 複素数演算関数群
|
|
* ---------------------------------------------------- */
|
|
Complex getComplex(double r, double i) {
|
|
return (Complex){r, i};
|
|
}
|
|
|
|
Complex addComplex(Complex a, Complex b) {
|
|
return getComplex(a.r + b.r, a.i + b.i);
|
|
}
|
|
|
|
Complex subComplex(Complex a, Complex b) {
|
|
return getComplex(a.r - b.r, a.i - b.i);
|
|
}
|
|
|
|
Complex timeComplex(Complex a, Complex b) {
|
|
return getComplex(a.r * b.r - a.i * b.i, a.r * b.i + a.i * b.r);
|
|
}
|
|
|
|
Complex divComplexByReal(Complex z, double div) {
|
|
return getComplex(z.r / div, z.i / div);
|
|
}
|
|
|
|
Complex cexptheta(double theta) {
|
|
return getComplex(cos(theta), sin(theta));
|
|
}
|
|
|
|
/* ----------------------------------------------------
|
|
* 信号処理関数群
|
|
* ---------------------------------------------------- */
|
|
|
|
/* 窓関数(Complex型配列専用に統合) */
|
|
void applyWindow(Complex *x, int N) {
|
|
for (int i = 0; i < N; i++) {
|
|
double w = 0.5 - 0.5 * cos(2.0 * M_PI * i / (N - 1)); // ハニング窓
|
|
x[i].r *= w;
|
|
x[i].i *= w;
|
|
}
|
|
}
|
|
|
|
/* 平均パワー計算(Complex型配列専用に統合) */
|
|
double calcAmpPower(Complex *x, int N) {
|
|
double power = 0.0;
|
|
for (int i = 0; i < N; i++) {
|
|
power += (x[i].r * x[i].r + x[i].i * x[i].i);
|
|
}
|
|
return power / N;
|
|
}
|
|
|
|
/* ビット逆順並び替え */
|
|
void bit_reversal(Complex *x, int N) {
|
|
int i, j, k;
|
|
Complex temp;
|
|
j = 0;
|
|
for (i = 0; i < N; i++) {
|
|
if (i < j) {
|
|
temp = x[i];
|
|
x[i] = x[j];
|
|
x[j] = temp;
|
|
}
|
|
k = N >> 1;
|
|
while (k <= j && k > 0) {
|
|
j -= k;
|
|
k >>= 1;
|
|
}
|
|
j += k;
|
|
}
|
|
}
|
|
|
|
/* 高速フーリエ変換 */
|
|
void fft(Complex *x, int N, int inverse) {
|
|
bit_reversal(x, N);
|
|
|
|
for (int stage = 1; (1 << stage) <= N; stage++) {
|
|
int block = 1 << stage;
|
|
int block2 = block >> 1;
|
|
double theta = (inverse ? 2.0 : -2.0) * M_PI / block;
|
|
Complex wm = cexptheta(theta);
|
|
|
|
for (int k = 0; k < N; k += block) {
|
|
Complex w = getComplex(1.0, 0);
|
|
for (int j = 0; j < block2; j++) {
|
|
Complex u = x[k + j];
|
|
Complex v = timeComplex(w, x[k + j + block2]);
|
|
|
|
x[k + j] = addComplex(u, v);
|
|
x[k + j + block2] = subComplex(u, v);
|
|
|
|
w = timeComplex(w, wm);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (inverse) {
|
|
for (int i = 0; i < N; i++) {
|
|
x[i] = divComplexByReal(x[i], N);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 離散フーリエ変換
|
|
* 冗長な回転子テーブル生成・計算関数を省き、
|
|
* 定義通りに計算するようリファクタリング済
|
|
*/
|
|
void dft(Complex *x, Complex *c, int N) {
|
|
for (int k = 0; k < N; k++) {
|
|
c[k] = getComplex(0.0, 0.0);
|
|
for (int n = 0; n < N; n++) {
|
|
double theta = -2.0 * M_PI * k * n / N;
|
|
Complex w = cexptheta(theta);
|
|
c[k] = addComplex(c[k], timeComplex(x[n], w));
|
|
}
|
|
}
|
|
}
|
|
|
|
/* 結果出力用ユーティリティ関数 */
|
|
void saveAmpSpectrum(const char *filename, Complex *x, int N) {
|
|
FILE *fp = fopen(filename, "w");
|
|
if (fp == NULL) {
|
|
fprintf(stderr, "Failed to open output file: %s\n", filename);
|
|
return;
|
|
}
|
|
for (int i = 0; i < N; i++) {
|
|
double amp = sqrt(x[i].r * x[i].r + x[i].i * x[i].i);
|
|
fprintf(fp, "%f\n", amp);
|
|
}
|
|
fclose(fp);
|
|
printf(" -> Saved amplitude spectrum to %s\n", filename);
|
|
}
|
|
|
|
/* ----------------------------------------------------
|
|
* メイン関数
|
|
* ---------------------------------------------------- */
|
|
int main(int argc, char *argv[]) {
|
|
if (argc != 3) {
|
|
fprintf(stderr, "Usage: %s <file1.sw> <file2.sw>\n", argv[0]);
|
|
return 1;
|
|
}
|
|
|
|
int N = LENGTH;
|
|
char *file1 = argv[1];
|
|
char *file2 = argv[2];
|
|
short input[LENGTH];
|
|
Complex x[LENGTH];
|
|
Complex c[LENGTH];
|
|
|
|
printf("========== FFT UNIFIED PROGRAM ==========\n");
|
|
printf("File 1: %s\n", file1);
|
|
printf("File 2: %s\n", file2);
|
|
printf("=========================================\n\n");
|
|
|
|
/* ----------------------------------------------------
|
|
* モード 1 & 2 & 3: 実行時間・パワー比較・FFT結果 (file1を使用)
|
|
* ---------------------------------------------------- */
|
|
printf("[Mode: File1 Analysis (%s)]\n", file1);
|
|
FILE *fp1 = fopen(file1, "rb");
|
|
if (fp1 == NULL) {
|
|
fprintf(stderr, "File open error (%s)\n", file1);
|
|
exit(1);
|
|
}
|
|
|
|
if (fread(input, sizeof(short), N, fp1) != (size_t)N) {
|
|
fprintf(stderr, "File read error (%s)\n", file1);
|
|
fclose(fp1);
|
|
exit(1);
|
|
}
|
|
fclose(fp1);
|
|
|
|
// --- 実行時間の比較 (Time Mode) ---
|
|
LARGE_INTEGER start, end, freq;
|
|
double elapsedTime;
|
|
QueryPerformanceFrequency(&freq);
|
|
|
|
for (int i = 0; i < N; i++) x[i] = getComplex(input[i], 0);
|
|
|
|
QueryPerformanceCounter(&start);
|
|
dft(x, c, N);
|
|
QueryPerformanceCounter(&end);
|
|
elapsedTime = (double)(end.QuadPart - start.QuadPart) / freq.QuadPart;
|
|
printf("\n--- Execution Time Comparison ---\n");
|
|
printf(" DFT Elapsed time: %f seconds\n", elapsedTime);
|
|
|
|
QueryPerformanceCounter(&start);
|
|
fft(x, N, 0);
|
|
QueryPerformanceCounter(&end);
|
|
elapsedTime = (double)(end.QuadPart - start.QuadPart) / freq.QuadPart;
|
|
printf(" FFT Elapsed time: %f seconds\n", elapsedTime);
|
|
|
|
// --- 窓関数適用とパワー比較 (Power Mode) ---
|
|
for (int i = 0; i < N; i++) x[i] = getComplex(input[i], 0);
|
|
double beforePower = calcAmpPower(x, N);
|
|
applyWindow(x, N);
|
|
double afterPower = calcAmpPower(x, N);
|
|
|
|
printf("\n--- Power Analysis (Window Application) ---\n");
|
|
printf(" BEFORE : %16f\n", beforePower);
|
|
printf(" AFTER : %16f\n", afterPower);
|
|
printf(" DIFF : %16f\n", beforePower - afterPower);
|
|
printf(" RATIO : %16f\n", afterPower / beforePower);
|
|
|
|
// --- FFT振幅スペクトル出力 (Amp Mode) ---
|
|
printf("\n--- Amplitude Spectrum Output ---\n");
|
|
fft(x, N, 0); // すでに窓関数適用済みの配列に対してFFTを実行
|
|
saveAmpSpectrum("result_amp_file1.txt", x, N);
|
|
|
|
/* ----------------------------------------------------
|
|
* モード 4: 指定時刻シーク分析 (file2を使用)
|
|
* ---------------------------------------------------- */
|
|
printf("\n[Mode: Seek Analysis (%s)]\n", file2);
|
|
FILE *fp2 = fopen(file2, "rb");
|
|
if (fp2 == NULL) {
|
|
fprintf(stderr, "File open error (%s)\n", file2);
|
|
exit(1);
|
|
}
|
|
|
|
typedef struct {
|
|
double time_sec;
|
|
const char *vowel;
|
|
} SeekTarget;
|
|
|
|
// 指定された5つの時刻を定義
|
|
SeekTarget targets[] = {
|
|
{0.361, "a"}, // あ
|
|
{0.969, "i"}, // い
|
|
{1.415, "u"}, // う
|
|
{1.936, "e"}, // え
|
|
{2.431, "o"} // お
|
|
};
|
|
|
|
long fs = 16000;
|
|
long bytes_per_sample = 2;
|
|
|
|
for (int t = 0; t < 5; t++) {
|
|
long start_byte = (long)(targets[t].time_sec * fs * bytes_per_sample);
|
|
fseek(fp2, start_byte, SEEK_SET);
|
|
|
|
if (fread(input, sizeof(short), N, fp2) != (size_t)N) {
|
|
fprintf(stderr, "File read error (%s) at %f s\n", file2, targets[t].time_sec);
|
|
continue;
|
|
}
|
|
|
|
// Complex型への変換、窓関数の適用、FFTの実行
|
|
for (int i = 0; i < N; i++) x[i] = getComplex(input[i], 0);
|
|
applyWindow(x, N);
|
|
fft(x, N, 0);
|
|
|
|
char out_filename[256];
|
|
sprintf(out_filename, "result_seek_%s.txt", targets[t].vowel);
|
|
saveAmpSpectrum(out_filename, x, N);
|
|
}
|
|
|
|
fclose(fp2);
|
|
|
|
printf("\nAll analyses finished successfully.\n");
|
|
return 0;
|
|
}
|
|
|