使用 fopen_s读取一个wav文件并对其求WVD并输出WVD结果不使用库函数C语言
时间: 2024-05-13 21:18:44 浏览: 51
C_function_example.rar_C 库函数_C语言_C语言教程_c 语言 教程_库函数
要使用C语言读取一个wav文件,需要了解一下wav文件的格式。wav文件由两个部分组成:文件头和音频数据。文件头包含一些描述音频的信息(如采样率、声道数等),而音频数据则是实际的音频信号。
以下是一个简单的读取wav文件的示例代码:
```c
#include <stdio.h>
#include <stdint.h> //需要用到int16_t类型
#define BUFFER_SIZE 1024
int main()
{
FILE *fp;
char filename[] = "test.wav";
uint16_t buffer[BUFFER_SIZE];
int bytes_per_sample;
int num_samples;
int sample_rate;
int num_channels;
//打开文件
fopen_s(&fp, filename, "rb");
if (fp == NULL) {
printf("Failed to open file\n");
return -1;
}
//读取文件头
fseek(fp, 22, SEEK_SET);
fread(&num_channels, sizeof(uint16_t), 1, fp);
fseek(fp, 34, SEEK_SET);
fread(&bytes_per_sample, sizeof(uint16_t), 1, fp);
fseek(fp, 24, SEEK_SET);
fread(&sample_rate, sizeof(uint16_t), 1, fp);
fseek(fp, 40, SEEK_SET);
fread(&num_samples, sizeof(uint16_t), 1, fp);
//读取音频数据
fseek(fp, 44, SEEK_SET);
while (fread(buffer, sizeof(uint16_t), BUFFER_SIZE, fp) > 0) {
//在这里可以对buffer进行处理
}
//关闭文件
fclose(fp);
return 0;
}
```
读取完wav文件之后,就可以对它进行WVD变换了。WVD变换需要用到时域信号的一阶导数和二阶导数,因此需要先对音频信号进行微分。具体步骤如下:
1. 将音频信号分为若干个重叠的帧,每帧大小为N;
2. 对每帧信号进行微分,得到一阶导数和二阶导数;
3. 将一阶导数和二阶导数按照时间和频率进行外积,得到WVD。
以下是一个简单的求WVD的示例代码:
```c
#include <stdio.h>
#include <stdint.h> //需要用到int16_t类型
#include <math.h>
#define BUFFER_SIZE 1024
#define FRAME_SIZE 256
void diff(uint16_t *input, int len, int16_t *output)
{
for (int i = 0; i < len-1; i++) {
output[i] = (int16_t)(input[i+1] - input[i]);
}
output[len-1] = output[len-2];
}
void wvd(int16_t *input, int len, float *output, int nfft)
{
float *spec1 = (float *)malloc(nfft * sizeof(float));
float *spec2 = (float *)malloc(nfft * sizeof(float));
float *spec3 = (float *)malloc(nfft * sizeof(float));
float *wvd = output;
for (int i = 0; i < len; i += FRAME_SIZE/2) {
//取一帧数据
uint16_t frame[FRAME_SIZE];
for (int j = 0; j < FRAME_SIZE; j++) {
if (i+j < len) {
frame[j] = (uint16_t)input[i+j];
} else {
frame[j] = 0;
}
}
//计算一阶导数和二阶导数
int16_t diff1[FRAME_SIZE];
int16_t diff2[FRAME_SIZE];
diff(frame, FRAME_SIZE, diff1);
diff(diff1, FRAME_SIZE, diff2);
//计算WVD
for (int n = 0; n < nfft; n++) {
spec1[n] = 0.0;
spec2[n] = 0.0;
spec3[n] = 0.0;
}
for (int m = 0; m < FRAME_SIZE; m++) {
for (int n = 0; n < nfft; n++) {
float w = 2.0 * M_PI * m * n / nfft;
float cosw = cos(w);
float sinw = sin(w);
spec1[n] += frame[m] * cosw;
spec2[n] += diff1[m] * cosw;
spec3[n] += diff2[m] * cosw;
}
}
for (int n = 0; n < nfft; n++) {
for (int k = 0; k < nfft; k++) {
int m = n + k - nfft/2;
if (m >= 0 && m < nfft) {
wvd[n*nfft+k] += spec1[n]*spec2[m]*spec3[k];
}
}
}
}
free(spec1);
free(spec2);
free(spec3);
}
int main()
{
FILE *fp;
char filename[] = "test.wav";
uint16_t buffer[BUFFER_SIZE];
int bytes_per_sample;
int num_samples;
int sample_rate;
int num_channels;
//打开文件
fopen_s(&fp, filename, "rb");
if (fp == NULL) {
printf("Failed to open file\n");
return -1;
}
//读取文件头
fseek(fp, 22, SEEK_SET);
fread(&num_channels, sizeof(uint16_t), 1, fp);
fseek(fp, 34, SEEK_SET);
fread(&bytes_per_sample, sizeof(uint16_t), 1, fp);
fseek(fp, 24, SEEK_SET);
fread(&sample_rate, sizeof(uint16_t), 1, fp);
fseek(fp, 40, SEEK_SET);
fread(&num_samples, sizeof(uint16_t), 1, fp);
//读取音频数据
int16_t *audio_data = (int16_t *)malloc(num_samples * sizeof(int16_t));
fseek(fp, 44, SEEK_SET);
fread(audio_data, sizeof(uint16_t), num_samples, fp);
//关闭文件
fclose(fp);
//计算WVD
int nfft = 2048;
float *wvd_data = (float *)calloc(nfft * nfft, sizeof(float));
wvd(audio_data, num_samples, wvd_data, nfft);
//输出WVD结果
for (int n = 0; n < nfft; n++) {
for (int k = 0; k < nfft; k++) {
printf("%f ", wvd_data[n*nfft+k]);
}
printf("\n");
}
free(audio_data);
free(wvd_data);
return 0;
}
```
需要注意的是,以上代码并没有进行任何优化,仅仅是为了演示WVD变换的基本过程。实际应用中需要考虑到性能和精度的平衡,可能需要使用更高效的算法和数据结构。
阅读全文