拿到多人会议录音时,常见的卡点不是“能不能转写”,而是先决定这条音频要不要走说话人切分。判断方式很直接:如果会后需要按人出纪要、按发言人检索、或者生成带人名的字幕,就应该先做说话人切分,再按时间段把音频切成小段送给文字转写;如果只要一份不关心谁说了什么的文字记录,整段转写更省事。Xiaomi-CocktailASR-1 在这条链路里承担的是“谁在什么时候说”这一步,它通常不负责最后文字稿的排版和润色,所以接入现有转写流程之前,需要先确认它的输入格式、输出字段和时间戳语义。
多人会议要分人转写,顺序建议是先说话人切分、再分段转写,而不是整段转写后回头猜说话人。接入 Xiaomi-CocktailASR-1 前,先从仓库 README 与示例脚本确认采样率、声道和输出里 speaker、start、end 的实际字段名,再用一段两人交替的短音频跑最小验证,对照播放位置检查时间戳偏差,最后才把带 speaker 的分段结果交给文字转写。重叠说话和远场噪声下的标签是否断裂,需要单独实测确认。
在开源仓库示例里确认 Xiaomi-CocktailASR-1 的输入输
要确认的是三件事:输入音频的格式(采样率、声道数、是否要求先重采样)、配置或命令行里有没有说话人切分相关开关、输出结构里哪个字段表示说话人。不要凭印象假设字段名,直接在仓库里搜。
# 在克隆下来的仓库根目录执行,文件名按实际情况替换
grep -rn 'sample_rate\|sampling_rate\|num_channels\|mono' README.md README_*.md configs/ 2>/dev/null | head -30
grep -rn 'speaker\|spk_id\|start\|end' examples/ scripts/ 2>/dev/null | head -40把搜到的结果记成一张小表:采样率是 8000 还是 16000(以仓库说明为准)、是否强制单声道、说话人字段叫 speaker 还是 spk、时间单位是秒还是毫秒。这张表是后面所有验证的基准,字段名写错,对齐检查就无从谈起。
准备一段两人交替说话的短音频做最小验证
样本要可控:两人轮流说话,每人两三句,中间留明显停顿,不要重叠,环境尽量安静,总长控制在 30 到 60 秒。录完转成仓库要求的格式,再跑一遍示例脚本。
# 统一成仓库 README 要求的格式(下面以 16 kHz 单声道为例,按实际说明修改)
ffmpeg -i two_speakers.m4a -ac 1 -ar 16000 -c:a pcm_s16le two_speakers.wav
# 跑示例脚本,脚本名与参数以 README 为准
python examples/infer.py `--audio` two_speakers.wav `--output` out.json
# 只看结构化输出
python -m json.tool out.json | head -60判断点只有一个:输出里是否出现两个不同的 speaker 标签,并且大致对应音频里那两次交替发言。如果所有片段都贴上同一个标签,先检查配置里是否真的开启了说话人切分,再检查音频是不是已经被混成单路的混音文件——两人已被混到一路且音量接近时,切分本身就更难,这属于输入条件的问题,不是换参数能解决的。
检查说话人标签与时间戳的对齐关系
先把四个字段抽出来看一遍,字段名按上一步记录的实际名称替换。
import json
data = json.load(open('out.json'))
rows = data['segments'] if isinstance(data, dict) and 'segments' in data else data
for r in rows:
print(r.get('start'), r.get('end'), r.get('speaker'), str(r.get('text'))[:20])然后播放原音频,在 10 秒、20 秒、30 秒附近各记一次“此刻听到的是谁”,和输出里同一时刻的 speaker 对比。要记录的是偏差范围,比如整体偏移是否稳定在同一量级、是否随时间累积,而不是纠结某一次是否完全一致。另外注意时间基准:如果 start 和 end 是相对切分后小音频的局部时间,接回绝对时间轴时需要加上偏移量,这一步漏掉,字幕就会整体前移或后移。
把说话人切分结果接进后续转写或字幕流程
切分结果只回答“谁在什么时候说”,文字由后续 ASR 补齐,两者靠 start 和 end 对齐。通用的接法是先按 speaker 分组,再逐段切音频送转写。
from collections import defaultdict
groups = defaultdict(list)
for r in rows:
groups[r['speaker']].append(r)
for spk, items in groups.items():
items.sort(key=lambda x: x['start'])
for it in items:
# 1) 用 ffmpeg 按 start/end 切出小段
# 2) 把小段送 ASR,拿到 text
# 3) 回填,保留原始 start/end/speaker
print({'speaker': spk, 'start': it['start'], 'end': it['end'], 'text': '<ASR 结果>'})输出字幕时,speaker 建议映射成单独一列或行首前缀,不要直接拼进 text 字段,否则后续检索、合并和人工校对的成本都会变高。
对比单人、双人重叠、远场噪声三种音频条件
用同一份配置、同一个模型,分别跑三种音频,只记录能复现的现象:
- 单人连续讲话:输出通常只有一个 speaker 标签。如果出现多个短标签来回切换,先看最小片段长度之类的参数,不要急着认定是模型问题。
- 双人重叠:两人抢话的区间经常被并成一个标签,或只在重叠段出现标签跳变。这是可复现的现象,会议里抢话多的话,需要接受重叠段说话人不可靠。
- 远场噪声:会议室远距离拾音、空调声、键盘声,可能额外切出很短的片段并分配成新 speaker。建议把同一段音频跑两次,确认这些短片段是否稳定复现,再决定要不要调阈值。
每次跑完都把输出留着,逐条对比标签数量和时间段覆盖是否连续、有没有空洞。这样得到的边界是你能自己复核的,比任何一句“支持多少说话人”都可靠。