# JustOneCacophony — 双耳渲染数学 [English](binaural.en.md) · [返回 README](../README.md) 本文说明 `pcm16 + ID11/OAMD → stereo` 路径中的计算、状态和时间对齐。双耳渲染直接接在对象重建之后,不经过 ADM BWF 或 AXML。 ## 1. 总体路径与记号 ```text pcm16:LFE + 15 路对象 PCM → 64-band QMF analysis → 77-band hybrid analysis → 逐对象方向、距离、双耳传递函数和 room send → 对象累加 + room network → hybrid synthesis → QMF synthesis → 961-sample 延迟补偿 → stereo WAV ``` 主要记号: | 符号 | 含义 | |---|---| | $s=0\ldots15$ | 输入源;$s=0$ 为 LFE,$s=1\ldots15$ 为对象 | | $e\in\{L,R\}$ | 左右输出耳 | | $p=0\ldots63$ | QMF phase / 时域 hop 内采样 | | $k=0\ldots63$ | QMF 子带 | | $h=0\ldots76$ | hybrid 子带 | | $j=0\ldots35$ | 方向 basis 项 | | $m$ | 64-sample QMF 时槽 | | $n$ | 时域采样位置 | 每个 QMF hop 为 64 samples,每个双耳控制块为 $$ N_b=512=8\times64, $$ 每个 E-AC-3/JOC 音频帧为 $$ N_f=1536=3N_b. $$ ## 2. 输入与控制块 输入矩阵为 $$ x_s[n],\qquad s=0\ldots15. $$ `pcm16` 的通道约定为: ```text ch0 special LFE ch1..15 JOC 对象 1..15 ``` 渲染器按连续采样流推进。QMF、hybrid、room 和 synthesis 状态不会在 1536-sample 帧边界清零。 ## 3. 64-band QMF analysis 令 $a_{p,\ell}$ 为固定的 64×10 polyphase 系数,$r_{s,\ell,p}[m]$ 为当前和前 9 个 hop 的 phase 历史。奇偶 lag 分别累加: $$ E_{s,p}[m] =\sum_{\substack{\ell=0\\\ell\text{ even}}}^{9} a_{p,\ell}r_{s,\ell,p}[m], $$ $$ O_{s,p}[m] =\sum_{\substack{\ell=0\\\ell\text{ odd}}}^{9} a_{p,\ell}r_{s,\ell,p}[m]. $$ 对任一 64-vector $v[p]$,定义调制变换 $$ \mathcal Q(v)_k = \operatorname{FFT}_{128} \left( \left[v[p]e^{-j\pi p/128}\right]_{p=0}^{63}, 0_{64} \right)_k e^{-j3\pi(k+1/2)/128}. $$ analysis 输出为 $$ X_{s,k}[m] = \mathcal Q(O_s)_k +j(-1)^k\mathcal Q(E_s)_k. $$ 所有历史、乘加和 FFT 结果使用 `float64/complex128`。 ## 4. 77-band hybrid analysis 低 3 个 QMF 子带使用 13-slot FIR 拆分为 16 个 hybrid bands。把复数的实部和虚部分量记为 $i,o\in\{0,1\}$,固定核为 $K_{p,i,\ell,h,o}$: $$ H_{s,h,o}[m] = \sum_{p=0}^{2} \sum_{i=0}^{1} \sum_{\ell=0}^{12} X_{s,p,i}[m-\ell]K_{p,i,\ell,h,o}, \qquad h=0\ldots15. $$ 其余 61 个 hybrid bands 是 QMF 3..63 的 6-slot 延迟: $$ H_{s,16+q}[m]=X_{s,3+q}[m-6], \qquad q=0\ldots60. $$ 因此 hybrid vector 的顺序为: ```text 0..15 低 3 个 QMF bands 的细分 16..76 延迟后的 QMF bands 3..63 ``` ## 5. OAMD 坐标与时间轴 ### 5.1 Q15 坐标到 Cartesian 对象状态中的 $q_1,q_2,q_3$ 先恢复到离散位置网格: $$ u_1=\min\left(1,\frac{\operatorname{round}(62q_1/32767)}{62}\right), $$ $$ u_2=\min\left(1,\frac{\operatorname{round}(62q_2/32767)}{62}\right), $$ $$ u_3=\operatorname{clip}\left( \frac{\operatorname{round}(15q_3/32767)}{15},-1,1\right). $$ ADM Cartesian 坐标为 $$ (X,Y,Z)=(2u_1-1,\ 1-2u_2,\ u_3). $$ ### 5.2 更新时间 一条位置更新的编码时刻为 $$ n_{\mathrm{coded}} =n_{\mathrm{frame}} +n_{\mathrm{outer}} +n_{\mathrm{block}}. $$ 首个有效状态作为 sample 0 的初始位置。后续更新加入对象 PCM 延迟 $D_o$,默认 $$ D_o=1473. $$ 若 ramp duration 为 $R>64$,连续运动为 $$ n_{\mathrm{start}}=n_{\mathrm{coded}}+D_o+64, $$ $$ R_{\mathrm{eff}}=R-64, $$ $$ \mathbf p[n] =(1-\alpha)\mathbf p_0+\alpha\mathbf p_1, \qquad \alpha=\frac{n-n_{\mathrm{start}}}{R_{\mathrm{eff}}}. $$ 当 $R\le64$ 时,目标位置在 $n_{\mathrm{coded}}+D_o$ 直接生效。 Rosella 参数在每个 512-sample block 起点求值,并用于该块的 8 个 hybrid slots。 ## 6. 距离 profile 与方向 普通对象只使用 Near、Mid、Far 三个 profile。每个 profile 包含: - 三轴负/正边界 $b_{x-},b_{x+},b_{y-},b_{y+},b_{z-},b_{z+}$; - 距离尺度 $D$ 和倒数尺度 $D^{-1}$; - 三轴内部尺度 $a_x,a_y,a_z$; - 最小归一化半径 $\rho_{\min}$。 Cartesian 坐标经过 Q15 metadata grid 后换成内部前、侧、上轴,乘以 profile 尺度: $$ \mathbf s=(a_zq_f,\ a_xq_l,\ a_yq_v). $$ 若射线超出 profile 边界,则用单一比例 $\lambda\le1$ 缩放: $$ \mathbf s' = \lambda\mathbf s. $$ 随后 $$ \rho=\|\mathbf s'\|_2, \qquad \rho_c=\max(\rho,\rho_{\min}), \qquad \alpha=\frac{\rho}{\rho_c}, $$ $$ \mathbf d= \begin{cases} \mathbf s'/\rho,&\rho>0,\\ (1,0,0),&\rho=0, \end{cases} $$ 物理半径为 $$ R=D\rho. $$ ## 7. 36 项方向 basis 方向 $\mathbf d=(x,y,z)$ 被展开为 36 项实值多项式: $$ \mathbf b(\mathbf d)= [1,x,y,z,x^2-\tfrac13,xy,xz,y^2-\tfrac13,yz,\ldots]^T. $$ 完整顺序由 `rosella_model.direction_basis()` 固定。最高次数为 5;所有 field 系数必须按该顺序点积,不能交换 basis 项。 逐耳 basis 会根据耳偏移重新归一化。令耳偏移标量为 $e$: $$ \epsilon=\frac{eD^{-1}}{\rho_c}, $$ $$ \mathbf d_-= \frac{(x,y-\epsilon,z)}{\|(x,y-\epsilon,z)\|_2}, \qquad \mathbf d_+= \frac{(x,y+\epsilon,z)}{\|(x,y+\epsilon,z)\|_2}. $$ ## 8. 逐耳路径与 ITD 归一化路径长度为 $$ \ell_-=\rho_c\sqrt{x^2+(y-\epsilon)^2+z^2}, $$ $$ \ell_+=\rho_c\sqrt{x^2+(y+\epsilon)^2+z^2}. $$ 模型允许通过 36-vector 对路径加入非负方向修正: $$ \ell'_e = \ell_e + \max(\mathbf v_e^T\mathbf b_e,0)\,2cD^{-1}. $$ 耳间延迟为 $$ \tau =|\ell'_+-\ell'_-|\,D\frac{f_s}{343.3}\alpha, \qquad f_s=48000. $$ 路径较长的一耳应用 hybrid-band 相位: $$ P_h=e^{j\omega_h\tau}, $$ 其中 $\omega_h$ 由模型的 20 个 hybrid group 参数递推到 77 个 bands。 ## 9. 方向 field 与直达增益 左右耳各有一个 77×36 complex field: $$ C_{e,h}(\mathbf d_e) = \sum_{j=0}^{35}F_{e,h,j}b_j(\mathbf d_e). $$ 另一次耳路径计算给出左右权重: $$ w_L=\frac{\ell_+}{\sqrt{\ell_-^2+\ell_+^2}}, \qquad w_R=\frac{\ell_-}{\sqrt{\ell_-^2+\ell_+^2}}. $$ 有效距离为 $$ R_e=\rho\,s_dD, $$ 其中 $s_d$ 为模型距离标量。Mid/Far 的公共衰减和 room send 为 $$ g_c=\frac{1}{\sqrt{1+s_rR_e^2}}, $$ $$ g_{\mathrm{room}}=R_eg_c. $$ Near 使用 $$ g_c=1, \qquad g_{\mathrm{room}}=0. $$ 令 $C_{e,h}^{(0)}$ 为 field 的第 0 个 basis 系数,中心保护项为 $1-\alpha$。普通直达传递函数可写成 $$ G_{L,h} =g_c\left[C_{L,h}w_L\alpha+C_{L,h}^{(0)}c_L(1-\alpha)\right], $$ $$ G_{R,h} =g_c\left[C_{R,h}w_R\alpha+C_{R,h}^{(0)}c_R(1-\alpha)\right]. $$ $c_L,c_R$ 由模型的耳权重配置选择;路径较长的一耳再乘 $P_h$。 ## 10. LFE 传递函数 LFE 不进入普通对象方向计算。其传递函数为 $$ G_{L,h}^{\mathrm{LFE}}=G_{R,h}^{\mathrm{LFE}}= \begin{cases} g_h,&0\le h<16,\\ 0,&16\le h<77. \end{cases} $$ 前 16 个固定系数为 ```text 2.60290003, 1.80741799, 0.659342408, -0.0275855921, -0.105803289, -0.0699509233, 0.0749056414, -0.00919809937, 0.00349014648,-0.0158600751,-0.000723021978,0.00188189559, -0.000421735429,0.0000329252762,0.0000317397971,0.000000580376991 ``` LFE 的 room send 恒为 0。 ## 11. 对象累加与 room input 每个 hybrid slot 的直达输出为 $$ Y^{\mathrm{direct}}_{e,h} = \sum_{s=0}^{15}H_{s,h}G_{s,e,h}. $$ room 输入为 $$ U_h = \sum_{s=1}^{15}H_{s,h}g_{\mathrm{room},s}. $$ LFE 不进入该和式。 ## 12. Room network room 只处理前 64 个 hybrid bands。输入先乘 $$ g_0=0.70710677. $$ 对每级 all-pass,设延迟样本为 $d[n]$、系数为 $a$: $$ r[n]=x[n]-ad[n], $$ $$ y[n]=ar[n]+d[n]. $$ all-pass 输出复制到 4 个 FDN branches。设延迟输出为 $\mathbf d_h[m]$、4×4 混合矩阵为 $M$: $$ \mathbf b_h[m] =U_h[m]\mathbf 1+M\mathbf d_h[m]. $$ 每个 branch 使用复反馈系数 $f_{h,i}$: $$ m_{h,i}[m]=f_{h,i}b_{h,i}[m]. $$ 主 tap、可选额外 tap 和左右输出矩阵合成为 $$ Y^{\mathrm{room}}_{e,h}[m] = \sum_{i=0}^{3}O_{e,h,i}z_{h,i}[m]. $$ 最终 hybrid 输出为 $$ Y_{e,h}=Y^{\mathrm{direct}}_{e,h}+Y^{\mathrm{room}}_{e,h}. $$ Python 后端把该递归网络展开为有限 complex FIR 并使用 overlap-add;C++ 后端直接保持递归状态。两者均跨帧连续。 ## 13. Hybrid synthesis hybrid synthesis 是 154 项稀疏即时映射。令映射项为 $(h,i,k,o,w)$,其中 $i,o$ 表示实部或虚部,则 $$ Q_{e,k,o}[m] \mathrel{+}= Y_{e,h,i}[m]w. $$ 输出是每耳 64 个 complex QMF bands。 ## 14. QMF synthesis 每耳 QMF vector 先展开为 128 项实向量 $$ \mathbf q_e=[\Re Q_{e,0},\Im Q_{e,0},\ldots,\Re Q_{e,63},\Im Q_{e,63}]^T. $$ 对每个 phase $p$ 和 rank $r=0\ldots3$: $$ f_{e,p,r}[m] =\mathbf b_{p,r}^T\mathbf q_e[m]. $$ 使用 10-slot taps 合成时域样本: $$ y_e[64m+p] = \sum_{\ell=0}^{9} \sum_{r=0}^{3} t_{p,\ell,r}f_{e,p,r}[m-\ell]. $$ ## 15. 延迟、尾声和输出 完整 filterbank 的固定延迟为 $$ L=961\ \text{samples}. $$ 只在连续流起点丢弃一次前 $L$ 个输出 samples。输入结束后继续送零,以释放 QMF、hybrid 和 room 状态。尾声裁切只作用于文件末端: $$ \max(|y_L[n]|,|y_R[n]|) > 10^{-8} $$ 的最后一个 sample 被保留,同时输出长度不得短于源 PCM 长度。 所有内部状态、参数计算和对象累加使用 `float64/complex128`。最终 writer 才转换为 float32 或 PCM24。 ## 16. Python 与 C++ 后端 两套后端共享: - 同一份 QMF/hybrid 固定表; - 同一份模型解析结果; - 同一套 512-sample 参数更新时间轴; - 同一组逐对象 complex gains 和 room sends; - 同一 961-sample 延迟补偿与尾声策略。 C++ 后端以 512-sample block 为处理单位,内部持有 QMF、hybrid、room 和 synthesis 状态。Python 只负责模型解析、OAMD 时间轴和每块参数更新。 ## 17. 模型文件 默认路径为 ```text HRTF/binaural.personalized_headphone ``` 也可通过 ```text --personalized-headphone PATH ``` 指定其它文件。 `.personalized_headphone` 中的 int32/Q15 参数在解析后提升为 float64。任意 SOFA FIR 不能只通过数组重排变成该参数模型;若要转换,需要拟合方向 fields、ITD、距离 profile、耳几何和 room 参数。 ## 18. 适用范围 当前路径处理 15 个普通点对象和 1 路 special LFE。对象 extent、spread、diffuse、divergence、channel lock,以及未实现的 OAMD element 变体不在本公式范围内。