
这次我们来看一个和游戏语音素材相关的项目偶像们的电话留言合集全员素材来源是《百万现场》页游。标题看上去像一个普通资源分享但真要把它整理成一份可收藏、可检索、带中文字幕的语音合集背后其实是一条完整的技术链路音频导出与批量格式转换、语音转写、字幕生成与对轴、批量重命名与归档最后还要做效果校验。这篇文章就把这套流程拆开讲清楚。这个合集的典型特征是“量大、角色多、片段短”。几十到上百段留言语音每段都在几十秒到两三分钟之间。处理这类素材时最值得关注的不是单独一条音频怎么转写而是整批文件如何自动转换、如何批量出字幕、如何按角色统一编号、如何防止中文字幕出现错别字和丢行。只要把这几条跑通后面无论是整理同人语音包还是做游戏素材考古都能直接用同一套流程。文章会从一个普通游戏音频合集的实际处理需求出发演示从原始音频到最终中文字幕文件夹的完整过程。重点覆盖 ffmpeg 批量转码、openai-whisper 与 faster-whisper 两种转写方案、SRT 字幕生成、专有名词替换与人工修正、目录批量归档和常见性能问题排查。适合这几类读者正在做游戏语音素材整理的同人作者需要把大量短音频批量转写成文字的内容运营或者想把手头零散语音文件结构化归档的技术爱好者。文章不讨论游戏本身的内容和剧情只讲音频处理链路。1. 核心能力速览能力项说明素材类型游戏偶像电话留言音频全员向实际角色数和音频总数以整理版本为准目标产物带中文字幕、按角色归档、可检索的语音合集主要工具ffmpeg、Python 3、openai-whisper / faster-whisper、字幕编辑工具运行平台Windows / macOS / Linux 均可转写模型可以 CPU 或 GPU 运行GPU 要求可选。CPU 可跑小体积模型有 CUDA 显卡能明显缩短转写时间显存占用需按实际模型版本和推理参数测试不同显卡差异较大接口 API本地转写默认无 API如需接入业务系统可以自行封装为 FastAPI 服务批量任务支持。按目录批量转码、批量转写、批量重命名适合场景游戏语音素材整理、音频转写、字幕制作、同人内容归档这里不写死“必须什么显卡”“必须多少显存”因为转写工具比较灵活huge 模型和 tiny 模型的资源消耗差十倍以上实际占用取决于你选哪个模型、用什么精度、跑 CPU 还是 GPU。2. 适用场景与使用边界2.1 适合做什么这个流程适合把零散音频变成结构化语料。典型场景包括游戏角色语音的归档与检索给每条留言音频生成文字稿方便后续搜索。中文字幕制作配合转写出来的 SRT 文件在播放器中显示字幕。二创素材准备整理好语音后后续剪辑时可以直接按关键词定位。语料积累如果后续要做同人向的语音合成或模型微调需要先有对齐好的文本和音频这套流程就是前置步骤。2.2 不适合做什么不适合做实时转写whisper 系模型是离线推理延迟达不到实时通话场景的要求。不适合处理大量带背景音乐、音效和人声重叠的片段转写准确率会明显下降。不适合把转写结果直接当成最终成品发布机器转写的中文长句经常需要人工调整标点和专有名词。2.3 版权与合规边界这一步必须强调游戏内音频素材属于游戏版权方声优配音还涉及声音权益和肖像权。这个技术流程只适用于个人学习、同人归档和内部研究不要把提取出来的语音包直接二次分发更不要用它做 AI 声音克隆、伪造通话记录、生成误导性内容或者训练商用模型。如果要在公开平台发布整理后的语音合集和字幕先确认版权方允许的范围。中文字幕如果来自人工翻译也需要确认翻译者的授权。技术能降低整理成本但不能替你解决版权问题。3. 环境准备与前置条件先列一份通用检查清单按你自己的系统环境对照准备。检查项建议操作系统Windows 10/11、Ubuntu 20.04、macOS 均可音频工具ffmpeg用于批量格式转换和音频采样Python3.10 到 3.12兼容性较好转写框架openai-whisper 或 faster-whisper二选一GPU 驱动如果使用 CUDA需要装好 NVIDIA 驱动和对应版本的 PyTorch磁盘空间原始音频 转写模型 中间 wav 字幕文件建议预留 20GB 以上网络转写模型首次运行需要下载如果下载不稳定可配置模型缓存目录或镜像源需要说明的是本地的电话留言音频文件从游戏客户端提取后可能是 ogg、m4a、wav 或 mp3 混合格式。不同来源的抽包工具导出的格式不一样所以第一步统一转成 wav 最稳妥。3.1 安装 ffmpegWindows 用户可以直接用包管理器安装也可以从 ffmpeg 官网下载编译好的二进制文件把 bin 目录加入 PATH。Linux 下安装sudo apt update sudo apt install -y ffmpegmacOS 下安装brew install ffmpeg安装完成后验证ffmpeg -version只要能看到版本号就说明路径可用。3.2 创建 Python 虚拟环境转写依赖的包比较多建议用虚拟环境隔离避免污染系统 Python。python -m venv venv source venv/bin/activate # Windows 下用 venv\Scripts\activate激活后升级 pippip install --upgrade pip这样后续安装 faster-whisper 或 openai-whisper 时不会碰到权限冲突。4. 素材准备与音频批量转换4.1 目录结构设计开始之前先把目录规划好否则批次一多就会乱。推荐结构如下。voice_mail/ ├── audio_raw/ # 原始音频保持导出状态 ├── audio_wav/ # 转换为 16kHz 单声道 wav ├── srt_output/ # 转写字幕文件 ├── txt_output/ # 转写文本便于检索 └── final/ # 最终归档目录按角色分文件夹原始文件放audio_raw中间文件和最终产物分开避免处理脚本误覆盖原始素材。4.2 批量转换为 16kHz 单声道 wavwhisper 系转写模型对 16kHz 单声道音频支持最好。统一用 ffmpeg 做一次采样率转换能减少后续转写时的音频解码问题。Linux / macOS 下的批量转换脚本cd voice_mail mkdir -p audio_wav for f in audio_raw/*.ogg; do name$(basename $f .ogg) ffmpeg -y -i $f -ar 16000 -ac 1 audio_wav/${name}.wav done如果你的原始音频是 m4a把后缀改成 m4afor f in audio_raw/*.m4a; do name$(basename $f .m4a) ffmpeg -y -i $f -ar 16000 -ac 1 audio_wav/${name}.wav doneWindows PowerShell 下的批量处理可以这样写Get-ChildItem -Path .\audio_raw -Filter *.ogg | ForEach-Object { $name $_.BaseName ffmpeg -y -i $_.FullName -ar 16000 -ac 1 .\audio_wav\$name.wav }转换完成后随机打开几个 wav 文件听一下确认没有爆音、没有截断、声道没有反相。4.3 检查音频长度分布电话留言音频长度差异可能很大先做一次时长统计方便判断转写策略。ffprobe -show_entries formatduration -of csv audio_wav/example.wav批量统计可以用 Pythonimport csv import subprocess from pathlib import Path wav_dir Path(./audio_wav) with open(audio_durations.csv, w, newline) as f: writer csv.writer(f) writer.writerow([filename, duration_seconds]) for wav in sorted(wav_dir.glob(*.wav)): result subprocess.run( [ffprobe, -show_entries, formatduration, -of, csvp0, str(wav)], capture_outputTrue, textTrue, ) try: duration float(result.stdout.strip()) except ValueError: duration -1 writer.writerow([wav.name, duration]) print(wav.name, duration)这个 CSV 后面可以用来核对转写结果有没有漏文件。5. 语音转写与中文字幕生成5.1 选择转写方案目前主流方案是 openai-whisper 和 faster-whisper。openai-whisper 的优势是官方实现命令行工具完善使用简单。缺点是 CPU 推理比较慢显存占用相对偏高。faster-whisper 是基于 CTranslate2 的重实现CPU 和 GPU 下速度都更快显存占用也更小适合批量处理大量短音频。推荐批量场景直接选 faster-whisper。5.2 安装 openai-whisperpip install -U openai-whisper命令行直接转写whisper ./audio_wav/example.wav --model small --language zh --output_format srt --output_dir ./srt_output参数说明--model small模型体积适中中文场景速度和准确率比较均衡。--language zh指定中文避免模型自动检测。--output_format srt输出字幕文件。--output_dir指定输出目录。5.3 安装 faster-whisperpip install faster-whisperfaster-whisper 通过 Python API 调用比较舒服。下面是一段批量转写脚本遍历audio_wav目录下所有 wav生成 srt 和 txt。import os from pathlib import Path from faster_whisper import WhisperModel model_size small model WhisperModel(model_size, devicecpu, compute_typeint8) wav_dir Path(./audio_wav) srt_dir Path(./srt_output) txt_dir Path(./txt_output) srt_dir.mkdir(exist_okTrue) txt_dir.mkdir(exist_okTrue) for wav_path in sorted(wav_dir.glob(*.wav)): print(fprocessing: {wav_path.name}) segments, info model.transcribe( str(wav_path), languagezh, beam_size5, vad_filterTrue, ) cue_list [] text_lines [] for seg in segments: start seg.start end seg.end text seg.text.strip() text_lines.append(text) start_str f{int(start // 60):02d}:{int(start % 60):02d}:{int((start % 1) * 100):02d} end_str f{int(end // 60):02d}:{int(end % 60):02d}:{int((end % 1) * 100):02d} cue_list.append((start_str, end_str, text)) base_name wav_path.stem srt_path srt_dir / f{base_name}.srt with open(srt_path, w, encodingutf-8) as f: for idx, (start_str, end_str, text) in enumerate(cue_list, start1): f.write(f{idx}\n) f.write(f{start_str},000 -- {end_str},000\n) f.write(f{text}\n\n) txt_path txt_dir / f{base_name}.txt with open(txt_path, w, encodingutf-8) as f: f.write(\n.join(text_lines)) print(fdone: {base_name})这段脚本会把每段音频转写成一条条带时间轴的 SRT 字幕同时生成纯文本版本方便搜索。注意compute_typeint8在 CPU 上能明显降低内存压力准确率损失通常在可接受范围内。如果你想追求更高准确率可以把 int8 换成 float16但要确认 CPU 是否支持或者改成 GPU 推理。5.4 转写效果校验转写完成后不要直接归档先抽 5 到 10 个文件人工检查角色名、地名、物品名是否写错。数字和语气词是否被吞掉。长句是否被错误断成多句。时间轴是否出现大面积偏移。电话留言通常有“滴”声提示音和开始结束音效这部分可能被转写成乱字符可以在后期统一清理。6. 专有名词替换与字幕清理6.1 为什么需要专有名词替换游戏角色名、组合名、活动名称在音译和汉字写法上经常不一致。whisper 转写时对专有名词的识别不稳定同一个名字在这条音频里写“美希”在另一条里可能写成“美咲”。所以需要一个映射表做统一替换。准备一个replace_map.json{ 美咲: 美希, 星梨花: 星梨花, 诗花: 诗花, 静香: 静香 }然后写一个批量替换脚本处理所有 srt 和 txt 文件。6.2 批量替换脚本import json from pathlib import Path with open(replace_map.json, r, encodingutf-8) as f: replace_map json.load(f) for srt_path in sorted(Path(./srt_output).glob(*.srt)): content srt_path.read_text(encodingutf-8) for old, new in replace_map.items(): content content.replace(old, new) srt_path.write_text(content, encodingutf-8) print(fupdated: {srt_path.name}) for txt_path in sorted(Path(./txt_output).glob(*.txt)): content txt_path.read_text(encodingutf-8) for old, new in replace_map.items(): content content.replace(old, new) txt_path.write_text(content, encodingutf-8)替换表一定要人工维护不能依赖模型自动修正。角色名写错会导致整个合集的检索结果不可靠。6.3 清理时间轴残留短语音的开头和结尾经常有空白段转写后容易出现首尾字幕时间轴偏长。可以用 Aegisub 或剪映手动拖动时间轴进行精修也可以直接在 srt 文本中把明显过长的空行删除。如果是批量处理可以给字幕文件设置一个最小持续时间比如单条字幕少于 0.3 秒就删除。但要注意这个规则不能一刀切语气词“嗯”“啊”有时也很有价值建议保留后人工判断。7. 字幕对轴与人工修正机器生成的 SRT 在安静环境下准确率较高但页面游戏的电话留言音频可能带有通话效果处理比如背景噪声、混响和模拟信号感这会导致时间轴不能完全贴合字句。7.1 使用 Aegisub 精修Aegisub 是经典字幕编辑工具适合逐条调整时间轴和文本。操作流程打开 srt 文件。加载对应 wav 音频。播放时查看字幕文本是否和语音对齐。对偏移较大的字幕用键盘快捷键调整开始和结束时间。修正错别字和断句。保存为新版本 srt。7.2 使用剪映快速处理剪映的优势是可视化适合时间轴偏移不严重的场景。把音频和 srt 字幕同时导入然后拖动字幕块微调。缺点是对批量处理不友好适合最后精修少数重点片段。7.3 制定人工修正标准人力和时间有限不用每条都做到逐字严丝合缝。建议按用途分级内部归档文本内容正确即可时间轴误差不超过 1 秒。公开字幕时间轴误差控制在 0.3 秒内专有名词全部统一。二创剪辑素材关键句必须精确对轴其他句子可放宽。这样能避免把大量时间浪费在非关键片段的精修上。8. 批量剪辑、重命名与文件归档8.1 按角色目录归档电话留言合集的特征是角色多。如果全部堆在一个文件夹里后续查找非常痛苦。推荐按角色名建立子目录final/ ├── 天海春香/ ├── 如月千早/ ├── 星井美希/ └── 高槻弥生/原始文件名通常是一串无规律数字或角色编号需要结合转写文本判断归属。如果原始音频文件本身带有角色编号可以在抽包阶段直接映射如果没有只能通过转写文本中的角色名或上下文人名判断。8.2 批量重命名脚本假设你已经按角色把文件分到对应目录可以用脚本统一重命名比如按“角色名_序号.wav”的规则。from pathlib import Path base_dir Path(./final) for char_dir in sorted(base_dir.iterdir()): if not char_dir.is_dir(): continue wav_files sorted(char_dir.glob(*.wav)) for idx, wav_path in enumerate(wav_files, start1): new_name f{char_dir.name}_{idx:03d}.wav wav_path.rename(char_dir / new_name) print(f{wav_path.name} - {new_name}) srt_files sorted(char_dir.glob(*.srt)) for idx, srt_path in enumerate(srt_files, start1): new_name f{char_dir.name}_{idx:03d}.srt srt_path.rename(char_dir / new_name) print(f{srt_path.name} - {new_name})脚本执行前先在小范围测试一次确认顺序符合预期再把全部文件跑完。8.3 生成索引文件归档完成后生成一个index.csv方便检索内容包括文件名、角色名、音频时长、转写文本。import csv from pathlib import Path rows [] for char_dir in sorted(Path(./final).iterdir()): if not char_dir.is_dir(): continue for txt_path in sorted(char_dir.glob(*.txt)): text txt_path.read_text(encodingutf-8).strip() rows.append([char_dir.name, txt_path.stem, text]) with open(index.csv, w, encodingutf-8-sig, newline) as f: writer csv.writer(f) writer.writerow([角色, 文件名, 转写文本]) writer.writerows(rows)这样整个合集就有了一个可搜索的索引后续要定位某句台词直接搜 CSV 即可。9. 资源占用与性能观察9.1 显存占用与推理速度这是很多本地部署用户最关心的部分。需要说明的是这里不写死任何具体显存占用因为 whisper 系列模型在不同精度、不同音频长度、不同 beam size 下的资源使用差异很大。更稳妥的做法是在本机记录一次实际数据用什么模型small 还是 medium。什么精度int8 还是 float16。CPU 还是 GPU。一段 1 分钟音频转写花了多少秒。任务管理器或 nvidia-smi 中记录的峰值占用。观察方法Linux 下用nvidia-smi查看 GPU 显存。Windows 下用任务管理器查看 GPU 专用显存。CPU 内存占用用top或任务管理器观察。9.2 影响性能的关键参数模型大小tiny、base、small 速度差距最大medium、large 准确率高但资源需求明显上升。精度int8 比 float16 省显存但可能在复杂句子上掉一点准确率。音频长度长音频需要完整加载进内存建议电话留言这种短音频按文件独立转写不要拼接成长文件。beam size数值越大搜索空间越大速度越慢效果提升有限。vad_filter开启后能过滤静音段减少无效计算。9.3 降低资源占用的通用策略如果机器配置一般建议先用 small int8 CPU 跑通流程。优先生成 txt不急着生成 srt。一次只跑一个音频文件避免同时开多个转写进程。电话留言本身是短音频不需要对音频做切分直接整段输入即可。如果 GPU 显存不够可以降低模型大小或改用 CPU int8。如果 CPU 内存不够可以把音频先切成 30 秒片段转写后再拼接时间轴。10. 接口 API 与批量任务扩展10.1 封装为本地 API如果后续要把转写能力接到自己的工具链里可以给 faster-whisper 封装一个简单的 FastAPI 服务。先安装依赖pip install fastapi uvicorn服务示例from fastapi import FastAPI, UploadFile from faster_whisper import WhisperModel app FastAPI() model WhisperModel(small, devicecpu, compute_typeint8) app.post(/transcribe) async def transcribe(file: UploadFile): temp_path f./tmp/{file.filename} with open(temp_path, wb) as f: f.write(await file.read()) segments, info model.transcribe(temp_path, languagezh, vad_filterTrue) result [] for seg in segments: result.append({start: seg.start, end: seg.end, text: seg.text.strip()}) return {language: info.language, segments: result}启动服务uvicorn app:app --host 127.0.0.1 --port 8000调用示例curl -X POST http://127.0.0.1:8000/transcribe \ -F file./audio_wav/example.wav需要注意这个接口只面向本地或内网测试不要直接暴露到公网。如果要做批量任务队列需要额外加任务状态管理和日志记录。10.2 批量任务设计电话留言数量多时建议不要用一个并发超高的脚本直接跑而是设计一个简单的任务清单phone_recordings/ ├── todo/ # 待处理 wav ├── processing/ # 正在处理 ├── done/ # 已完成 └── failed/ # 失败处理逻辑每次从 todo 取一个文件。转写成功后移动到 done。转写失败记录错误日志文件移动到 failed。下次重跑只处理 failed 目录里的文件。这样即使中途断电或脚本崩溃也不会丢失进度。11. 常见问题与排查方法问题现象可能原因排查方式解决方案ffmpeg 命令找不到未安装或未加入 PATHffmpeg -version重新安装并配置环境变量转写输出乱码音频采样率过高或格式损坏ffprobe 查看音频信息统一转为 16kHz 单声道 wav模型下载失败网络不稳定或模型缓存目录不可写查看报错信息配置本地模型路径或镜像源CPU 转写速度极慢模型过大或 beam size 过高查看单条音频耗时换 small/tiny 模型开启 int8GPU 显存不足模型精度过高nvidia-smi 观察占用改用 CPU int8或缩小模型SRT 时间轴整体偏移音频包含片头片尾提示音播放对比在 Aegisub 或剪映中整体平移时间轴角色名不一致whisper 对专有名词识别不稳定对比多条音频中的同一个名字维护 replace_map 批量替换转写结果漏行音频中静音段过长触发 VAD 截断检查 txt 文件行数关闭 vad_filter 或调低静音阈值批量脚本中断单条音频损坏查看 failed 目录日志跳过损坏文件重试失败任务端口被占用本地 API 服务与其他进程冲突lsof -i:8000或任务管理器更换端口启动12. 最佳实践与使用建议电话留言合集这类素材处理起来不复杂但很容易在小细节上浪费时间。这里给出几条实践建议。第一次跑通时先拿 5 条音频做完整链路测试不要一上来就全量转写。确认 ffmpeg、whisper、字幕输出、批量重命名全部正常后再扩大范围。这样能避免在全量处理到一半时发现参数选错浪费好几个小时。保留一套最小可运行配置。把目录结构、replace_map.json、转写脚本、归档脚本都放在一个项目目录下记录下当前使用的模型大小和精度参数。以后再整理其他语音素材可以直接复制这套结构。模型文件、输入音频、输出字幕和中间产物分目录管理。原始文件和中间 wav 永远保留一份方便后续换模型重新转写时复用。批量任务一定要加日志和失败重试机制。脚本崩溃后重新执行要能跳过已经处理成功的文件不要每次从头开始。涉及角色语音、声优声音、游戏音频时必须谨慎处理授权问题。本地转写、个人学习没有问题但公开发布、商用、训练模型都有明确风险。如果你的目的是做公开字幕合集先确认来源是否允许整理和二创不要因为“素材本来就在网络上”就默认没有版权问题。转写结果发布前做最后一次人工复核。至少抽 10% 的音频对比转写文本重点看角色名、数字、语气词和句尾有没有严重错误。短语音比较容易精修花少量时间在关键片段上能明显提升合集质量。13. 总结与下一步这个项目最值得尝试的点是把一堆看似零散的游戏语音素材通过批量转换、离线转写、字幕生成和目录归档变成一份可搜索、可复用的结构化语音库。整套流程对硬件要求不高CPU 加 small 模型就能跑通有 GPU 可以把转写时间缩短到原来的几分之一。最先应该验证的功能是音频批量转码和单条转写准确率。先用 5 条音频测试效果如果转写文本基本准确再继续做批量处理和字幕归档。最容易踩的坑有两个一是模型下不动导致流程卡在第一步二是专有名词不统一导致检索结果不可信这两点需要提前准备。后续可以继续扩展的方向包括把转写服务封装成接口供内部工具调用加入任务队列支持全自动批处理把生成的字幕接入视频压制流程或者把整理后的语料用于同人项目的文本检索和语音索引。先把基础链路跑通再按需要加功能这个项目就能从一次性整理脚本变成可复用的本地语音处理工作流。建议收藏备用以后碰到类似的音频整理需求直接照着这套流程走一遍就能出成果。