系列文章目录
前言
结合上一节的内容,这节我们使用VS2022,结合libsndfile库设计一个用于处理音频文件的PEQ。
该PEQ可以根据滤波器类型设置不同的参数。
一、前期准备
libsndfile是什么?
libsndfile是一个强大且实用的音频处理库,它提供了跨平台的支持,覆盖了大量常见的音频格式,并且接口灵活,易于集成到现有项目。此外,它有着活跃的社区和完善的文档,使得无论你是初学者还是经验丰富的开发者,都能从中找到你需要的功能。
1.2.2支持的音频格式:
Microsoft WAV | SGI / Apple AIFF / AIFC | Sun / DEC / NeXT AU / SND | Headerless RAW | Paris Audio File PAF | Commodore Amiga IFF / SVX | Sphere Nist WAV | IRCAM SF | Creative VOC | Soundforge W64 | GNU Octave 2.0 MAT4 | GNU Octave 2.1 MAT5 | Portable Voice Format PVF | Fasttracker 2 XI | HMM Tool Kit HTK | Apple CAF | Sound Designer II SD2 | Free Lossless Audio Codec FLAC |
---|
下载地址:libsndfile
github:libsndfile
二、VS 启动!
1.创建项目
1.新建项目
2.配置源文件头文件
1.添加头文件BiQuad.h
记得要提前把上一节中的两个文件移动到项目文件夹中
2.添加源文件
和上一步相同的方法添加BiQuad.cpp
3.新建主函数main.cpp
点击“添加”
4.完整的项目文件
3.配置libsndfile库
1.将下载好的libsndfile文件添加到项目目录
2.把\libsndfile-1.2.2-win64\bin文件下的sndfile.dll复制到根目录
3.在VS修改项目属性
右键解决方案>属性
3.“C/C++”>“附加包含目录”
添加根目录和libsndfile库的目录:
4.“链接器”>“常规”>“附加库目录”
添加的路径如下:
4.编写主函数
1.需要修改的参数
1.设置输入输出文件的路径
// 定义输入和输出文件的路径
const char* inputFileName = "music\\扫频48k.wav";
const char* outputFileName = "music\\output_扫频.wav";
2.添加滤波器,设置每种滤波器的类型和相关的参数
BiQuad(Type type, double dbGain, double freq, double srate, double bandwidthOrQOrS, bool isBandwidthOrS);
参数分析:
type
: 滤波器类型(LOW_PASS, HIGH_PASS, BAND_PASS, NOTCH, ALL_PASS, PEAKING, LOW_SHELF, HIGH_SHELF)
gain
:增益值
freq
: 截止频率
bandwidthOrQOrS
: 带宽或Q值或S参数,值越大影响的频率范围越小
isBandwidthOrS
: 带宽或S参数标志(只有在高低切滤波器是这是为true
,其他类型此参数设置为false
)
// 创建滤波器组,并添加所需的滤波器频段
FilterBank filterBank(sfinfo.samplerate);
// 添加各种类型的滤波器
//filterBank.addBand(BiQuad::LOW_PASS, 0.0, 500.0, 0.707, false); // 低通滤波器
//filterBank.addBand(BiQuad::HIGH_PASS, 0.0, 2000.0, 0.707, false); // 高通滤波器
//filterBank.addBand(BiQuad::BAND_PASS, 0.0, 1000.0, 8.0, false); // 带通滤波器
//filterBank.addBand(BiQuad::NOTCH, 0.0, 1000.0, 0.8, false); // 陷波滤波器
//filterBank.addBand(BiQuad::ALL_PASS, 0.0, 1000.0, 1.0, false); // 全通滤波器
//filterBank.addBand(BiQuad::PEAKING, 3.0, 1000.0, 1.0, false); // 峰值滤波器
//filterBank.addBand(BiQuad::LOW_SHELF, 3.0, 500.0, 1.0, true); // 低搁架滤波器
filterBank.addBand(BiQuad::HIGH_SHELF, 3.0, 2000.0, 1.0, true); // 高搁架滤波器
2.完整主函数代码
#include <iostream>
#include <vector>
#include <sndfile.h>
#include "BiQuad.h"
// 表示一个频段的滤波器
class FilterBand {
public:
// 构造函数,初始化左右声道的滤波器
FilterBand(BiQuad::Type type, double dbGain, double freq, double srate, double bandwidthOrQOrS, bool isBandwidthOrS)
: filterL(type, dbGain, freq, srate, bandwidthOrQOrS, isBandwidthOrS),
filterR(type, dbGain, freq, srate, bandwidthOrQOrS, isBandwidthOrS) {}
// 处理左声道样本
double processLeft(double sample) {
return filterL.process(sample);
}
// 处理右声道样本
double processRight(double sample) {
return filterR.process(sample);
}
private:
BiQuad filterL; // 左声道滤波器
BiQuad filterR; // 右声道滤波器
};
// 表示一个包含多个频段滤波器的滤波器组
class FilterBank {
public:
// 构造函数,初始化采样率
FilterBank(double srate) : sampleRate(srate) {}
// 添加一个频段的滤波器
void addBand(BiQuad::Type type, double dbGain, double freq, double bandwidthOrQOrS, bool isBandwidthOrS) {
bands.emplace_back(type, dbGain, freq, sampleRate, bandwidthOrQOrS, isBandwidthOrS);
}
// 处理左右声道的音频数据
void process(double* left, double* right, sf_count_t numFrames) {
for (sf_count_t i = 0; i < numFrames; ++i) {
double lSample = left[i];
double rSample = right[i];
for (auto& band : bands) {
lSample = band.processLeft(lSample);
rSample = band.processRight(rSample);
}
left[i] = lSample;
right[i] = rSample;
}
}
private:
double sampleRate; // 采样率
std::vector<FilterBand> bands; // 滤波器频段列表
};
int main() {
// 定义输入和输出文件的路径
const char* inputFileName = "music\\扫频48k.wav";
const char* outputFileName = "music\\output_扫频.wav";
SF_INFO sfinfo; // 音频文件信息结构体
// 打开输入文件
SNDFILE* infile = sf_open(inputFileName, SFM_READ, &sfinfo);
if (!infile) {
std::cerr << "Error: could not open input file " << inputFileName << "\n";
return 1; // 如果无法打开文件,返回错误代码1
}
// 检查输入文件是否为双声道
if (sfinfo.channels != 2) {
std::cerr << "Error: input file is not stereo\n";
sf_close(infile);
return 1; // 如果文件不是双声道,返回错误代码1
}
// 分配内存以存储左右声道数据
std::vector<double> left(sfinfo.frames);
std::vector<double> right(sfinfo.frames);
std::vector<double> buffer(sfinfo.frames * 2); // 存储双声道的音频数据
// 读取输入文件的音频数据到缓冲区
sf_read_double(infile, buffer.data(), sfinfo.frames * 2);
sf_close(infile); // 读取完成后关闭输入文件
// 将缓冲区数据分离成左右声道
for (sf_count_t i = 0; i < sfinfo.frames; ++i) {
left[i] = buffer[2 * i]; // 左声道数据
right[i] = buffer[2 * i + 1]; // 右声道数据
}
// 创建滤波器组,并添加所需的滤波器频段
FilterBank filterBank(sfinfo.samplerate);
// 添加各种类型的滤波器
//filterBank.addBand(BiQuad::LOW_PASS, 0.0, 500.0, 0.707, false); // 低通滤波器
//filterBank.addBand(BiQuad::HIGH_PASS, 0.0, 2000.0, 0.707, false); // 高通滤波器
//filterBank.addBand(BiQuad::BAND_PASS, 0.0, 1000.0, 8.0, false); // 带通滤波器
//filterBank.addBand(BiQuad::NOTCH, 0.0, 1000.0, 0.8, false); // 陷波滤波器
//filterBank.addBand(BiQuad::ALL_PASS, 0.0, 1000.0, 1.0, false); // 全通滤波器
//filterBank.addBand(BiQuad::PEAKING, 3.0, 1000.0, 1.0, false); // 峰值滤波器
//filterBank.addBand(BiQuad::LOW_SHELF, 3.0, 500.0, 1.0, true); // 低搁架滤波器
filterBank.addBand(BiQuad::HIGH_SHELF, 3.0, 2000.0, 1.0, true); // 高搁架滤波器
// 处理左右声道的音频数据
filterBank.process(left.data(), right.data(), sfinfo.frames);
// 将处理后的数据重新组合成立体声
for (sf_count_t i = 0; i < sfinfo.frames; ++i) {
buffer[2 * i] = left[i]; // 处理后的左声道数据
buffer[2 * i + 1] = right[i]; // 处理后的右声道数据
}
// 打开输出文件
SF_INFO sfinfoOut = sfinfo; // 保持输入文件的信息用于输出文件
SNDFILE* outfile = sf_open(outputFileName, SFM_WRITE, &sfinfoOut);
if (!outfile) {
std::cerr << "Error: could not open output file " << outputFileName << "\n";
return 1; // 如果无法打开文件,返回错误代码1
}
// 将处理后的音频数据写入输出文件
sf_write_double(outfile, buffer.data(), sfinfo.frames * 2);
sf_close(outfile); // 写入完成后关闭输出文件
std::cout << "Processing complete. Output written to " << outputFileName << "\n";
return 0; // 程序执行成功
}
总结
本文利用上节的滤波器构造函数,结合libsndfile库来读写音频文件,设计了一个可以添加任意数量滤波器的PEQ。
虽然上面的代码实现了PEQ的功能,但是这个滤波器使用起来还是不够直观,下一节将在本文代码的基础上生成一个PEQ的动态库,以动态库创建一个网页的前后端。可以在网页上直观的设置滤波器相关参数。
如有错误,欢迎在评论区中指出。