🧰 工具先查名录(硬性):要写脚本做某件事之前,先看本 skill 的
TOOLS.md—— 本 skill 全部脚本的用途 / 用法 / 路径清单,外加本机路径收纳表。 有能用的就改它,不要重建。 新增或改名脚本后,跑一次python "<skills>/civ6-modding/tools/skill_manifest.py" civ6-audio-pipeline刷新名录(--check可做漂移检测)。
Civ6 音频管线(civ6-audio-pipeline)
从素材到游戏内发声的全自动/半自动管线。(证据清单见 references/mechanism.md §7),机制细节见 references/mechanism.md,工作史与关键决策记录见 references/history.md,动手前先扫一遍"铁律"。
0. 环境与路径(默认值,可被 skill 目录 local_paths.json 覆盖)
| 项 | 路径 | 说明 |
|---|---|---|
| WwiseCLI | 见 local_paths.json: wwcli(本机默认值仅为作者示例) |
官方命令行生成器,与 GUI Shift+F7 同引擎 |
| 模板工程(瘦身) | <skill>\assets\template_slim\FelineJasperKitty ⚠ 不随仓库分发 |
结构壳(空 Originals),仅供本机实验脚本参考 |
| 模板工程(完整) | 见 local_paths.json: template_full(首次用 scripts/ensure_template.py 获取) |
含媒体;GUI 里做 voice/bgm 容器工作时用它 |
| 教程 | 上游开源教程《Civ6_Modding_Textbook》第 11 章 ⚠ 不随仓库分发 | 领袖/BGM 的 GUI 步骤权威依据;用 ensure_template.py --repo <url> 拉取 |
| P1 源工程 | 见 local_paths.json: p1(ModBuddy 源工程根) |
.civ6proj 注册对象 |
| P2 运行目录 | 见 local_paths.json: p2(游戏 Mods 目录) |
.modinfo 注册对象 |
| ffmpeg/ffprobe | PATH 内 | 核验与响度均衡依赖 |
local_paths.json是本机私有配置(键:wwcli/template_full/p1/p2),不入库;也可用CIV6_WWCLI、CIV6_TEMPLATE_FULL、CIV6_P1、CIV6_P2环境变量临时覆盖。⚠ 第三方模板与教程不随本 skill 分发(上游仓库未附许可证):公开仓库里
assets/template_slim/、assets/tutorial11/为空属正常。首次使用时跑python scripts/ensure_template.py(会先询问,再用git clone --depth 1拉上游教程仓库并写入local_paths.json: template_full);没有 git/网络时手动下载该仓库并填template_full即可。
编码自适应(无需
PYTHONUTF8=1):audio_check.py/audio_normalize.py/new_bank_project.py/wwise_wire.py已内置 UTF-8 子进程解码,并在输出重定向(管道/采集)时自动切 UTF-8 标准输出;
铁律(违反即返工):
- 直改工程文件前 Wwise 必须关闭——GUI 内存态一旦保存会覆盖直改结果。
- 只做增量插入与按 GUID 的 Name 更新;模板内
id88602518等手调对象一个字节不碰。 - bank 必须 Wwise 2015.1 编译(SoundbankVersion=113 / BKHD 布局 ver 24,与本地先例 mod 一致;官方原版 ver 20,引擎兼容两者)。禁止用新版 Wwise 迁移工程。
- 评估版许可每 bank ≤200 媒体项,大批量拆 bank。
- 事件引用全走 GUID;改名=只改 Name(引擎按 GUID 解析)。
- ini 写盘:无 BOM、CRLF、ASCII;modinfo/civ6proj:保持原编码与换行。
- 注册校对:所有物理文件(含 ini)必须出现在
.modinfo的<Files>节 /.civ6proj的<Content>清单;ini 除此之外还必须单独出现在<UpdateAudio>加载动作。交付前register_to_mod.py --verify必须 PASS(自动核对 UpdateAudio→ini、磁盘↔清单一致)。
1. 三类路由(用户提供分类后先走此表)
| 类别 | Wwise 载体 | 全自动范围 | 必须人工(GUI)的步骤 |
|---|---|---|---|
| sfx 普通音频(UI音/单位音/环境音/台词单发) | 克隆模板→新建独立 bank(ActorMixer+Event+Bank 三个 work unit 直改) | 全流程:核验→均衡→导入→事件→生成→注册 | 无(触发端 Lua/DB 由后续接线需求决定) |
| voice 领袖语音 | 教程模板的 Speech 工作单元(语言夹本地化) | 核验→均衡(-21LUFS/单声道)→导入→事件改名与注册→生成→注册 | 教程 11 章:动画时间线 Sound 事件填写 + 动画 Duration 调整(素材编辑器 Timeline Editor) |
| bgm 文明音乐 | 教程模板的 Interactive Music(时代容器/权重) | 核验→均衡(-28/-25LUFS/立体声/流式)→媒体替换→事件→生成→注册 | 教程 11 章:交互音乐布局里 ExitCustom 自定义时间戳、时代选曲权重、主题音乐时代配置 |
BGM/voice 的"人工步骤"在 GUI 里做完并保存后,回到本管线继续(事件/bank/注册仍由脚本完成)。
分类决策规则:用户先声明哪些是 voice / bgm / quote(台词类:伟人行动音、著作朗读、奇观语录);未声明的素材若时长 ≥60s → 疑似 BGM,必须询问用户后再归类;否则一律按 sfx 处理(audio_check 会对 ≥60s 未分类文件打印 [需询问] 提示)。
1.5 BGM 内部三组结构与主动询问流程
文明音乐(BGM)按教程分为三组:
- 轮播背景音乐:正常游玩时随机/按权重轮播的背景音乐。
- 外交背景音乐:进入领袖外交界面时播放;按官方做法,一个文明对应一首外交背景音乐(
LeaderMusic_<Civ>/Play_Leader_Music_<Civ>)。 - 主题音乐:每次载入游戏时先播放当前时代的主题曲;建议按 4 个时代准备(远古/中古/工业/原子)。
BGM 素材分类引导(skill 触发时必须执行)
BGM 制作触发时必须主动引导分类:
- 先扫描用户提供的素材目录/文件。
- 按文件名/子目录预分类:
- 文件名含
Theme/主题→ 主题音乐; - 文件名含
Diplo/外交/Leader→ 外交背景音乐; - 其余 → 默认归为轮播背景音乐。
- 文件名含
- 主动向用户确认:
- “请确认哪些是主题音乐?”
- “请确认哪些是外交背景音乐?”
- “其余将默认为轮播背景音乐。”
- 主动询问时代粒度:
- “主题音乐是否按 4 个时代准备?”
- 将分类结果记录为 assignment/manifest,后续脚本按此处理。
轮播机制 ≠ 权重机制(必读区分)
- 轮播机制:决定“哪些音乐对象进入哪个时代的
CivilizationMusic播放列表”。权威解释(教程原文 + 用户定案)——每个时代容器 = 本时代主题曲 1 首 + 本时代全部轮播音乐 + 上一时代主题曲 + 上一时代轮播音乐的一半:- Ancient:远古主题曲 + 全部远古轮播音乐(首个时代,无上一时代可延续)。
- Medieval:中古主题曲 + 全部中古轮播音乐 + 远古主题曲 + 一半远古轮播音乐。
- Industrial:工业主题曲 + 全部工业轮播音乐 + 中古主题曲 + 一半中古轮播音乐。
- Atomic:原子主题曲 + 全部原子轮播音乐 + 工业主题曲 + 一半工业轮播音乐。
- Atomic 是最后一个时代容器,其轮播音乐不再向后续延续。
- 教程原文的“一半(取你更喜欢的一半即可)”指上一时代轮播音乐的一半;热度排序只是可选的筛选细节,不是固化规则。
- 权重机制:决定“同一时代播放列表内部各曲目的相对权重”:
- Ancient:主题曲与轮播音乐等权。
- Medieval:中古主题曲 50,其余曲目(含延续曲)各 10。
- Industrial / Atomic:本时代主题曲 60,其余曲目(含延续曲)各 10。
- ⚠️ 共享禁忌:同一
MusicSegment严禁在多个容器下以相同 GUID 声明——Wwise 只承认第一个父级,其余容器引用该段的播放项会被静默丢弃(实机复核)。跨容器复用必须生成全独立 GUID 副本(segment/track/AudioFileSource/clip/cue 全新)。
2. 预命名规范(用户整理素材时约定,脚本按此路由)
- 文件名 = 事件名主体(去扩展名),只用 ASCII 字母数字下划线。
- sfx:
<前缀>_SFX_<用途>_<序号>.wav或任意稳定名;事件自动生成Play_<名>/Stop_<名>成对。 - voice:按教程惯例
<前缀>_<场景>(如FELI_JK_FIRST_MEET_A);同名事件改名由wwise_wire.py --rename-event批量完成。 - bgm(文明音乐)按三组命名:
- 轮播背景音乐:
<CIV>_<时代>_Ambient<序号>.wav(如Feline_Ancient_Ambient1); - 外交背景音乐:
<CIV>_Diplo.wav(一个文明一首,如RAGUNNA_Diplo.wav); - 主题音乐:
<CIV>_<时代>_Theme.wav(如Feline_Medieval_Theme.wav)。 - 事件沿用模板
Play_Music_<CIV>/Play_Leader_Music_<CIV>等既有命名。
- 轮播背景音乐:
- 素材池阶段豁免:查重/分区/响度整备阶段保留原始曲名(用户需辨认曲目)并输出 assignment 清单;进入 ③ 导入 Wwise 前才改上述规范名。
- 单文件 ≥30s 默认流式(Stream),短音效默认内存驻留;可用
--streaming/--nostream覆盖。
2.5 响度策略(校准基准 + 组内相对均衡)
- 绝对响度:(
references/calibration.json):voice -21 / quote -23.5 / BGM 远古 -28 / BGM 后世 -25 / sfx 普通音频默认 -27 LUFS(乐队多轨叠加实测确认,接近远古 BGM -28 且多轨并播整体更平衡)。已知替代风格:BS 系热母带(BGM ≈ -12,已入游戏验证),沿用该家族风格时用--i覆盖。 - 数据来源(基准的出处):脚本读的生效值是
references/calibration.json;该文件的基准由references/calibration_raw.json(原始响度实测记录)推导而来 —— 改基准或复核某个数值时两个文件对照着看。 - BGM 时代响度分配(2026-09 用户定案):
-28/-25按素材所属时代分,不按播放时代分——远古时代素材全部 -28(远古主题曲即贯穿全局的主题曲,后续时代容器复用时保持 -28,无需另做 -25 副本);中世纪及之后时代的素材全部 -25(含各自主题曲)。轮播曲跨时代复用只体现在 Wwise 容器权重配置,不改变文件响度。 - ⚠ 同一播放容器内严禁混排两档基准:上一条是文件层的规则;而轮播机制(§1.5)会把本时代素材 + 上一时代素材塞进同一个时代容器,-28 与 -25 一旦同容器就是逐曲跳 3 dB 台阶(听感:忽大忽小)。定案:同一容器(同一时代列表)内所有曲目必须落在同一基准上——沿用 -28/-25 时代分配时,跨时代复用进同一容器的素材也要归到该容器的基准。验收靠
audio_check.py的[POOL]段,它会直接点出"疑似双基准混排 + 两簇中心与间隔"(实机教训:某 BGM 包 97 首被判出 低簇 n=24 中心 -28.00 / 高簇 n=73 中心 -25.00 / 间隔 2.80 dB)。 - 短时响度锚(
--mode shortterm):轮播池(BGM 轮播组)不要用纯积分 LUFS 对齐。积分响度相等 ≠ 听感相等——实测同一批交付 BGM 积分极差仅 3.10 dB 时,中位短时响度 S_p50 极差高达 7.60 dB,corr(LRA, S_p50−I) = -0.555(动态越大的曲子,典型听感越低于它自己的积分值)。--mode shortterm以 S_p50 为目标、用恒定增益(volume=滤波器)对齐,结构上不可能产生泵浦,并自带真峰夹紧(不越--tp)。 - 绝对响度与原始音量无关:loudnorm 归一化 = 响度测量 + 增益补偿,最终 LUFS 与原始峰值/响度无关。原始差异只影响:① 原始过静提增益后可能触发限幅/底噪放大;② 已削波素材降增益也救不回失真。处理后音色与相对动态不变。
- 相对响度(
--mode relative):同批次同用途素材以批内中位数为锚,target_i = 锚点 + retain×(原值−锚点),retain 默认 0.10(90% 矫正、保留 10% 偏差)——从高往低压但不强行平均,保留排序与动态。(15 轨批实测:散布 3.2dB → 残余 0.3dB) - 声道中间件(实测修复 3dB 偏差):
audio_normalize.py对“源声道数 ≠ 目标声道数”的素材(如 voice 收立体声录屏、bgm 收单声道源)会先自动生成目标声道 PCM 中间件,再对中间件做 loudnorm 测量与均衡,保证测量对象 = 最终输出对象。(旧流程成品整体低 3dB,脚本内已闭环,无需人工预降混。) - 若单轨内部乐器失衡(如混音里笛子偏响),per-track 响度无法修,需回源分轨重混。
- 淘汰件与母带一律备份隔离,禁止直接删除:查重淘汰件移入专用隔离目录,原始母带移入归档目录,由用户验收后自行清理。
2.5a 三道安全闸(audio_normalize.py 已内建;违反即返工)
- 幂等闸
--idem(默认 0.3 dB):已落在目标 ±0.3 dB 内的文件直接跳过,不做任何处理([SKIP])。 为什么:对已达标素材再跑一遍,增益是 0,唯一产物是风险。实测某 BGM 批次 97 个源文件里 91 个本已精确落在 -28.00/-25.00(Ancient_Ambient01源 = -28.00、Atomic_Ambient01源 = -25.00、Medieval_Ambient01源 = -24.99),这一遍均衡纯属空转;而其中 6 个文件正因为被"多跑了一遍"而在实机里出现忽大忽小。 - 线性闸(预检 + 后验,默认开启):
linear=true在 ffmpeg 里是**"允许"不是"保证"**。af_loudnorm.c的init()原文:
不满足即静默改走时变增益——不报错、不告警,听感就是泵浦(呼吸感)。offset = s->target_i - s->measured_i; offset_tp = s->measured_tp + offset; if ((offset_tp <= s->target_tp) && (s->measured_lra <= s->target_lra)) { s->frame_type = LINEAR_MODE; // 否则落到 FIRST/INNER/FINAL_FRAME = 时变增益 }- 预检:脚本复刻上式,不通过就拒写该文件(原文件零改动)、逐条给出原因,进程退出码 2。
- 后验:
print_format=json后uninit()会输出normalization_type = linear|dynamic,这是权威判据;报dynamic一律判失败并丢弃产物。 --lra是双重身份:既是"目标动态范围",也是"是否允许线性"的开关。源 LRA 超它就会被降级——放宽--lra(如 20)即可放行并保留原动态;--allow-dynamic才是在知情前提下接受泵浦。
- 短时闸:轮播池走
--mode shortterm(恒定增益路径),结构上无泵浦可能。
验收口径:audio_check.py 现已输出 LRA / S_p50 / 同池离散度([POOL] 段)与"泵浦风险文件"计数。只查积分 LUFS 是查不出泵浦的——泵浦文件的积分响度完全达标(实测:一个被时变增益处理过的文件,积分精确 -25.00 LUFS、看不出任何异常,而轨内增益在 3 秒窗上摆动达 5.95 dB)。
2.8 ShortID 注册表 + 纯 Python bank(⚠️ 实验性,正式交付走 Vorbis)
正式音频交付一律走 Wwise Vorbis 流式(new_bank_project.py + wwise_wire.py → WwiseCLI 生成)。纯 Python ADPCM 打包已被实机验证废弃(ffmpeg 标准 MS ADPCM 内嵌后游戏按 WWise 私有格式解码 → 噪音+闪退),audio_pack.py 的 speechbank/plainbank 仅作结构与 ShortID 研究,不得用于正式交付。
本 skill 保留两项机制:
S=<skill目录>/scripts
# ✅ 有效:ShortID 注册表(防冲突基础)
python $S/audio_pack.py scan
# ⚠️ 实验:只做结构/bank 研究,勿用于交付(运行时会打印警告)
python $S/audio_pack.py speechbank <wav目录> --out <Audio目录> [--bank 名]
python $S/audio_pack.py plainbank <wav目录> --bank <名>_Bank --out <Audio目录> [--prefix Play_]
- ✅ ShortID 规律(有效):Event ShortID = FNV-1(32)(小写事件名),区分不同对象/音频靠唯一事件名前缀;媒体/声音/动作/容器 ID 从
state/id_registry.json避让分配,杜绝不同内容 ID 撞车。scan命令产出注册表,供new_bank_project.py/wwise_wire.py建 Wwise 工程时参考防冲突。 - ⚠️ 编码(废弃):不可用于交付(原因见本节首段)。
- 模板
assets/template_slim/template_speech.bnk现在仅作实验参考;正式交付用完整模板工程(ensure_template.py拉取)。 - 产物:实验产物
.bnk/.xml/.txt+_Banks.ini仅用于研究,不要拿去register_to_mod.py注册交付。
2.9 第 0 步(可选):素材整备(源池 → 规范素材池)
素材来源为网易云 ncm 加密、多目录混杂、存在重复或需要按时代分区时,先做整备再进五步标准流程:
# ⓪a ncm 解密(默认输出 <源目录>/converted;ffprobe 复核通过且显式 --delete-source 才删源)
python $S/ncm_decrypt.py <ncm文件或目录...> [--out DIR] [--delete-source] [--selftest]
# ⓪b 指纹查重(24对数频带×64时间片指纹 + 响度包络相关 + 时长比;默认只报告,--quarantine 才移动)
python $S/audio_dedupe.py <文件或目录...> [--auto 0.975 --env 0.985 --dur 0.99] \
[--prefer 子串] [--quarantine DIR] [--cache DIR] [--jobs 4] [--report PATH]
# ⓪c 音乐特征分析(BPM/响度/动态/亮度/打击密度 + 唤醒度评分,四分位=四时代归属建议,CSV 落盘)
python $S/music_features.py <文件或目录...> [--out features.csv] [--cache DIR] [--jobs 4]
- 判同阈值:fp≥0.975 且 env≥0.985 且时长比≥0.99 → 同一母带(本地 129 文件/32 对判同实证)。同曲不同混音、昼/夜变体、分轨(Layer/backing)落在疑似区,保留并报告,不自动删。
- 人声/伴奏守卫:名称一边含 伴奏/instrumental 而另一边不含 → 即使高相似也只进疑似清单。
- 质量排序:无损>有损 → 采样率 → 位深 → 时长,全平取
--prefer子串匹配方(如网易云官方曲名)。 - 分区建议仅供参考:唤醒度=0.40×BPM+0.30×RMS+0.15×打击密度+0.15×亮度(z 分数加权),与曲名语感冲突时以人工微调为准,不影响格式合规。
- 批量调用用
--filelist传路径清单,禁止 shell 字符串拼接(中文/空格路径会因命令替换不做引号移除而断裂)。
3. 标准流程(五步,逐步对应脚本)
S=<skill目录>/scripts
# ① 核验(格式/采样率/声道/时长/LUFS 体检表;--fix 自动转 48k/单双声道;--json 程序化验收)
python $S/audio_check.py <素材路径...> --category sfx|voice|bgm [--era ancient|later] [--fix] \
[--filelist F] [--jobs N] [--json PATH]
# ② 响度均衡(absolute=校准基准;relative=组内 90% 矫正保留 10% 偏差;就地写+备份;--out 模式统一 .wav 后缀)
python $S/audio_normalize.py <素材路径...> --category voice|quote|bgm|sfx [--era later] \
[--mode absolute|relative] [--retain 0.10] [--i -23] [--out DIR] [--filelist F] [--jobs N]
# ②a 仅保留有效部分:ShortID 注册表刷新(speechbank/plainbank 为实验,正式交付勿用)
python $S/audio_pack.py scan
# ③ 正式打包统一走 Wwise Vorbis:建 Wwise 工程并导入(sfx 全自动;voice/bgm 用 --template 指向用户已复制好的教程模板副本)
python $S/new_bank_project.py --proj <新工程目录> --bank <名>_Bank --media <素材目录> [--template <模板工程>] [--generate]
# ④ 接线/改名/生成(用户 GUI 导入保存后的增量接线也走这里)
python $S/wwise_wire.py <工程目录> --scan
python $S/wwise_wire.py <工程目录> --wire <bank名> --events-wu <Events工作单元名> [--only 子串] [--dry-run]
python $S/wwise_wire.py <工程目录> --rename-event 旧名 新名
python $S/wwise_wire.py <工程目录> --generate <bank名>
# ⑤ 注册到 mod(自动定位 P1 源工程 .civ6proj / P2 运行目录 .modinfo,双向同步)
python $S/register_to_mod.py --bank-dir <工程>\GeneratedSoundBanks\Windows --bank <名>_Bank \
--find <mod名> [--section ingame|global|menu] [--dry-run]
# ⑤b 注册后语义校验(每次注册/交付前必跑, 期望 PASS)
python $S/register_to_mod.py --verify --audio-id <id> --mod <mod目录>
.bak_*口径(与家族「不静默备份」的差异,如实记录):就地写模式会在原文件旁留一份.bak_*副本, 分布在两类文件上,处理方式不同:
- 素材 / 母带 / 工程内部文件(有意例外,改坏可回退):
audio_check.py --fix→.bak_check、audio_normalize.py→.bak_norm、leader_timeline.py→.bak_ast、wwise_wire.py/music_wire.py→.bak_wire、build_combination_bank.py→.bak_rebuild。这些副本不入库、不进交付包, 验收后自行清理(同 §2.5 的「淘汰件与母带备份隔离」口径)。- 项目主体文件(历史只交给 git):
register_to_mod.py→.bak_reg、unregister_audio.py→.bak_unreg落在.modinfo/.civ6proj旁边,而这两个文件是 git 管理的项目主体, 家族与工程口径均为「默认不建立备份副本」。→ 提交 / 交付前删掉这两类副本,不要随包分发。
4. 脚本清单
| 脚本 | 职责 | 依据 |
|---|---|---|
ncm_decrypt.py |
网易云 ncm 解密→裸流(AES+RC4 变体密钥流,klen=128 布局变体兼容);ffprobe 复核通过且显式 --delete-source 才删源;--selftest 往返自检 |
anonymous5l/ncmdump 参考实现(taurusxin 复刻版)+ 本地 54/54 实证 |
audio_dedupe.py |
跨目录音频指纹查重:判同/疑似/人声伴奏守卫,质量排序,淘汰件备份隔离(默认只报告) | 本地双库查重实证(129 文件 / 32 对判同,已知歌曲对校准阈值) |
music_features.py |
BPM/响度/动态/亮度/打击密度特征 + 唤醒度评分与四时代分位建议(CSV) | 本地 97 首 BGM 时代分区实证 |
audio_check.py |
RIFF/PCM/48k/声道/LUFS 体检 + --fix 容器纠正;LRA / S_p50 / 同池离散度 [POOL](含"双基准混排"判定);--lra-cap/--pool-spread;--filelist/--jobs/--json;UTF-8 子进程解码/输出自适应 |
教程 11 章最佳实践 + 中文 Windows 实测修复 + 2026-09 泵浦/听感离散度事故(§2.5a) |
audio_normalize.py |
loudnorm 双遍响度均衡;幂等闸 --idem、线性闸(预检拒写 + normalization_type 后验,拒写时退出码 2)、短时锚 --mode shortterm;--out 统一 .wav 后缀;--filelist/--jobs;声道中间件自动对齐测量/输出 |
教程:语音-21、远古 BGM-28、后世-25 + 立体声语音实测修复 + af_loudnorm.c init() 线性判定 + 2026-09 重复归一事故(§2.5a) |
new_bank_project.py |
克隆模板→独立 bank 工程(导入+work unit 注入+可选生成) | 教程模板工程 + 本地实证 XML 模式 |
wwise_wire.py |
scan/wire/rename/generate | 本地 mod 实战交付验证 |
register_to_mod.py |
.civ6proj(CDATA UpdateAudio + Content 条目)与 .modinfo(UpdateAudio + Files)双注册 + Banks.ini;--verify 语义校验(UpdateAudio 只指向 ini) |
本地双案例(.civ6proj 与 .modinfo 各一)实证 |
build_combination_bank.py |
组合式 bank:N 分轨 + 全组合 Play/Stop 多动作事件(引擎并轨,UI 零改动) | Wwise 多动作事件 schema |
music_wire.py |
fade 淡出检测 / ExitCustom 自动落点 / 时代权重(教程规则) | 模板 MusicCue CueType=2 + Weight=Real64 实测 |
leader_timeline.py |
领袖 .ast 时间线:6 槽位 FXName + Duration=语音时长+pad(三案例路由) | civ6-asset-forge → reference/leader-2d.md 模板 ast 实测 schema |
unregister_audio.py |
移除音频注册(modinfo UpdateAudio/Files、civ6proj CDATA/Content,可选清源目录) | 与 register 互逆 |
ensure_template.py |
模板缺失时询问用户并从教程仓库拉取,写 local_paths.json 自适应 | 致谢章节同源 |
wwise_shortid.py |
WWise ShortID 核心库:FNV-1 事件哈希 / .bnk 解析 / 字节模板重建 / ID 注册表 / WAV→WEM 编码 | 内化自早期独立打包库(8/8 事件 ID 与成品一致实证);ShortID 机制有效,编码部分不用于交付 |
audio_pack.py |
ShortID 注册表刷新(scan,有效)+ 实验性纯 Python bank(speechbank/plainbank,⚠️ ADPCM 已实证废弃,勿交付) | 内化自早期独立打包 CLI;正式交付走 Wwise Vorbis |
paths.py |
库(无独立子命令,被本 skill 其余脚本 import):读 skill 根目录 local_paths.json,支持 CIV6_* 环境变量覆盖;直接运行 = 打印本机路径配置自查(各键缺失时会指出写哪、写什么) |
本 skill 自有的路径解析器(不是 civ6-modding/tools/_paths.py) |
5. 注册产物基线(缺一不可)
<mod>\Platforms\Windows\Audio\
├─ <名>_Banks.ini # [InGame] 下列 bank;主菜单要响移 [Global]
├─ <名>_Bank.bnk/.xml/.txt
└─ <ID>.wem … # bank xml 内引用的流式媒体,逐个拷入
.modinfo:<InGameActions> 开标签后插入 <UpdateAudio id="…"><File>Platforms/Windows/Audio/….ini</File></UpdateAudio> + <Files> 补条目。
.civ6proj:InGameActionData CDATA 内 </InGameActions> 前插单行 UpdateAudio(正斜杠)+ <Content Include="Platforms\Windows\Audio\…"><SubType>Content</SubType></Content> 条目。
6. 触发端速查(注册完成后接线用)
- Lua(仅 UI 上下文):
UI.PlaySound('Play_X');无 StopSound API,停止=播放Stop_X事件(媒体协调器 MediaStop 模式:播放前 MediaStop→Play_→Stop_ 转换播放)。 - 数据库列直接填事件名:
UnitCommands.Sound/UnitOperations.Sound/Buildings.QuoteAudio/TechnologyQuotes|CivicQuotes|LeaderQuotes|Features.QuoteAudio/GreatWorks.Audio(引擎自动加 Play_ 前缀)/TurnSegments.Sound/RandomEvent_Presentation.Sound/LoadingInfo.PlayDawnOfManAudio。 - artdef:Audio 集合(3D 位置音)+ 教程时间线 Sound 事件(领袖语音)。
- 测试:civ6-tuner(FireTuner TCP:4318)执行
UI.PlaySound('Play_X')。
7. 已知边界
- 交互音乐容器内部的素材不自动接线(由容器/switch 管理),
--scan会列出但需人工确认。 - 语言本地化语音按
语言/<同名bank>约定铺文件;语言列表见教程模板的 Speech 工作单元。 .civ6proj的 Cooked 产物由 ModBuddy 构建生成,本管线只改源工程与运行目录,不模拟 ModBuddy 构建。- 纯 Python ADPCM 打包已废弃(原因与影响面见 §2.8)。
- 纯 Python 路径不做多语言 per-language bank。
致谢
本管线的模板工程、音频导入流程与预处理标准来自 优妮《小优妮的文明6模组笔记》(Civ6_Modding_Textbook)第 11 章「音频」与配套 WWiseProject 模板的无私分享:
https://github.com/dwughjsd/Civ6_Modding_Textbook。GUI 半自动步骤均以该教程为权威依据,感谢原作者对社区的支持。
模板自适应(分享后在新机器上运行)
全新机器首次使用前先确认:
ffmpeg/ffprobe已加入 PATH(素材核验/响度均衡/时长探测依赖;缺失时脚本会报找不到,请先安装)。- Wwise 2015.1.9 已安装,且
local_paths.json的wwcli指向WwiseCLI.exe(脚本缺失时给出明确提示)。 git可用于ensure_template.py自动拉取完整模板;没有 git/网络时手动下载教程仓库并配置template_full。- 可选 Python 依赖:
numpy(audio_dedupe.py/music_features.py)、pycryptodome(ncm_decrypt.py),用到对应步骤时再pip install。
需要完整模板(voice/bgm 的 GUI 容器工作)而机器上没有时:先询问用户,确认后执行
python scripts/ensure_template.py --to <目标目录>——从上述仓库git clone --depth 1拉取并复制WWiseProject/FelineJasperKitty(含媒体)。拉取后脚本会把新路径写入 skill 目录
local_paths.json(键template_full),ensure_template.py检测与audio_pack.py scan会自适应;调用完整模板时用new_bank_project.py --template <完整模板路径>或直接读取该键。实验性纯 Python bank(
audio_pack.py speechbank/plainbank)默认使用assets/template_slim/template_speech.bnk(20KB,纯结构、无成品音频);state/id_registry.json是本机状态,不随 skill 分发:接收方首次跑audio_pack.py scan(或在 Wwise 打包前维护注册表)后开始累积自己的已用 ID 表;字段含义与维护规则见state/README.md。
作者与致谢
- 作者:千与千寻瀑
- 致谢:优妮、岛村卯月、UzukiShimamura