首页 / 资讯中心 / 文章详情

pku-hrtf-lib实战指南:HRTF双耳渲染全流程解析

pku-hrtf-lib实战指南:HRTF双耳渲染全流程解析 ★ FEATURED ARTICLE
简介这份 pku-hrtf-lib 压缩包面向音频开发者与虚拟现实研究者提供北京大学 HRTF头部相关传递函数相关的头文件、C 源程序与实现滤波器逻辑的工程代码可帮助用户快速接入 HRIR 卷积处理实现声源空间定位与三维音频渲染。压缩包共 14 个文件以 h 头文件为主配合 c 源码及多个修订版本文件整体仅 2.21MB结构轻量便于本地编译、对照与二次开发。已有 341 人学习下载。资源内含 pku_hrtf_lib.c 与 pku_hrtf_filter.h 等核心模块可用于学习 HRTF 数据组织方式、滤波器接口设计以及头相关脉冲响应在双耳渲染中的应用对数字信号处理和 C/C 编程有一定基础的读者可借助这些代码快速搭建 HRTF 测试程序并基于库中的接口扩展不同方位角、高度角的定位实验。1. pku-hrtf-lib 是什么一个能在本地跑通虚拟声场的起点做 VR、游戏空间音频或者单纯想在耳机里还原声音从左后方一米处传来最典型的挫败感是明明左右声道都有信号声像却总在头顶内侧打转前后感和上下感完全出不来。原因就是少了头相关传输函数HRTF这一层物理滤波。pku-hrtf-lib 是北京大学公开的双耳 HRTF 测量库解压出来是一批按方位角、仰角组织的 HRIR 数据文件常见后缀是 .wav 或 .mat。它解决的是用一台普通电脑和一副耳机也能跑通真实测听级双耳渲染的问题。适合音频算法工程师、游戏音频开发者和做主观听音实验的研究生。这篇笔记从文件结构一路讲到渲染插值、参数配置和排错让你能照着把最小双耳渲染系统跑起来。2. 先用懂再上手HRTF 的测量口径与 pku-hrtf-lib 的文件结构从 pku-hrtf-lib.rar 解压之后通常第一眼看到的是按仰角分层的目录或者一堆编号复杂的 .wav 文件。先别急着写加载代码因为 HRTF 库的测量口径和文件格式决定你后面所有渲染代码怎么写。读数据的方式分两类时域 HRIR 和频域 HRTF。很多教程只讲其中一种等你换到另一个包时就不知道怎么处理了。这一章先把数据表示、坐标系统和命名规则讲透。2.1 从 HRIR 到 HRTF两种文件格式对应两种算法路径HRIR头相关冲激响应是时域信号做法是把单声道音源和它做卷积HRTF 是频域复值响应做法是把信号变换到频域后做乘法。PKU 这类测量库通常以 HRIR 形式发布因为 .wav 可以直接戴耳机试听方便快速验证。如果你拿到的是 .mat 大文件那里面存的可能是频域矩阵也可能是时域脉冲字段名因版本而异。常见字段有 fs、hrtf_left、hrtf_right、position但别指望一定叫这个。先跑一个探测脚本把 .mat 里所有非私有字段打出来from scipy.io import loadmat m loadmat(pku_hrtf.mat) for key in m.keys(): if key.startswith(__): continue data m[key] print(key, data.shape, data.dtype)看到复数且 shape 类似 (n_freq, n_azimuth, n_elevation) 的就是频域 HRTF看到实数且第一维几百、第二维几十的多半是时域 HRIR。如果是频域 HRTF用 numpy 的 ifft 转回时域np.fft.irfft(spec, n512)。我一般建议先把数据统一转成 HRIR 再进渲染管线因为时域卷积对后续的插值、裁剪和 AB 听感测试更直观。要注意的是同一个包有时会同时给 .wav 和 .mat两边内容不一致时以 .wav 为准。因为 .mat 里的频域数据可能做了平滑或裁剪而 .wav 更像原始测量。如果你解码 .wav 后发现一组左右耳波形完全一致大概率是压缩包制作时把单声道复制了两份这种数据不能用于定位别浪费时间。2.2 打开 rar 之后先别急着读数据先把坐标系和图谱对齐HRTF 库必须定义方位角从哪个方向开始、顺时针还是逆时针、仰角正负方向。PKU 这套数据常见约定是正前方为 0°从头顶往下看顺时针增加到 360°仰角正向上、负向下但同类库 CIPIC 和部分欧洲库用的是 -90° 到 90° 的方位角定义如果不确认后面渲染的声像方位会整体偏移。快速确认方法是用 0° 方位角的 HRIR 渲染一个短脉冲比如 0.5ms 的咔哒声戴耳机听它是否出现在正前方。如果明显偏左或偏右就要查左右耳是否装反或者方位角定义是从左耳方向为 0°。若偏转角度是固定的可以在加载后统一加个偏移常量。坐标对齐在代码里就是一段角度换算# 把 0~360 的方位角转成 -180~180方便和游戏引擎的方向角对齐 az az_original if az_original 180 else az_original - 360为什么不直接用 0~360因为渲染引擎里常用度/秒描述角速度-180~180 在跨越正后方边界±180时不会出现跳变而 0~360 在 0°/359.5° 边缘会产生从 359 突变到 0 的问题测听时会听到声像从一个方向突然跳回另一个方向。图谱对齐指的是不同角度下 HRTF 在 4~10kHz 的谷和峰位置不一样。能听到的高度感和前后感主要靠这些谱特征。你可以在预处理阶段算一下 30° 和 90° 的平均频谱差异确认高频段有明显区分度。如果拿到 0° 和 180° 的 HRTF 频谱几乎一样这组数据的耳廓测量可能没做好需要谨慎使用。2.3 文件命名与元数据角度编码其实很直白PKU 库的文件命名一般会把方位角、仰角直接写进文件名常见的是az030_ele000_L.wav这种也有把仰角放目录名的。字段拆解如下文件片段含义az000方位角 0°即正前方az030方位角 30°按约定向右az330方位角 330°等效于 -30°向左ele000仰角 0°水平面ele-030仰角 -30°水平面以下_L / _R左耳 / 右耳 HRIR如果包内还有一个 hrtf.mat建议把字段名、采样率、测量距离记录到一个 metadata.json 里后续重采样和近场补偿都要用。测量距离很关键多数 HRTF 库在 1 米距离下测量如果你的应用场景是 0.2 米近场必须做近场补偿否则声像距离感会全错。解析文件名时用正则比手写 split 更稳import re m re.search(raz([-]?\d)_ele([-]?\d), filename) if m: az int(m.group(1)) elev int(m.group(2))这里把正负号也考虑进去因为有些版本把左方 30° 直接写成az-030而不是az330。两种风格你都可能在包里遇到所以加载函数必须同时兼容。3. 把 pku-hrtf-lib 解析成可用数组Python 加载与预处理脚本这一章写实际解析脚本。先用 Python 做数据清洗和验证等算法跑通再移植到 C 或 Rust。解析层不要追求实时先把数据读干净、统一格式、排除损坏文件。我会把每个步骤都拆开你按顺序执行就能得到一个纯净的 HRIR 数据集。3.1 检测实际格式先看文件头再决定用 scipy 还是 soundfile解压后先别直接sf.read先看一下文件结构和文件头因为有些扩展名是 .wav 的其实是裸 PCM。用find和file命令做一次摸底cd pku-hrtf-lib find . -type f | head -30 file $(find . -name *L.wav | head -1) python3 -c import soundfile as sf; print(sf.info($(find . -name *L.wav | head -1)))第一行列出所有文件第二行用 file 检查第一个左耳文件的真实格式第三行打印采样率、长度、通道数。如果 file 输出是 RIFF (little-endian) data, WAVE audio那正常如果输出是 data 或者 PCM说明是裸 PCM需要用wave模块或 numpy 从字节流读取。soundfile.info 返回的采样率是 44100 还是 48000 一定要记下来后面重采样以这个为准不要看文件名里的注释。如果包里有 .mat 文件用下面的脚本探测字段python3 - PY from scipy.io import loadmat m loadmat(hrtf.mat) for k, v in m.items(): if not k.startswith(__): print(k, v.shape, v.dtype) PY你可能会看到类似hrtf_l、hrtf_r的数组也可能是ir_left。如果数组 shape 是 (512, 1442)那第一维可能是 FFT 频点第二维是角度数。此时建议打印一下数组的元素范围复数模长接近 1 左右是频域 HRTF实数值很小且前几十个点有明显脉冲形状的是时域数据。3.2 写一个 load_pku_hrtf 函数输出双耳脉冲与方位角确认格式后写一个通用的加载函数。我习惯让它递归查找所有*_L.wav然后尝试配对*_R.wav最后用正则解析角度。代码如下import glob import os import re import numpy as np import soundfile as sf def load_pku_hrtf(rootpku-hrtf-lib): entries [] pattern os.path.join(root, **, *_L.wav) for lpath in sorted(glob.glob(pattern, recursiveTrue)): rpath lpath.replace(_L.wav, _R.wav) if not os.path.exists(rpath): print(missing right ear:, rpath) continue left, sr_l sf.read(lpath, dtypefloat32) right, sr_r sf.read(rpath, dtypefloat32) if sr_l ! sr_r: print(sample rate mismatch:, lpath) continue base os.path.basename(lpath) m re.search(raz([-]?\d)_ele([-]?\d), base) if not m: print(cannot parse angle from:, base) continue az int(m.group(1)) elev int(m.group(2)) entries.append({ azimuth: az, elevation: elev, left: left, right: right, sr: sr_l, }) return entries逻辑说明glob.glob递归找到所有左耳文件再用replace(_L.wav, _R.wav)推断右耳路径。这个做法依赖左右耳命名只差一个字母如果包内是left_000.wav这种命名就把 replace 改成对应规则。解析角度用正则避免因某个文件多一个下划线导致 int 解析失败。最后把左右耳波形和角度放进 dict。这段代码里最重要的参数是root解压路径不要有中文和空格否则 soundfile 在 Windows 上容易抽风。采样率不一致的文件直接跳过并在控制台打警告不要自动重采样因为自动重采样会掩盖一部分数据损坏问题。3.3 数据预处理去直流、截断、归一化与转频域数据读进来之后不能直接拿去渲染。测量设备可能引入直流偏置、房间反射尾音、文件响度不一致。预处理函数要统一做四件事去直流、截断、淡出、归一化。def preprocess_ir(ir, target_len512, fs_in44100, fs_out48000): # 重采样先用线性插值顶替工程验证后再换高质量重采样器 if fs_in ! fs_out: x_old np.arange(len(ir)) x_new np.linspace(0, len(ir), int(len(ir) * fs_out / fs_in), endpointFalse) ir np.interp(x_new, x_old, ir) # 去直流取前 16 个采样的均值作为直流偏移 ir ir - np.mean(ir[:16]) # 截断或补零到固定长度 if len(ir) target_len: ir ir[:target_len] else: ir np.pad(ir, (0, target_len - len(ir))) # 后段淡出避免截断造成咔哒声 fade_len min(128, target_len // 8) fade np.ones(target_len) fade[-fade_len:] np.linspace(1.0, 0.0, fade_len) ir ir * fade # RMS 归一化到 0.5让左右耳响度一致 rms np.sqrt(np.mean(ir ** 2)) 1e-10 ir ir * (0.5 / rms) return ir.astype(np.float32)逻辑说明重采样用np.interp只是权宜之计如果后续要做严谨实验建议用 libsamplerate 或 soxr 的 sinc 重采样线性插值会在高频产生镜像噪声。去直流不能用减去整个波形均值因为 HRIR 后半段可能混入房间低频驻波前 16 个采样能更准确反映采集链路本身的直流偏移。截断到 512 点在 48kHz 下约 10.7ms对 HRIR 来说足够覆盖头部和耳廓的主要滤波过程又能甩掉大部分测量房间的反射尾音。归一化时用 RMS 而不是峰值因为 HRIR 的首个脉冲峰值可能因角度不同差异很大用峰值归一化会把 0° 和 90° 的音量差距抹平反而破坏方向感里的响度线索。RMS 归一化到 0.5 后续渲染时不容易削波。如果想转成频域 HRTF 用于频域处理from numpy.fft import rfft def hrir_to_hrtf(ir, fft_len1024): spec rfft(ir, nfft_len) return specfft_len至少要两倍于 HRIR 长度避免卷积时出现时域混叠。这个函数在插值渲染时很有用。3.4 检查数据的正确姿势脉冲响应可视化与能量检查预处理完以后先别急着接渲染花两分钟看一眼数据有没有明显异常。这段检查代码能帮你避免后面白做一天的实验。import matplotlib.pyplot as plt def check_hrtf_pair(entry, sr): left entry[left] right entry[right] plt.figure(figsize(8, 4)) t np.arange(len(left)) / sr * 1000 plt.plot(t, left, labelL) plt.plot(t, right, labelR) plt.xlabel(time (ms)) plt.ylabel(amplitude) plt.grid(True) plt.legend() plt.title(faz{entry[azimuth]}, elev{entry[elevation]}) plt.show() # 互相关算 ITD 粗测 corr np.correlate(left, right, modefull) lag np.argmax(corr) - len(left) 1 itd_ms lag / sr * 1000 print(fITD {itd_ms:.3f} ms)逻辑说明健康的 HRIR 波形应该是在最初 1~2ms 有一个明显主脉冲之后快速衰减并带有几个小波动后续尾部不会出现大幅度震荡。90° 方位角时右耳比左耳早到ITD 大约是 0.3~0.8ms也就是 0.6 米头宽对应的双耳延时。如果 ITD 超过 2ms说明采样率标错或声道配对有问题。另外检查左右耳能量差。对于 90° 方位角右侧声源到达左耳时头部遮挡明显左耳整体能量会比右耳低 5~10dB。如果两个波形形状和量级都几乎一样这组数据多半是复制出来的定位能力会很弱。遇到这种文件直接从数据集里删掉不要抱侥幸心理。4. 实时渲染落进音频引擎插值策略与最小实现数据加载和预处理完成以后剩下的是渲染路径选择。HRTF 库的测量角度通常是离散的间隔 10° 或 15°而实际声源可以出现在任意角度。你需要确定插值策略并把它落进一个处理器里。这一章会讲清楚从查表到卷积、再到近场和混响补偿的最小实现。4.1 方位角离散网格到任意方向最近邻与线性插值的取舍最简单的做法是最近邻查找把目标方位角归到最近的测量角度直接用那组 HRIR。优点是零计算量缺点是声源转动时在角度边界处会产生明显的啪声和定位跳变。线性插值是默认方案对左右耳 HRIR 分别做加权混合混合系数由目标角度在两个相邻测量点之间的位置决定。时域直接插值 HRIR 虽然实现简单但会破坏 HRTF 相位中的耳间时间差ITD信息导致声像模糊。常见做法是在频域插值幅度谱相位用线性延时单独补偿。def interpolate_hrtf_spec(spec1, spec2, alpha): # spec1, spec2 是相邻角度的 HRTF 复数谱 mag1 np.abs(spec1) mag2 np.abs(spec2) phase1 np.angle(spec1) phase2 np.angle(spec2) # 对幅度谱做线性插值 mag mag1 * (1 - alpha) mag2 * alpha # 相位差做角度插值注意要处理环绕 phase_diff phase2 - phase1 phase_diff np.angle(np.exp(1j * phase_diff)) # 包络到 [-pi, pi] phase phase1 phase_diff * alpha return mag * np.exp(1j * phase)逻辑说明alpha是目标点离第一个点的比例介于 0 和 1 之间。相位不能直接做普通线性插值因为相位在 ±π 边界会跳变所以先把相位差转换成一个复平面上的单位向量取角度后再插值这样包络后的相位差是连续变化的。这个函数输出的复数谱用np.fft.irfft就能转回 HRIR。这个插值方式在离线渲染里够用但要实时跑就费劲。我一般在实时引擎里离线把所有角度按 5° 间隔预计算好运行时只查表完全不做插值。这样既避免了插值算法的性能消耗又不会出现跳变。4.2 在 Python 里做逐采样点卷积先跑通再优化算法验证阶段没必要上分块卷积直接调fftconvolve就行。最小渲染函数可以写成from scipy.signal import fftconvolve def render_with_hrtf(mono, left_ir, right_ir): out_left fftconvolve(mono, left_ir, modefull)[:len(mono)] out_right fftconvolve(mono, right_ir, modefull)[:len(mono)] return np.stack([out_left, out_right], axis1)逻辑说明modefull会输出len(mono) len(ir) - 1个采样截到len(mono)是为了和输入对齐。这个截断会造成卷结尾的衰减丢失但由于 HRIR 已经做过淡出末尾能量非常低听感上损失忽略不计。fftconvolve内部对短音频输入长度很敏感如果 mono 只有几十个采样fft 反而比直接卷积慢但对于整段语音、音乐测试足够了。如果要模拟声源连续移动不能每帧直接切换 HRIR否则会像之前说的那样产生咔哒声。常见的做法是做交叉淡化def crossfade_irs(old_ir, new_ir, fade_len512): ramp np.linspace(0, 1, fade_len) out np.copy(old_ir) out[-fade_len:] old_ir[-fade_len:] * (1 - ramp) new_ir[:fade_len] * ramp return out交叉淡化长度fade_len我一般取 10~20ms也就是 480~960 个采样。太短能听到切换痕迹太长会让声像移动变得迟滞模糊。注意这里假设两次 HRIR 的起始相位已经对齐否则交叉淡化过程中会出现梳状滤波听起来像移动时的金属声。因此实际工程中建议不要对原始 HRIR 做交叉淡化而是对插值后的 HRIR 做并且每次插值前把两个相邻 HRIR 的起始点通过循环对齐到同一相位。4.3 把尾音和距离感做出来近场补偿与直达/混响分离PKU-HRTF 这类库的测量距离一般是一米如果把 HRTF 直接用在距离 0.3 米的虚拟声源上声像会明显摆在头外固定距离随源接近没有让声场变大。近场补偿常见做法是随距离调整 ITD 和增益。我用的简单模型是def nearfield_gain(distance_m, reference_m1.0): # 距离越近声压越大但 HRTF 的方向滤波效果逐渐减弱 return min(1.0, reference_m / distance_m) def nearfield_itd_scale(distance_m, reference_m1.0): # 近场时 ITD 会略微变小距离过近时头部遮挡效应增强 if distance_m reference_m: return 0.7 0.3 * (distance_m / reference_m) return 1.0第一个函数算直达声增益第二个函数缩放 ITD。距离小于 1 米时ITD 会收缩因为声波不再是平面波而是球面波到达两耳的路径差变小。这个模型不严谨但用于游戏音频够用真实近场 HRTF 需要专门测量。混响处理上最典型的误区是给所有反射声也叠加 HRTF。正确做法是把信号分成直达声和混响声两条总线直达声经过 HRTF 卷积混响声用简化的双耳去相关器比如左右耳各加一个不同延时的全通滤波器再串一个房间衰减。这样混响声不会破坏直达声的定位反而让声像更稳定。4.4 参数速查采样率、滤波器长度、插值切换阈值整理一份可以贴到项目文档里的参数表参数建议值说明输出采样率48000 Hz和主流音频引擎工程统一HRIR 长度512 taps48kHz 下约 10.7msFFT 卷积块1024 或 2048实时分块卷积时使用插值方法频域幅度线性 ITD 补偿比时域直接插值更保真交叉淡化窗口10~20ms声源移动时避免咔哒声近场补偿距离阈值1 m小于 1 米启用近场模型直达/混响比近场 1:0.3远场 1:1.5距离感主要由此控制如果你手里的包是 44.1kHz 采样建议在预处理阶段统一重采样到 48kHz而不是渲染时再转。因为每个 HRIR 长度只有几百点逐文件转采样廉价且容易检查错误。记得重采样后重新做一次 RMS 归一化否则响度会偏移。5. 避坑要诀从解压到渲染最常见翻车点这片领域很多问题不是算法不懂而是数据使用姿势错了。下面五条是我实际从头到尾跑完一个 HRTF 渲染项目时踩过的坑几乎每一个都能让最终测听效果彻底翻车。每条按现象、原因、解决来写你可以直接照着排查。5.1 方位角零点对不齐测听时声像整个偏移现象用 0° 方位角 HRIR 渲染一个语音声源戴耳机听到的声音不在正前方而是稳定偏左或偏右并且所有角度都偏移同一个量。原因库里的 0° 定义和你的渲染程序定义不一致。比如库可能定义 0° 为右耳正侧面而你的程序默认 0° 为正前方或者库的方位角顺时针增长你的引擎是逆时针增长导致所有角度镜像翻转。解决先渲染两个参考音一个 0°一个 90°。90° 声源如果听到在左边而不是右边说明方位角方向是镜像的把方位角取负即可如果 0° 偏侧但 90° 位置基本正确说明零点有一个固定偏移。在加载函数里加一个角度补偿常量az_corrected (az azimuth_offset) % 360然后用主观测听反复微调azimuth_offset直到 0°、±45°、±90° 五个位置都能指认正确。这一步花十分钟值非常值。5.2 左右耳数据装反声道翻转导致的头外像现象声源放在右侧 90°但右耳听到的声音比左耳更晚、更小感觉声像跑到头外右侧但方向感很别扭更严重时声像干脆在头内翻转。原因压缩包里个别文件的左右耳标注反了。测量员摆麦克风时有概率把左右声道编号搞反尤其当文件名是_L/_R但内容其实是单声道复制时问题隐藏得很深。解决写一个自动检测脚本对每个角度做左右耳 ITD 估计。物理正确的 ITD 应该和方位角符号一致右侧声源右耳先到ITD左耳减右耳应为正左侧相反。对库中所有角度算一遍 ITD如果符号方向和方位角符号相反就交换左右耳数组if np.sign(itd) ! np.sign(azimuth_normalized): left, right right, left注意这里用的是归一化后的方位角正代表右侧。这个自动修复属于血泪经验我就曾因为没做这步整个水平面定位测听全部反了白折腾一下午。5.3 采样率不匹配音调变化和高度感丢失现象渲染出来的声音整体音调偏低且声源高度感几乎消失只有左右偏移感。原因HRIR 文件是 44.1kHz 采样但渲染工程跑在 48kHz。读文件时没看 soundfile 返回的采样率直接按 48kHz 播放相当于把信号时间轴拉长音调降了约 8%48000/44100≈1.088。更麻烦的是HRTF 里的频谱谷位置被错误搬移高度感线索被破坏。解决加载函数里必须打印每个文件的采样率且预处理阶段统一到目标采样率。最稳的做法是在load_pku_hrtf里加一个采样率检查发现不等于目标值就调用高质量重采样。不要用np.interp直接做它会引入频谱混叠让 8kHz 以上的 HRTF 特征失真。至少用scipy.signal.resample_polyfrom scipy.signal import resample_poly ir_resampled resample_poly(ir, fs_out, fs_in)resample_poly采用多项式重采样器对脉冲响应这种短信号表现足够好。重采样后重新做一次峰值对齐因为重采样会引入零点几个采样的平移对 ITD 影响很大。5.4 连续旋转时出现跳变插值没做用最近邻的代价现象声源从一个角度移动到另一个角度时听到的不是平滑移动而是隔 15° 就咔嚓跳一步像老式步进电机的声像。原因渲染代码用了最近邻查表每到一个离散测量角度才切换 HRIR。切换时前后两组冲激响应波形完全不同输出信号在切换点发生不连续人耳对 10ms 以内的波形突变非常敏感。解决手动插值哪怕只是最原始的时域线性插值也能避免跳变。先把切换点前后两个角度 HRIR 做线性加权同时在切换点前后做 10ms 交叉淡化。更好的方案是离线把 HRTF 按 5° 间隔预计算成一张大表运行时直接查表。这样表内本身已经平滑查表输出不会跳变CPU 代价远低于实时插值。5.5 数据里带录音链路不平坦原样使用会感觉声音闷现象渲染出来的语音高频细节明显比原始录音少声音发闷、有盒中感且所有角度都是如此。原因HRTF 测量时传声器、耳机和声卡各自的频响叠加进了数据。有些测量库会做自由场补偿有些不会。如果没做补偿HRIR 里就留着一条不平坦的采集链路频响最后渲染时高频部分被衰减听起来就像蒙了一层布。解决先做一次耳机频响校正。播放一段已知频谱的参考信号用耳机麦克风测量回放频响然后反演均衡滤波器。如果条件不允许最简单的方法是在数据预处理里加一个高通把 60~80Hz 以下的直流和低频漂移去掉同时用已知频谱平坦的语音信号做 AB 测试。如果发闷感依然明显可以测一下 5kHz 附近是否比原始信号低了 3dB 以上是的话就补一条 4~8kHz 的搁架式 EQ。但注意 EQ 增益不要超过 2~3dB否则会把 HRTF 本身的耳廓特征压平定位感反而变差。6. 反复验证你的渲染结果扫频听感与盲测的实操手法6.1 先用正弦扫频确认左右耳与方位把一段 100Hz 到 12kHz 的指数扫频信号分别用 0°、30°、90° 的 HRIR 渲染戴耳机连续听。健康的结果是方位的移动会让扫频声像平滑地从正前方向侧面移动并且在 4kHz 以上听到明显的音色变化。如果 90° 时左右耳音量差和 ITD 感知不到回头看 5.2 和 5.3。6.2 动态声源测试让声像稳定移动的关键让同一段扫频或语音以每秒 10~20° 的速度绕头部旋转一圈。重点听两个位置正后方±180°和头顶附近。正后方最容易暴露方位角零点对齐问题头顶最容易暴露仰角插值不够的问题。如果旋转到某个角度声音突然从外部掉进头内说明这个角度的左右耳能量差和 ITD 不协调需要检查插值方法。6.3 与真实录音对比检漏的最后一关有条件的话找一段真人头录音或者用音箱在消声室不同角度录制的语音和你的 HRTF 渲染结果做 AB 对比。我自己的习惯是先在 0°、±45°、±90° 五个点做主观盲测每个位置重复三次正确率低于 90% 就不允许自己结束调试。这套流程救过我一次当时 0° 和 180° 的虚线位置总被听反后来查出是相位插值时候的共轭方向处理错了不做一个完整的动态定位测听根本发现不了。希望这两千字的笔记能帮你绕过这些我已经替你踩过的坑。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站