引用FFTW3读取并估计wav文件音频的基频
接上一篇《不引用第三方库读取wav文件并逐帧估算基频》,这里引用了第三方专用库FFTW3,实现同样的功能。
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
#include <fftw3.h>
#pragma comment( lib, "libfftw3-3.lib")
#pragma comment( lib, "libfftw3f-3.lib")
#pragma comment( lib, "libfftw3l-3.lib")
// WAV文件头结构
typedef struct {
char riff[4];
uint32_t overall_size;
char wave[4];
char fmt_chunk_marker[4];
uint32_t length_of_fmt;
uint16_t format_type;
uint16_t channels;
uint32_t sample_rate;
uint32_t byte_rate;
uint16_t block_align;
uint16_t bits_per_sample;
char data_chunk_header[4];
uint32_t data_size;
} WAV_HEADER;
// 读取WAV文件头
WAV_HEADER read_wav_header(FILE* file) {
WAV_HEADER header;
fread(&header, sizeof(WAV_HEADER), 1, file);
return header;
}
// 读取WAV文件的音频数据
float* read_wav_data(FILE* file, WAV_HEADER header, int* num_samples) {
int data_size = header.data_size / (header.bits_per_sample / 8);
float* data = (float*)malloc(data_size * sizeof(float));
short int* temp = (short int*)malloc(header.data_size);
fread(temp, sizeof(short int), header.data_size / sizeof(short int), file);
for (int i = 0; i < data_size; i++) {
data[i] = (float)temp[i] / 32768.0; // 假设是16位PCM编码,范围是-32768到32767
}
free(temp);
*num_samples = data_size;
return data;
}
// 估算基频(简单峰值检测)
double estimate_fundamental_frequency(fftw_complex* out, int N, double sample_rate) {
double max_magnitude = 0.0;
int max_index = 0;
// 查找FFT结果中的最大峰值(除了直流分量)
for (int i = 1; i < N / 2; i++) {
double magnitude = sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]);
if (magnitude > max_magnitude) {
max_magnitude = magnitude;
max_index = i;
}
}
// 计算基频
double fundamental_frequency = (double)max_index * (sample_rate / N);
return fundamental_frequency;
}
// 自定义基频估算函数(简单峰值检测)
double custom_estimate_fundamental_frequency(fftw_complex* out, int N, double sample_rate) {
double max_magnitude = 0.0;
int max_index = 0;
int start_index = N / (sample_rate / 10.0); // 假设我们只对10Hz到采样率一半之间的频率感兴趣
int end_index = N / 2; // FFT结果的对称性,只考虑前半部分
// 查找FFT结果中的最大峰值(在指定范围内)
for (int i = start_index; i < end_index; i++) {
double magnitude = sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]);
if (magnitude > max_magnitude) {
max_magnitude = magnitude;
max_index = i;
}
}
// 如果找到了峰值,则计算对应的频率
if (max_index > 0 && max_index < end_index) {
double fundamental_frequency = (double)max_index * (sample_rate / N);
return fundamental_frequency;
}
else {
// 如果没有找到明显的峰值,则返回0或某个表示未找到基频的值
return -1.0; // 这里返回-1表示未找到基频,实际应用中可以根据需要调整
}
}
// 自定义基频估算函数(简单峰值检测)
double estimate_fundamental_frequency(double* magnitudes, int N, double sample_rate) {
double max_magnitude = 0.0;
int max_index = 0;
int start_index = N / (sample_rate / 10.0); // 假设我们只对10Hz到采样率一半之间的频率感兴趣
int end_index = N / 2; // FFT结果的对称性,只考虑前半部分
// 查找FFT结果中的最大峰值(在指定范围内)
for (int i = start_index; i < end_index; i++) {
if (magnitudes[i] > max_magnitude) {
max_magnitude = magnitudes[i];
max_index = i;
}
}
// 如果找到了峰值,则计算对应的频率
if (max_index > 0 && max_index < end_index) {
double fundamental_frequency = (double)max_index * (sample_rate / N);
return fundamental_frequency;
}
else {
// 如果没有找到明显的峰值,则返回0或某个表示未找到基频的值
return -1.0; // 这里返回-1表示未找到基频,实际应用中可以根据需要调整
}
}
int main(int argc, char* argv[]) {
//if (argc != 2) {
// fprintf(stderr, "Usage: %s <wav_file>\n", argv[0]);
// return 1;
//}
FILE* file = fopen("wdsyy1.wav", "rb");
if (!file) {
perror("Failed to open file");
return 1;
}
WAV_HEADER header = read_wav_header(file);
// 检查是否是PCM编码的WAV文件
if (header.format_type != 1) {
fprintf(stderr, "Unsupported WAV format\n");
fclose(file);
return 1;
}
int num_samples;
float* data = read_wav_data(file, header, &num_samples);
fclose(file);
// 设置FFT大小(通常是2的幂)
int N = 1024; // 可以根据需要调整
while (N < num_samples) N *= 2;
// 为FFT准备输入和输出数组
fftw_complex* in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);
fftw_complex* out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * N);
//fftw_plan plan = fftw_plan_dft_r2c_1d(N, (double*)data, out, FFTW_ESTIMATE);
//fftw_plan plan = fftw_plan_dft_1d(N, in, out, FFTW_ESTIMATE);
// 将音频数据填充到FFT输入数组中(注意:只填充前num_samples个值,其余置零)
for (int i = 0; i < num_samples; i++) {
in[i][0] = data[i];
in[i][1] = 0.0;
}
for (int i = num_samples; i < N; i++) {
in[i][0] = 0.0;
in[i][1] = 0.0;
}
fftw_plan plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
// 执行FFT
fftw_execute(plan);
// 计算FFT结果的幅度
double* magnitudes = (double*)malloc(sizeof(double) * (N / 2 + 1));
for (int i = 0; i <= N / 2; i++) {
magnitudes[i] = sqrt(out[i][0] * out[i][0] + out[i][1] * out[i][1]);
}
// 估算基频
//double fundamental_frequency = estimate_fundamental_frequency(out, N, header.sample_rate);
//double fundamental_frequency = custom_estimate_fundamental_frequency(out, N, header.sample_rate);
// 估算基频
double fundamental_frequency = estimate_fundamental_frequency(magnitudes, N, header.sample_rate);
printf("Estimated fundamental frequency: %.2f Hz\n", fundamental_frequency);
// 清理
fftw_destroy_plan(plan);
fftw_free(in);
fftw_free(out);
free(data);
return 0;
}运行结果

FFTW3库文件(头文件,lib,dll)
X64由于文件超大了,所以分开了,下载后放到一起即可。
凯特网版权声明:以上内容允许转载,但请注明出处,谢谢!

