1.PulseAudio音量模型
2.PulseAudio基础设施
PulseAudio音量模型
对于任何一个跟声音相关的源码软件/硬件,音量无疑都是源码非常重要的东西。太小听不见,源码太大有损听力。源码对数字音频来说大音量时还可能有失真。源码对车机来说,源码影视源码免更新大音量机器功率也相应增大,源码增加电力消耗。源码所以了解一些音量相关的源码知识还是很有必要的。
参考文献:
简单说来,源码人耳对音量的源码感知是反直觉的。如左下图所示,源码声音振幅随音量滑块线性变化,源码此时,源码人耳对音量的源码感觉如右下图所示。小音量时,肇庆到深圳源码滑块等距变化,听上去音量变化很大;大音量时,滑块等距变化,听上去音量变化反而没那么大。
当声音振幅随音量滑块呈现左下图那样的非线性变化时,人耳对音量的感觉如右下图所示,呈线性变化,比较符合人的直觉。关于声音振幅随音量滑块的非线性变化,PulseAudio采用了一种立方映射模型(cubic mapping)。
PulseAudio里有四种音量,这四种音量是等价的,可以使用volume.h中的api进行相互换算。
volume是PulseAudio里基本的音量表示,属于pa_volume_t类型,微有交友源码相关定义见下面的代码段。
就是2.1中的volume与PA_VOLUME_NORM的百分比。
linear是PulseAudio立方音量模型的体现,linear与volume之间的关系如下:
其中, 为linear, 为volume, 为PA_VOLUME_NORM。
可以理解为通常声学意义上的分贝(dB)。
其中, 为decibel, 为linear, 为volume, 为PA_VOLUME_NORM。
一段PCM格式的数字音频,如果要将音量调大(小),动力线公式源码只要把这段音频里的每个数据都调大(小)。实操的话,只要乘以相应的系数就可以了。这个系数就是前文提到的PulseAudio四种音量里的linear。
对于一段音频 ,调整音量后得到 ,则
其中, 为linear, 为volume, 为PA_VOLUME_NORM,画成框图的话:
如果一段音频 ,先调整了音量 ,又调整了 ,则最终的 :
用框图表示:
这样子处理当然没有什么问题,但对于同一段音频,注册留言墙源码经过一个音量调整块需要做 次运算(跟音频格式、时长等有关),经过两个音量调整块的话就需要 次运算。这种情况是可以优化的,只要运用乘法结合律改变一下运算顺序就可以了。这样无论这段音频经过几个音量调块,算法的时间复杂度都是不变的。
可能是出于编码实践方面的考量,对于一段音频先后经过几个音量调整块的情况,PulseAudio里采用了一种等效音量的做法,其目的也是在降低多次音量调整的时间复杂度。
对于一段音频 ,先调整了音量 ,又调整了音量 ,得到了最终的 。那么,完全可以想象有一个等效音量 ,使得同样的 ,调整音量 后得到同样的 。
由此可以得出,
进一步,
反映到代码上,
PulseAudio的音量模型到此介绍完毕,关于不同通道、不同位深、有符号/无符号、整型/浮点型、大端/小端等不同音频格式的细致处理,可以去看源码。
最后再说一个mute on的事。从笔者对应DSP驱动的经验来看,mute和volume设定的是同一个寄存器,mute on就是把volume调到最小。但PulseAudio的实现不是这样的。上面讲过,调volume就是对音频数据做乘法。mute on如果也这样实现的话,有点浪费算力了。因为对于给定的音频格式,不用做乘法也知道mute on时对应的数据是啥,直接调用memset()就可以了。但不要想当然的以为是memset(void *s, 0, size_t n),不同格式下对应mute on时的音频数据并不都是0。具体见下面代码段里silent_byte()的实现。
PulseAudio基础设施
pa_mainloop是PulseAudio核心基础设施,提供timer、异步io、defer三种功能。实现方案包括基于glib和poll()、pipe()等API两种,本文仅探讨后者。poll()是阻塞函数,等待timeout时间,time_event功能即基于此。pipe()用于进程间通信,本文示例展示如何自建匿名管道,一个读、一个写。
pa_mainloop使用方法包含创建和销毁对象,主要步骤为:调用pa_mainloop_iterate()迭代一次,调用pa_mainloop_run()让主循环持续运行。具体用法在相关文件中详细说明。
深入分析pa_mainloop源码,涉及核心数据结构和API方法。io_new()、time_new()、defer_new()创建事件,共同点在于构建pollfds数组并调整timeout计算。io_new()特别添加文件描述符,time_new()设定触发时间,pa_mainloop_poll()利用这些信息。
enable和disable事件时调整状态,调用pa_mainloop_wakeup()唤醒主循环。time_new()创建一次性timer,调用time_restart()可实现周期性操作。事件释放时将状态标记为dead,pa_mainloop_prepare()时进行清理,同时可能调整计数器并唤醒主循环。
综上,pa_mainloop作为PulseAudio关键组件,通过精心设计的API实现灵活高效的功能。深入了解其内部机制有助于更深入地掌握PulseAudio工作原理。