先检查音频输入:WAV、裸 PCM 与采样率
同一段录音,文件路径输入正常,换成数组或字节后却出现异常?在调整模型、热词或 VAD 之前,先核对输入的格式、采样率、声道与数值范围。一个短小、只读的文件检查器,就能帮助你排除“空文件”“截断数据”和“把声道数算进时长”等基础问题。
例如,一个 16 kHz 双声道文件有 32000 个采样帧,时长是 2 秒,不是 4 秒。每帧含两个声道的采样值;把左右声道直接展平成单声道序列,会改变时间关系。另一个常见误区是把原本 8 kHz 的数据标记为 16 kHz:修改采样率标签没有生成新的采样点,不能代替重采样。
本文检查器仅面向本地、未压缩、16 位整数 PCM WAV,使用 Python 标准库,不加载 FunASR 或模型、不联网、不修改文件。它不是通用音频解码器,不支持 MP3、浮点 WAV 或任意裸 PCM,也不验证语音内容、识别准确率或生产服务。测试中的音频是人工构造的二进制样本,不是真实语音质量评测。
1. 先区分容器、字节与数组
WAV 是容器。文件包含格式信息与音频数据,不能把整个文件的字节都当作采样值。下面的示例使用 wave 解析容器,再读取数据帧。
裸 PCM 没有自描述的完整容器。处理前需要从数据来源确认采样率、声道数、位宽、符号和字节序。仅凭扩展名或字节长度不能可靠推断这些信息。不要把未知 PCM 强行加一个 WAV 头就认定修复完成。
数组有形状和数值范围。有符号 PCM16 的范围是 -32768 到 32767;转换成浮点 dtype 不等于缩放幅度。按 32768 归一化后,负端可到 -1,正端略小于 1。二维数组还需要明确哪一维是声道,不能只靠 reshape。具体支持方式应以所用版本的输入实现为准。
2. 运行只读的 PCM16 WAV 检查器
将下面代码作为独立的 inspect_wav.py 脚本运行,传入本地 WAV 路径。它逐块读取数据,报告采样帧数、时长和所有声道的绝对峰值,同时拒绝空数据、非 PCM16 和截断文件。
import json
import struct
import sys
import wave
def inspect_pcm16_wav(path):
with wave.open(str(path), "rb") as audio:
channels = audio.getnchannels()
rate = audio.getframerate()
declared = audio.getnframes()
if audio.getcomptype() != "NONE" or audio.getsampwidth() != 2:
raise ValueError("Expected uncompressed PCM16 WAV")
if channels < 1 or rate < 1 or declared < 1:
raise ValueError("Empty or invalid audio")
frame_bytes = channels * 2
chunk_frames = max(1, 65536 // frame_bytes)
frames = 0
peak = 0
while True:
raw = audio.readframes(chunk_frames)
if not raw:
break
if len(raw) % frame_bytes:
raise ValueError("Truncated sample or channel frame")
frames += len(raw) // frame_bytes
peak = max(peak, max(abs(value[0])
for value in struct.iter_unpack("<h", raw)))
if frames != declared:
raise ValueError("Audio data is shorter than its WAV header")
return {
"channels": channels,
"sample_rate": rate,
"frames": frames,
"duration_seconds": frames / rate,
"peak_abs": peak / 32768.0,
}
if __name__ == "__main__":
print(json.dumps(inspect_pcm16_wav(sys.argv[1]), indent=2))
python inspect_wav.py /path/to/input.wav
duration_seconds 使用帧数除以采样率,不再除以或乘以声道数。peak_abs 是全部声道上的最大绝对采样值,不是响度、信噪比或语音概率。非空的全零文件会返回 0;这和没有任何数据帧的空文件不同。峰值达到 1 值得检查,但单个极值本身不能证明整段录音发生了削波。
3. 怎样使用检查结果
- 时长与播放器或采集记录不符时,先核对数据来源的真实采样率和声道布局,不要通过修改标签让数字“看起来正确”。
- 文件被拒绝时,先检查导出或传输是否完整。对 MP3、24 位或浮点 WAV,使用明确支持该格式的解码工具,而不是删除验证条件。
- 双声道需要转单声道时,按所用前端的声道策略处理;需要重采样时,使用真实的重采样器,并保留转换前后的文件与参数。
- 峰值合理只说明幅度检查没有暴露这一类问题,不能证明录音中有可识别语音,也不能替代转录验收。
4. 版本与测试边界
#3899 的输入修复集成保留了原作者的提交,修复 NumPy PCM16 的缩放和二维浮点输入的声道处理。可核对固定提交的输入加载实现。截至本文发布,修复已合入 main;已验收的根包仍是 FunASR 1.4.26,不能据此宣称安装 1.4.26 已包含这次 main 修复。本文检查器不依赖该修复,也不会修改你的安装。
随文测试直接执行中英文正文及构建页面里的同一段代码,覆盖单/双声道、8/16 kHz、PCM16 正负边界、非空静音、空文件、8 位输入、截断采样、截断整帧、非 WAV、跨读取块的末尾峰值和命令行入口。它检查已覆盖的输入契约,不是任意损坏文件的安全审计,也不涵盖所有 WAV 编码扩展。
文件检查之后,再用有权处理的真实录音检查文字、时间戳、漏句和说话人结果。分享问题时附上脱敏的格式信息、版本与最小复现,避免公开私有音频、转录或绝对路径。
下一步使用会议转录验收清单,把输入检查与实际转录结果对应起来。