一、概述
libsndfile库是一个用 C 语言编写的开源库,用于读取和写入多种音频文件格式。
环境:QT5.9.9、cmakegui3.23.0、QT的编译器是minWG32
二、安装
1、下载libsndfile源码,连接:https://github.com/libsndfile/libsndfile
2、解压源码,运行cmakegui,选择源码路径和编译目标路径
3、点击Configure,再弹出的对话框选择目标编译器,MinGW Makefiles,点击finish,就会进行相关配置。(如果已经配置过,想要重新配置,需要清理下缓存,cmakegui界面:File->Delete Cache)
4、等配置完成,在界面找到安装路径选项CMAKE_INSTALL_PREFIX,选择编译后生成文件的安装路径,然后点击右下角的Generate,等待完成生成cmakefile相关文件。
5、Generate步骤执行结束后,运行QT minWG环境(在cmd命令窗口执行以下命令)
6、cmd命令窗口,切换路径到刚生成makefiles的文件夹下
7、输入编译命令,mingw32-make,等待编译完成后,再输入mingw32-make install
9、在刚指定的安装路径build_install就可以看到include 、lib、bin 等相关文件,至此编译完成
三、测试
1、创建QT测试项目,把生成的include文件sndfile.h、libsndfile.a拷贝到项目路径下,并在.pro文件添加现有文件和库文件如下
2、编写测试代码
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "fftw3.h"
#include "sndfile.h"
#include <QDebug>
#include <QVector>
#include <complex>
#include <iostream>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);wav_test();
}MainWindow::~MainWindow()
{delete ui;
}// 计算 FFT(双精度版本)
std::vector<std::complex<double>> MainWindow::compute_fft(const std::vector<double>& input_signal, int N) {// 分配 FFTW 输入和输出数组fftw_complex *in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);fftw_complex *out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * N);// 加窗(汉宁窗)std::vector<double> windowed_signal(N);for (int i = 0; i < N; ++i) {double window = 0.5 * (1 - cos(2 * M_PI * i / (N - 1))); // 汉宁窗windowed_signal[i] = input_signal[i] * window;in[i][0] = windowed_signal[i]; // 实部in[i][1] = 0.0; // 虚部}// 创建 FFTW 计划fftw_plan plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);// 执行 FFTfftw_execute(plan);// 转换为 C++ complex 格式std::vector<std::complex<double>> fft_result(N);for (int i = 0; i < N; ++i) {fft_result[i] = std::complex<double>(out[i][0], out[i][1]);}// 清理资源fftw_destroy_plan(plan);fftw_free(in);fftw_free(out);return fft_result;
}// 计算信号能量
double MainWindow::compute_energy(const std::vector<double>& signal) {double energy = 0.0;for (double sample : signal) {energy += sample * sample;}return energy;
}// 判断音频是否正常
bool MainWindow::is_audio_normal(const std::vector<double>& audio_data, int sample_rate) {const int N = 1024; // FFT 点数(必须是 2 的幂次)const int step = N / 2; // 滑动步长(50% 重叠)// 1. 静音检测(总能量是否过低)double total_energy = compute_energy(audio_data);double silence_threshold = 1e-6 * sample_rate; // 经验阈值if (total_energy < silence_threshold) {std::cout << "Silence detected(energy too low)" << std::endl;//检测到静音(能量过低)return false;}// 2. 分帧 FFT 分析(示例仅分析前 1 帧)std::vector<double> frame(audio_data.begin(), audio_data.begin() + N);if (frame.size() < N) {std::cerr << "Not enough audio data" << std::endl;//音频数据不足return false;}std::vector<std::complex<double>> fft_result = compute_fft(frame, N);// 3. 计算幅度谱std::vector<double> magnitude_spectrum(N / 2);for (int i = 0; i < N / 2; ++i) {magnitude_spectrum[i] = std::abs(fft_result[i]);}// 4. 频率分辨率double freq_resolution = sample_rate / N;// 5. 检测目标频段能量(如人声 300Hz~3400Hz)double voice_low = 300.0;double voice_high = 3400.0;int voice_low_idx = static_cast<int>(voice_low / freq_resolution);int voice_high_idx = static_cast<int>(voice_high / freq_resolution);double voice_band_energy = 0.0;for (int i = voice_low_idx; i <= voice_high_idx; ++i) {if (i < magnitude_spectrum.size()) {voice_band_energy += magnitude_spectrum[i];}}// 6. 高频噪声检测(如 > 8000Hz 的能量占比过高)int noise_low_idx = static_cast<int>(8000.0 / freq_resolution);double noise_band_energy = 0.0;for (int i = noise_low_idx; i < magnitude_spectrum.size(); ++i) {noise_band_energy += magnitude_spectrum[i];}double total_spectrum_energy = std::accumulate(magnitude_spectrum.begin(), magnitude_spectrum.end(), 0.0);double noise_ratio = noise_band_energy / total_spectrum_energy;// 7. 判断是否异常if (voice_band_energy < 0.1 * total_spectrum_energy) {std::cout << "Frequency deviation detected (low energy in the vocal range)" << std::endl;//检测到频率偏移(人声频段能量不足)return false;}if (noise_ratio > 0.3) {std::cout << "High-frequency noise detected (high noise proportion)" << std::endl;//检测到高频噪声(噪声占比过高)return false;}std::cout << "audio normal" << std::endl;return true;
}int MainWindow::wav_test() {// 1. 读取 WAV 文件(使用 libsndfile)SF_INFO sf_info;SNDFILE* file = sf_open("audio.wav", SFM_READ, &sf_info);if (!file) {std::cerr << "can not open wav file" << std::endl;return -1;}std::vector<double> audio_data(sf_info.frames * sf_info.channels);sf_read_double(file, audio_data.data(), audio_data.size());sf_close(file);// 如果是立体声,取单声道(或计算双声道均值)if (sf_info.channels == 2) {std::vector<double> mono_audio(audio_data.size() / 2);for (size_t i = 0; i < mono_audio.size(); ++i) {mono_audio[i] = (audio_data[2 * i] + audio_data[2 * i + 1]) / 2.0;}audio_data = mono_audio;}// 2. 判断音频是否正常bool is_normal = is_audio_normal(audio_data, sf_info.samplerate);std::cout << "normal: " << (is_normal ? "Yes" : "No") << std::endl;return 0;
}
5、编译运行