Whisper提取文本和切分音频
最近在整理托福真题音频时,遇到一个很现实的问题。
很多口语音频不是一个文件对应一个题目,而是一个文件里面同时包含多个环节。比如先是 Listen and Repeat,后面又接着 Take an interview。如果只是把整段音频丢给 Whisper 转成文本,当然能看懂内容,但这还不够。
真正麻烦的是:我后面还要把其中某一段音频切出来,单独做训练材料。
这就需要知道每一段在原始音频里的起止时间。否则只能靠手动拖进度条听,太慢,而且很容易切不准。

先说结论
Whisper 是可以拿到时间戳的。
它至少可以给两层时间信息:
segment级别的起止时间,也就是一小段文本对应音频里的哪一段。word级别的起止时间,也就是每个单词大概从第几秒开始、第几秒结束。
对我这个场景来说,真正有用的是第二种,也就是 word_timestamps。
因为 segment 是模型自动切出来的,它不一定刚好按题型边界切。比如 Take an interview 可能会被合并到上一段 Listen and Repeat 的最后一句里面。如果只看 segment,就会切过头。
这次实验的音频
我随便找了一个真题口语音频:
真题/1月/1.21-2/新托福2026真题02SpeakingModule1.mp3
这个音频总长大约 159.61s。它的结构比较典型:
Speaking section
Listen and repeat
...
Take an interview
...
这正好适合用来验证:Whisper 能不能帮我找到 Listen and Repeat 的起点,以及 Take an interview 的起点。
Whisper 命令
我本机上 whisper 在 Anaconda 里,ffmpeg 在 Homebrew 里,所以命令里加了一下 PATH。
PATH="/opt/homebrew/bin:$PATH" /opt/homebrew/anaconda3/bin/whisper \
"真题/1月/1.21-2/新托福2026真题02SpeakingModule1.mp3" \
--model base.en \
--language English \
--output_format json \
--word_timestamps True \
--output_dir /tmp/codex-whisper-speaking02
这里关键是两个参数:
--output_format json
--word_timestamps True
json 里会保留结构化结果,word_timestamps 会把每个词的时间也带出来。
识别出来的关键时间
Whisper 的 segment 结果大概是这样:
[13.10-18.92] Listen and repeat. You will listen as someone speaks to you...
[77.08-85.04] informed about upcoming sales. Take an interview. An interviewer will ask you questions...
这里就能看出一个问题:Take an interview 被放进了上一段 segment 里。也就是说,segment 对阅读转写文本够用,但对精准切音频还不够。
再看 word 级别时间戳,就清楚多了:
13.10 13.42 Listen
13.42 13.82 and
13.82 14.06 repeat.
79.88 80.56 Take
80.56 80.74 an
80.74 81.08 interview.
所以这次可以得到几个比较实用的边界:
| 内容 | 时间 |
|---|---|
Listen and repeat 标题 | 13.10s - 14.06s |
Take an interview 标题 | 79.88s - 81.08s |
| Listen and Repeat 整个环节 | 约 13.10s - 79.75s |
| 题目材料,含场景描述和跟读句子 | 约 28.54s - 79.50s |
| 只保留跟读句子,不含场景描述 | 约 42.86s - 79.50s |
这里我留了一点点 buffer。比如 Take an interview 是 79.88s 开始,那前一段可以切到 79.75s 左右,避免把下一个标题带进去。
这里还有一个容易漏掉的点:42.86s 是第一句跟读内容的开始,但它前面还有一小段场景描述。比如这次是:
You are working a part-time job at a grocery store near campus.
You are being trained to assist customers.
Listen to your trainer and repeat what he says.
Repeat only once.
如果是做真正的题目训练,这段场景描述应该保留。否则虽然跟读句子还在,但题目背景没了,材料是不完整的。
用 FFmpeg 切出来
如果我要保留整个 Listen and Repeat 环节,包括说明、场景和所有跟读句子,可以这样切:
/opt/homebrew/bin/ffmpeg -y \
-i "真题/1月/1.21-2/新托福2026真题02SpeakingModule1.mp3" \
-ss 13.10 -to 79.75 \
-c:a libmp3lame -q:a 2 \
/tmp/codex-audio-cuts/speaking02-listen-repeat-section.mp3
如果我只想保留题目材料,不要前面的考试说明,但要保留场景描述,应该从 28.54s 开始切:
/opt/homebrew/bin/ffmpeg -y \
-i "真题/1月/1.21-2/新托福2026真题02SpeakingModule1.mp3" \
-ss 28.54 -to 79.50 \
-c:a libmp3lame -q:a 2 \
/tmp/codex-audio-cuts/speaking02-listen-repeat-question-with-context.mp3
如果只想做非常纯粹的 shadowing,完全不要场景描述,也可以从第一句跟读内容开始切。但这个更像是二次加工材料,不适合作为默认切法:
/opt/homebrew/bin/ffmpeg -y \
-i "真题/1月/1.21-2/新托福2026真题02SpeakingModule1.mp3" \
-ss 42.86 -to 79.50 \
-c:a libmp3lame -q:a 2 \
/tmp/codex-audio-cuts/speaking02-listen-repeat-prompts-only.mp3
这里我没有用 -c copy,而是重新编码了一下。原因很简单:我更在意切点准确。-c copy 更快,但有时会受音频帧或视频关键帧影响,切出来的点不一定那么干净。
这个方法的原理
不用把它想复杂。
Whisper 在识别音频时,本来就不是只输出一整段文字。它内部会把音频切成若干片段,每个片段都有对应的时间范围。开启 word_timestamps 之后,它会进一步估计每个单词在音频时间轴上的位置。
所以我们可以反过来利用这个信息:
- 先把整段音频转成带时间戳的 JSON。
- 在 JSON 里找题型标题,比如
Listen and repeat、Take an interview。 - 用下一个题型标题的开始时间,作为上一个题型的结束时间。
- 前后加一点点 buffer。
- 用 FFmpeg 按这个时间范围切音频。
这里最重要的不是 Whisper 把每个词都识别对。比如这次它把一些商品词识别得并不准,但这不影响切分,因为我要找的是题型标题和结构边界。
真正需要注意的是:不要只依赖普通文本,也不要只依赖 segment。普通文本没有时间,segment 的边界又不一定等于题型边界。比较稳的做法是用 word 级别的时间戳找锚点。
后面可以自动化
如果后面要批量处理真题音频,我会按这个流程做:
- 遍历所有口语音频。
- 用 Whisper 生成 JSON。
- 把所有 word 拉平成一个列表。
- 忽略大小写和标点,匹配
listen and repeat、take an interview这类锚点。 - 生成一个切分清单,比如
audio_path / section / start / end。 - 再批量调用 FFmpeg 切出目标音频。
这个流程比较适合托福这种固定结构的音频。因为题型标题通常比较稳定,不需要真的理解整段内容,只要能找到结构锚点就行。
这次的经验
这个方法解决的不是“转写文本”的问题,而是“文本和音频时间轴对齐”的问题。
以前我容易把 Whisper 理解成一个转文字工具,但这次发现它更有价值的地方是:它可以帮我把一段长音频变成可检索、可切分的结构化材料。
这对后面的听力和口语训练很有用。真题音频只要先过一遍 Whisper,就可以自动拆成更小的训练单元,不需要每次都手动听、手动切。
下次再处理这类音频,我会直接先生成带 word timestamp 的 JSON,再决定怎么切。不要一开始就手动拖进度条,那个效率太低了。