使用 fopen_s读取一个wav文件并对其进行fft和STFT并输出结果c语言代码不使用fftw库函数并解释代码
时间: 2024-05-16 13:17:43 浏览: 184
c代码-生成50个0-50的整数,排序后输出。
下面是使用C语言实现读取WAV文件,并进行FFT和STFT的代码示例,不使用fftw库函数:
```c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14159265358979323846
// WAV文件头信息结构体
typedef struct {
char ChunkID[4];
unsigned int ChunkSize;
char Format[4];
char Subchunk1ID[4];
unsigned int Subchunk1Size;
unsigned short AudioFormat;
unsigned short NumChannels;
unsigned int SampleRate;
unsigned int ByteRate;
unsigned short BlockAlign;
unsigned short BitsPerSample;
char Subchunk2ID[4];
unsigned int Subchunk2Size;
} WavHeader;
int main(int argc, char* argv[]) {
// 打开WAV文件
FILE* fp = NULL;
fopen_s(&fp, "audio.wav", "rb");
if (fp == NULL) {
printf("Error opening file.");
exit(1);
}
// 读取WAV文件头信息
WavHeader header;
size_t bytes_read = fread(&header, sizeof(WavHeader), 1, fp);
if (bytes_read != 1) {
printf("Error reading file header.");
exit(1);
}
// 计算每一帧音频的采样数和FFT窗口大小
int frame_size = 1024; // 每一帧音频的采样数
int fft_size = 2048; // FFT窗口大小
int hop_size = frame_size / 2; // 每一帧音频之间的跳跃步长
int num_frames = (header.Subchunk2Size / header.BlockAlign - frame_size) / hop_size + 1;
// 分配内存空间
short* audio_data = (short*) malloc(sizeof(short) * header.Subchunk2Size / sizeof(short));
float* fft_real = (float*) malloc(sizeof(float) * fft_size);
float* fft_imag = (float*) malloc(sizeof(float) * fft_size);
float* stft_real = (float*) malloc(sizeof(float) * num_frames * fft_size);
float* stft_imag = (float*) malloc(sizeof(float) * num_frames * fft_size);
// 读取音频数据
bytes_read = fread(audio_data, sizeof(short), header.Subchunk2Size / sizeof(short), fp);
if (bytes_read != header.Subchunk2Size / sizeof(short)) {
printf("Error reading audio data.");
exit(1);
}
// 对每一帧音频进行fft和STFT
int i, j, k;
for (i = 0; i < num_frames; i++) {
// 计算当前帧音频的起始位置
int start_index = i * hop_size;
// 将音频数据复制到FFT窗口中
for (j = 0; j < fft_size; j++) {
int audio_index = start_index + j - fft_size / 2;
if (audio_index < 0 || audio_index >= header.Subchunk2Size / sizeof(short)) {
fft_real[j] = 0.0f;
} else {
fft_real[j] = (float) audio_data[audio_index];
}
fft_imag[j] = 0.0f;
}
// 对FFT窗口进行快速傅里叶变换
for (j = 0; j < fft_size; j++) {
float sum_real = 0.0f;
float sum_imag = 0.0f;
for (k = 0; k < fft_size; k++) {
float angle = 2.0f * PI * j * k / fft_size;
sum_real += fft_real[k] * cos(angle) + fft_imag[k] * sin(angle);
sum_imag += fft_imag[k] * cos(angle) - fft_real[k] * sin(angle);
}
stft_real[i * fft_size + j] = sum_real;
stft_imag[i * fft_size + j] = sum_imag;
}
}
// 输出结果
for (i = 0; i < num_frames; i++) {
printf("Frame %d:\n", i);
for (j = 0; j < fft_size; j++) {
printf("%f + %fi\n", stft_real[i * fft_size + j], stft_imag[i * fft_size + j]);
}
printf("\n");
}
// 释放内存空间
free(audio_data);
free(fft_real);
free(fft_imag);
free(stft_real);
free(stft_imag);
// 关闭文件
fclose(fp);
return 0;
}
```
代码中使用了WAV文件头信息结构体来存储WAV文件的头部信息,通过读取WAV文件头信息来获取音频数据的采样率、通道数、采样位数等重要参数。
代码中先计算了每一帧音频的采样数和FFT窗口大小,然后通过fread函数读取了整个WAV文件的音频数据。接着,对于每一帧音频数据,将其复制到FFT窗口中,然后使用傅里叶变换计算FFT窗口的频域表示,并将结果存储到STFT矩阵中。
最后,输出了STFT矩阵的结果。需要注意的是,STFT矩阵的大小为num_frames x fft_size,其中num_frames表示总共有多少帧音频数据,fft_size表示每一帧音频数据的FFT窗口大小。
需要指出的是,本代码示例中实现的FFT算法是一种Naive算法,其时间复杂度为O(N^2),因此在实际应用中可能不够高效。如果需要更高效的FFT算法,可以参考其他开源库,例如fftw、KissFFT等。
阅读全文