Qt实现数字音频均衡器

在实现音频播放器的时候,我们常常需要一个均衡器来调节各个频段的增益,就是我们平常说的调重低音。一个数字均衡器的架构通常都如图所示:

从图中可以看到,这里的数字均衡器实际上就是三个滤波器,各个滤波器分别负责不同频段的音频调节,这三个滤波器叫做滤波器组。当然一个数字滤波器组也不一定只有三个滤波器,理论上来说可以有任意多个滤波器,而且滤波器越多,能调整的也就越精细。

从物理上来说人类听觉的频率范围在0~20kHZ这个范围,于是我们定义三个频段:[0,Hz 400Hz], [400, 2000HZ], [2000Hz, 无穷],分别为低音,中音和高音。于是我们只需要设计出来一个[0, 400Hz]的低通滤波器,一个[400, 2000Hz]的带通滤波器,以及一个[2000Hz,无穷]的高通滤波器就可以组成一个均衡器了。当然本文并不是讨论如何设计滤波器的,这是一个复杂的数学推导过程,有兴趣的可以来信探讨。另外,我已经设计好了这三个滤波器,文末会附上代码。

好了,现在我们有了思路了,来看看如何在Qt中实现这个想法。首先最重要的是提供这个滤波器组,一个滤波器组大概长这样:

class EQFilterGroup
{public:/*参数: 低音增益, 中音增益, 高音增益*/EQFilterGroup(float const lowGain, float const midGain, float const highGain);/*setter and getter here*/    virtual QBuffer* filter(QAudioBuffer const& buffer);
}

这个类提供一个接口,这个接口输入一个音频帧(QBuffer),然后输出滤波后的音频帧。

然后我们看一下Qt中如何播放一个音频流:

   QFile sourceFile;QAudioOutput *audio = new QAudioOutput(this);sourceFile.setFileName("/tmp/test.raw");sourceFile.open(QIODevice::ReadOnly);QAudioFormat format;format.setSampleRate(8000);format.setChannelCount(1);format.setSampleSize(8);format.setCodec("audio/pcm");format.setByteOrder(QAudioFormat::LittleEndian);format.setSampleType(QAudioFormat::UnSignedInt);QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());if (!info.isFormatSupported(format)) {qWarning() << "Raw audio format not supported by backend, cannot play audio.";return;}audio = new QAudioOutput(format, this);audio->start(&sourceFile);

其实非常简单,只需要给QAudioOutput这个组件设置好播放的音频格式参数,然后提供一个音频流就可以播放这个音频流了,在Qt里面所有的数据流都被抽象称为了QIODevice, 当然音频流也不例外,代码片段中的sourceFile就是一个音频文件数据流,它是QIODevice的子类。
于是现在的问题是: 我们如何将EQFilterGroup整合到上述代码当中?如果我们能把滤波器组伪装成一个像上面代码里sourceFile的文件一样就好了,那么QAudioOutput就可以直接读取滤波器组滤波后的数据流了。这里我们可以使用适配器模式来帮助我们完成这一个伪装。根据适配器模式的做法,我们只需要继承QIODevice然后实现对应的接口并且集成EQFilterGroup的滤波功能就可以实现一个可以滤波的QIODevice。大概代码如下:

class AudioBufferDevice : public QIODevice
{Q_OBJECT
public:explicit AudioBufferDevice(QAudioDecoder *decoder, EQFilterGroup* filter, QObject *parent = nullptr);virtual bool atEnd() const override;virtual qint64 bytesAvailable() const override;protected:virtual qint64 readData(char* data, qint64 size) override;virtual qint64 writeData(const char *data, qint64 maxSize);private:QAudioDecoder* _decoder;QQueue<QBuffer*> _queue;QQueue<QAudioBuffer*> _abuffer_queue;EQFilterGroup* _filter;bool _isFinished;
};

由于我们需要播放的是mp3文件,所以我们首先要通过QAudioDecoder来将mp3文件解码成音频帧,然后将音频帧输入滤波器组,滤波器组将滤波后的音频帧写入一个FIFO缓冲区内,并且通过QIODevice::readData接口向外界提供这些滤波后音频帧的数据流。当然,出于性能考虑,从QAudioDecoder解码到EQFilterGroup将滤波后数据写入缓冲池这一个过程也可以放入另一个线程中进行。

这样完成了上述的类之后,我们就可以实现一个低音炮播放器了:

   EQFilterGroup* filter = new EQFilterGroup(2.0, 0.5, 0.5); //放大低音2倍, 中音高音弱化为1/2QAudioDecoder* decoder = new QAudioDecoder(this);decoder->setSourceFilename("/tmp/test.raw");QAudioFormat format;format.setSampleRate(8000);format.setChannelCount(1);format.setSampleSize(8);format.setCodec("audio/pcm");format.setByteOrder(QAudioFormat::LittleEndian);format.setSampleType(QAudioFormat::UnSignedInt);decoder->setAudioFormat(format);QIODevice *device = new AudioBufferDevice(decoder, filter);device->open(QIODevice::ReadOnly);QAudioOutput *audio = new QAudioOutput(this);QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());if (!info.isFormatSupported(format)) {qWarning() << "Raw audio format not supported by backend, cannot play audio.";return;}audio = new QAudioOutput(format, this);audio->start(device);

