参数化均衡器(PEQ)制作指南(二)

系列文章目录

参数化均衡器(PEQ)制作指南(一)



前言

结合上一节的内容,这节我们使用VS2022,结合libsndfile库设计一个用于处理音频文件的PEQ。
该PEQ可以根据滤波器类型设置不同的参数。


一、前期准备

libsndfile是什么?
libsndfile是一个强大且实用的音频处理库,它提供了跨平台的支持,覆盖了大量常见的音频格式,并且接口灵活,易于集成到现有项目。此外,它有着活跃的社区和完善的文档,使得无论你是初学者还是经验丰富的开发者,都能从中找到你需要的功能。
1.2.2支持的音频格式:

Microsoft WAVSGI / Apple AIFF / AIFCSun / DEC / NeXT AU / SNDHeaderless RAWParis Audio File PAFCommodore Amiga IFF / SVXSphere Nist WAVIRCAM SFCreative VOCSoundforge W64GNU Octave 2.0 MAT4GNU Octave 2.1 MAT5Portable Voice Format PVFFasttracker 2 XIHMM Tool Kit HTKApple CAFSound Designer II SD2Free 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的动态库,以动态库创建一个网页的前后端。可以在网页上直观的设置滤波器相关参数。
如有错误,欢迎在评论区中指出。

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值