音频编码ES8311调试笔记(五)
ES8311音量寄存器设置
上次对音量设置不太在乎,结果发现其中还是有不少知识的,补上一篇关于音量寄存器和硬件电路关系的笔记。
设置音量
(ES8311_REG32, 0xff) // DAC 音量,默认是0,最大255,可以编个映射的函数设定音量曲线调整。
简单粗暴的方法是直接把0-100映射到寄存器的0-0xff值,缺点是同样的程序,不同的功放会导致声音大小不一样。
官方组件中的说明:为了平衡在不同平台上播放相同内容的响度差异,需要了解音频增益的相关机制。简单说来音频增益包括软件增益 (可调节)和硬件增益 (不可调节) 两部分。软件增益可以通过改变音频数据的幅值或者改变音量寄存器实现。硬件增益受外围电路的影响,主要取决于模拟信号的放大系数。实现中选取了典型的影响参数 esp_codec_dev_hw_gain_t,作为配置参数进行配置,以抵消平台间响度差异,详情可参考代码注释 [esp_codec_dev_vol.h]。
于是跟进“简单“看一下,又花了一晚上补知识 。
ES8311中,0x32寄存器的0-255对应-95.5dB-32.0dB,寄存器值每改变1,对应改变0.5dB。人耳对声音的感知是对数关系,如果我们希望让耳朵感觉调音量大小时是连续变化的,就要按dB来进行改变,虽然寄存器值和DB一一对应,但其中涉及一些计算,需要先按DB计算,并参考一些DB值,最后换成寄存器对应值进行写入。
音量响度的直观感觉有这样一个参考:
| 声压级 (dB SPL) | 对应的声音场景 | 可听性 |
|---|---|---|
| -10 dB | 实验室超静环境 | 极少数人可感知 |
| 0 dB | 人耳最小可听阈值 | 健康年轻人勉强可闻 |
| 10 dB | 树叶沙沙声 | 清晰可闻 |
| 30 dB: | 安静的图书馆 | |
| 60 dB: | 正常对话 | |
| 85 dB: | 长期暴露可能损伤听力 | |
| 120 dB: | 摇滚音乐会前排 |
从ES8311本身来看,达到32dB已经是寄存器的极限了,但在ES8311后面的功放芯片电路(PA)还会产生增益,最后要把软件增益和硬件增益相加才是最终增益,为了不至于换个功率大点的功放就要重写调音量范围的程序,需要考虑用总的增益来设置这个寄存器。做到所见即所得。就是程序里写多少DB,输出就是多少DB(当然是在功放提供的最大范围内)。
硬件电路的增益要测量,具体了解个大概意思就行,我以耳朵感受为准,估个差不多的值。
这其中还有一个匹配的情况,如果DAC输出电压 > PA输入电压:那在软件设置增益时需要衰减,如果DAC输出电压 < PA输入电压:那在软件设置增益时需要放大。
这么说很枯燥,上例子:
本次硬件电路中,ES8311的输出范围是0-3.3V,PA的输入范围是0-5V,因此这两个在匹配时,已经因为电压的原因产生了一个负增益值:
20 * log10(3.5 / 5) = -3.61 dB
因此软件设置时,首先就得增加3.61dB,后面输入时才能达到0dB。
假设PA输出时增益是20,那么理论上寄存器设成-17.39时,通过PA输入,再通过输出,增益就是0dB,简单的数学加法
-17.39 - 3.16 + 20 = 0。
反过来说,如果你想达到最终20dB的响度,那寄存器就要设成3.61,相对于寄存器,后面硬件电路实际提供的增益只有17.39dB。这就是官方说明里那段话的意思。
官方的新组件中通过以下函数来实现硬件总增益计算,供后面校准实际增益用,其中pa_gain是PA的增益,以刚才的例子中电压及PA增益来计算,这个函数的返回值是17.39,先记住这个数,后面分析程序要用。
float esp_codec_dev_col_calc_hw_gain(esp_codec_dev_hw_gain_t *hw_gain)
{
float pa_voltage = hw_gain->pa_voltage;
float dac_voltage = hw_gain->codec_dac_voltage;
if (pa_voltage == 0.0) {
pa_voltage = 5.0;
}
if (dac_voltage == 0.0) {
dac_voltage = 3.3;
}
return 20 * log10(dac_voltage / pa_voltage) + hw_gain->pa_gain;
}
官方新版组件是在初始化ES8311过程时调用了这个函数,保留了这个校准值。
下面具体分析设置音量的函数es8311_set_vol()。
static int es8311_set_vol(const audio_codec_if_t *h, float db_value)
{
audio_codec_es8311_t *codec = (audio_codec_es8311_t *) h;
if (codec == NULL) {
return ESP_CODEC_DEV_INVALID_ARG;
}
if (codec->is_open == false) {
return ESP_CODEC_DEV_WRONG_STATE;
}
//-------------------------------------
db_value -= codec->hw_gain;
int reg = esp_codec_dev_vol_calc_reg(&vol_range, db_value);
ESP_LOGD(TAG, "Set volume reg:%x db:%d", reg, (int) db_value);
return es8311_write_reg(codec, ES8311_DAC_REG32, (uint8_t) reg);
}
从分割线之后开始,参数 db_value 是想要设置的音量响度。
以期望得到的音量减去硬件电路增益,也就是刚才的17.39,得到校准后的增益值;
比如我们希望实现最终45dB的输出,那实际要设的寄存器值是45-17.39=27.61;
调用esp_codec_dev_vol_calc_reg()函数,把这个27.61映射成寄存器值;
最后写入寄存器0x32中。
esp_codec_dev_vol_calc_reg()函数把27.61映射寄存器值的过程如下:
int esp_codec_dev_vol_calc_reg(const esp_codec_dev_vol_range_t *vol_range, float db)
{
if (vol_range->max_vol.db_value == vol_range->min_vol.db_value) {
return vol_range->max_vol.vol;
}
if (db >= vol_range->max_vol.db_value) {
return vol_range->max_vol.vol;
}
if (db <= vol_range->min_vol.db_value) {
return vol_range->min_vol.vol;
}
float ratio =
(vol_range->max_vol.vol - vol_range->min_vol.vol) / (vol_range->max_vol.db_value - vol_range->min_vol.db_value);
return (int) ((db - vol_range->min_vol.db_value) * ratio + vol_range->min_vol.vol);
}
参数 vol_range是提前预设值,就是0x32寄存器能提供的最大范围。
ES8311提供了1-7位,范围是0至255,对应-95.5至32dB。
函数首先限制了dB的范围,也就是说你要设的27.61在-95.5至32这个范围里,从这里可以看出,按例子的参数,最大响度也就输出32+27.61=57.61dB;
然后计算出寄存器值与dB的映射系数是2,即每加减0.5dB,寄存器加减1。
最后计算寄存器值。说真的,真是很烦看到一个数学式子用了这么长的名字来代替,实在看不下去,但这个式子可以通用,将来换个寄存器和DB值是其他对应方式的,改vol_range值就行了,计算公式还可以用。
要是专门优化成ES8311用,核心就一句:
return (int) ((db + 95.5) * 2);
寄存器值应该是246
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)