Files
MeowOS-For-R3P2/patch_v2/meow_audiod/vol_gain.c
T
AskaEth e37c622597 feat(audiod): BT 输出音量软件增益(曲线与 DAC 同源)+ vol_apply BT 分支真生效
真机现象:音量设 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(活改音量不爆音、不丢帧)。
2026-11-12 08:07:37 +08:00

117 lines
4.5 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* 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);
}
}