以上就是在Qt中实现数字音频均衡器的全部啦,完整的代码在这里Qt实现数字音频均衡器

Qt实现数字音频均衡器[文末附代码]相关推荐

  1. 【控制系统数字仿真与CAD——实验报告】实验三:离散相似法数字仿真(文末附完整代码 + 实验结果)

    一.实验目的 1. 了解离散相似法的基本原理 2. 掌握离散相似法仿真的基本过程 3. 应用离散相似法仿真非线性系统 4. MATLAB实现离散相似法的非线性系统仿真 5. 掌握SIMULINK仿真方 ...

  2. Python 阶段性总结《抽奖系统》(文末附代码地址)

    大家好,今天是python 的阶段性总结,经过前面的学习,我们需要用一个小Demo(抽奖系统)来巩固我们的所学所识: 1.抽奖系统介绍 这是一个通过命令行执行抽奖的操作系统,没有华丽的界面. 1.1 ...

  3. python 表情包 gif_几行 python 代码合成 gif / 微信表情~与恶意合成软件说再见【文末附代码】...

    图片来源:<鬼灭之刃> op 截屏 gif 格式的图片不但自带"能动"属性,还可作为"表情"被收入微信表情包.随手制作 gif 表情包可以算得上是当 ...

  4. 几行 python 代码合成 gif / 微信表情~与恶意合成软件说再见【文末附代码】

    图片来源:<鬼灭之刃> op 截屏 gif 格式的图片不但自带"能动"属性,还可作为"表情"被收入微信表情包.随手制作 gif 表情包可以算得上是当 ...

  5. 基于P2P万信金融-- 万信金融项目之业务大总结(文末附代码地址)

    一.项目介绍 1. 行业简介 P2P金融又叫P2P信贷.其中,P2P是 peer-to-peer 或 person-to-person 的简写,意思是:个人对个 人.P2P金融指个人与个人间的小额借贷 ...

  6. 利用vue数据驱动视图的思想实现走马灯(文末附代码)

    以"猥琐发育,别浪!"为例. 思路: 1.搭建静态页面 2.创建vue实例,展示数据 3.处理字符串(切割+拼接) 4.开始.暂停按钮的点击事件 搭建静态页面 <div id ...

  7. 周志华《Machine Learning》学习笔记(6)--神经网络(文末附代码)

    上篇主要讨论了决策树算法.首先从决策树的基本概念出发,引出决策树基于树形结构进行决策,进一步介绍了构造决策树的递归流程以及其递归终止条件,在递归的过程中,划分属性的选择起到了关键作用,因此紧接着讨论了 ...

  8. @autowired注解_品Spring:对@Autowired和@Value注解的处理方法(文末附spring系列资源合集)...

    作者:编程新说李新杰 出自:微信公众号"编程新说" 原文:品Spring:对@Autowired和@Value注解的处理方法 在Spring中能够完成依赖注入的注解有JavaSE提 ...

  9. MATLAB实战系列(十九)-遗传算法解决TSP(旅行商)问题-应用及解析(文末附MATLAB源码)

    接上篇MATLAB实战系列(十八)-遗传算法解决TSP(旅行商)问题-算法原理 https://wenyusuran.blog.csdn.net/article/details/114060030 感 ...

最新文章

  1. Vue指令实战:结合bootstrap做一个用户信息输入表格
  2. [JS 分析] 汽_车_之_家 JS 生成 CSS 伪元素 hs_kw44_configUS::before
  3. 装饰者模式在源码中的应用
  4. fcntl函数之文件锁 F_SETLK
  5. OpenJudge NOI 1.8 15:细菌的繁殖与扩散
  6. mybatis sql标签_【1039期】Mybatis面试18问,你想知道的都在这里了!
  7. 超级计算机应用领域的概括,超级计算机进展的相关研究
  8. VS2010与OpenCV2410简单配置
  9. fasterrcnn论文_【每周CV论文推荐】 深度学习人脸检测入门必读文章
  10. 数据库设计优化经验谈(转载)
  11. 7.15 HTMl + CSS 笔记整理(一)
  12. 为何python不好找工作-听说自学Python不好找工作,小白要如何学Python?
  13. 百鸡问题的四种(层)解法
  14. 花音机器人_氧叔在“难红难在哪儿”系列中分析曾黎时提到:
  15. 运算放大器电路PCB的设计技巧
  16. 在Wordpress网页直接插入bilibili视频方法
  17. C++虚函数概念及使用(基础)
  18. aot慈善币跑路了_慈善币AOT:用公益收割“韭菜”
  19. 【杂说】东南大学建校117周年,校庆快乐
  20. 直播间自动发言互动的思路和实现步骤,和打包成APP的方法

热门文章

  1. 程序员的千元级护眼神器? ——明基BL2480T简测
  2. Python机器学习期末总复习
  3. autolink android不显示下划线,Android开发技巧——去掉TextView中autolink的下划线
  4. lombok的setter和getter失效问题
  5. 安卓能连接无线苹果不能连接到服务器,为啥安卓连的上的WiFi苹果连上了没网 快来看看...
  6. 10.Redis系列之数据类型GeoSpatial
  7. java web 名片管理系统
  8. DWR(2):DWR配置详情
  9. 微信小程序 --- 完成小程序支付功能
  10. Linux中没有rc.local文件的解决方法