一、概述
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、編譯運行