真机现象:音量设 10 仍然满响度——vol_apply() 的 BT 分支 logmsg + return,
g_vol_pct 不更新、AVRCP 被 bt_vol_full() 钉在 127、写路径无任何软件缩放,
于是 BT 永远在耳机自己的满音量。
- 新增 vol_gain.{c,h}(纯整数,无 libm/软浮点):增益表由 dac_vol_set 的同一个
衰减寄存器函数 vol_gain_dac_att_reg() 推出(0.5dB/步、平方锥度),
Q31 迭代乘法开机算一次;vol=100 逐字节不变,vol=0 全 0(memset 快路)。
- dev_write(BT 路径)逐样本乘 + 32 位中间量 + 饱和;增益恒 <=1 故不可能削波。
- 改音量走 512 样本线性斜坡(Bresenham 余额分摊,末步不补差),防爆音。
- vol_apply() BT 分支改为更新 g_vol_pct + 持久化;DAC numid=1/2/11 仍不写
(M50 T11 隔离断言不变)。
- dac_vol_set() 改用 vol_gain_dac_att_reg(),DAC/BT 两条路锥度不可能漂移。
- 测试:test_vol_gain(曲线/缩放/斜坡纯函数断言)+ T11 改为断言
「BT 音量不写 DAC 寄存器」+ 新增 T18(wav 捕获音量阶梯 RMS/峰值)
/ T19(活改音量不爆音、不丢帧)。
117 lines
4.5 KiB
C
117 lines
4.5 KiB
C
/* vol_gain.c — 纯整数增益表 + 缩放/斜坡(宿主与目标机共用)。见 vol_gain.h 的定性说明。 */
|
||
#include "vol_gain.h"
|
||
|
||
#include <string.h>
|
||
|
||
/* 每衰减步 0.5dB 的振幅系数:10^(-0.5/20) = 10^(-0.025) = 0.9440608762859234。
|
||
* Q31 = round(0.9440608762859234 × 2^31) = 2027355295(离线算出并钉死;运行期不碰浮点)。 */
|
||
#define VOL_GAIN_STEP_Q31 2027355295u
|
||
|
||
static int32_t s_gain_q15[256]; /* 下标=衰减寄存器值;只读(init 之后) */
|
||
static int s_gain_ready = 0;
|
||
|
||
/* 衰减步 → 增益的整数迭代:acc 以 Q31(15 位整数 + 16 位小数)表示 Q15 增益,
|
||
* 每步 acc = acc × STEP >> 31。255 步累计相对误差 ~2^-23,实测与 pow() 相差 ≤1 LSB。 */
|
||
void vol_gain_init(void)
|
||
{
|
||
if (s_gain_ready) return;
|
||
uint64_t acc = (uint64_t)VOL_GAIN_UNITY << 16;
|
||
for (int reg = 0; reg < 256; reg++) {
|
||
int32_t g = (int32_t)((acc + 32768u) >> 16);
|
||
if (g > VOL_GAIN_UNITY) g = VOL_GAIN_UNITY;
|
||
s_gain_q15[reg] = g;
|
||
acc = (acc * VOL_GAIN_STEP_Q31 + (1u << 30)) >> 31;
|
||
}
|
||
s_gain_ready = 1;
|
||
}
|
||
|
||
/* dac_vol_set() 的锥度原样搬来(平方锥度;255=-127.5dB)。**单一出处**:dac_vol_set 与
|
||
* BT 增益表都取这个值,谁都不许自己再算一遍,否则 DAC/BT 曲线会各走各的。 */
|
||
int vol_gain_dac_att_reg(int pct)
|
||
{
|
||
if (pct <= 0) return 255; /* 真静音 */
|
||
if (pct > 100) pct = 100;
|
||
int reg = (100 - pct) * (100 - pct) * 192 / 10000;
|
||
if (reg > 255) reg = 255;
|
||
if (reg < 0) reg = 0;
|
||
return reg;
|
||
}
|
||
|
||
int vol_gain_q15(int pct)
|
||
{
|
||
if (!s_gain_ready) vol_gain_init(); /* 未初始化就查=防御(正常由 main 先 init) */
|
||
if (pct <= 0) return 0; /* vol=0 必须真静音,不给「很小声」留余地 */
|
||
return s_gain_q15[vol_gain_dac_att_reg(pct)];
|
||
}
|
||
|
||
int16_t vol_gain_scale_s16(int16_t in, int g_q15)
|
||
{
|
||
int32_t v = (int32_t)in * g_q15; /* |in|≤32768 × g≤32768 → |v|≤2^30,int32 不溢出 */
|
||
v = (v + (1 << 14)) >> 15; /* 四舍五入回 S16 量程(单位增益时恒等) */
|
||
if (v > 32767) return 32767;
|
||
if (v < -32768) return -32768;
|
||
return (int16_t)v;
|
||
}
|
||
|
||
void vol_gain_ramp_snap(vol_gain_ramp_t *r, int pct)
|
||
{
|
||
r->cur_q15 = r->tgt_q15 = vol_gain_q15(pct);
|
||
r->step_q15 = 0;
|
||
r->bump = 0;
|
||
r->bump_left = 0;
|
||
r->left = 0;
|
||
}
|
||
|
||
void vol_gain_ramp_set(vol_gain_ramp_t *r, int pct, int ramp_samples)
|
||
{
|
||
int32_t tgt = vol_gain_q15(pct);
|
||
if (tgt == r->tgt_q15) return; /* 目标没变:不重置进行中的斜坡 */
|
||
r->tgt_q15 = tgt;
|
||
int32_t d = tgt - r->cur_q15;
|
||
if (ramp_samples <= 0 || d == 0) {
|
||
r->cur_q15 = tgt;
|
||
r->step_q15 = 0;
|
||
r->bump = 0;
|
||
r->bump_left = 0;
|
||
r->left = 0;
|
||
return;
|
||
}
|
||
/* 线性斜坡 = 固定步进 + 余额分摊(不除法的 Bresenham):
|
||
* n 个样本走完 d,step=d/n 截断,余数 rem 按 ±1 分摊到前 |rem| 个样本。
|
||
* 好处:**最后一步不会突然补差**(旧版 step 截断后末尾要一次跳 500+ LSB = 爆音),
|
||
* 全程每样本增益变化 ≤ |step|+1,且终点精确等于目标(无过冲)。 */
|
||
int32_t ad = (d > 0) ? d : -d;
|
||
int32_t n = ramp_samples;
|
||
if (n > ad) n = ad; /* 增益差比斜坡短:缩短斜坡(步进仍至少 1 LSB) */
|
||
int32_t step = d / n;
|
||
int32_t rem = d - step * n; /* |rem| < n,与 d 同号 */
|
||
r->left = n;
|
||
r->step_q15 = step;
|
||
r->bump = (rem > 0) ? 1 : -1;
|
||
r->bump_left = (rem > 0) ? rem : -rem;
|
||
}
|
||
|
||
void vol_gain_ramp_apply(vol_gain_ramp_t *r, uint8_t *buf, size_t nbytes)
|
||
{
|
||
size_t ns = nbytes / 2;
|
||
if (!buf || !ns) return;
|
||
if (r->left <= 0) {
|
||
/* 稳态快路:单位增益→原样返回(vol=100 逐字节不变);静音→memset 全 0(vol=0 真静音) */
|
||
if (r->cur_q15 == VOL_GAIN_UNITY) return;
|
||
if (r->cur_q15 == 0) { memset(buf, 0, ns * 2); return; }
|
||
}
|
||
for (size_t i = 0; i < ns; i++) {
|
||
int32_t g = r->cur_q15;
|
||
if (r->left > 0) {
|
||
r->cur_q15 += r->step_q15;
|
||
if (r->bump_left > 0) { r->cur_q15 += r->bump; r->bump_left--; }
|
||
if (--r->left == 0) r->cur_q15 = r->tgt_q15; /* 兜齐(理论上已精确相等) */
|
||
g = r->cur_q15;
|
||
}
|
||
int16_t s;
|
||
memcpy(&s, buf + i * 2, 2); /* 字节存取:不假设缓冲对齐、不违反严格别名 */
|
||
s = vol_gain_scale_s16(s, g);
|
||
memcpy(buf + i * 2, &s, 2);
|
||
}
|
||
}
|