Communitygithub.com

日文歌词字幕:断句、出字时刻、表记、排版

为音乐视频构建日本读者实际阅读方式的日文歌词字幕:在文節边界断行,按实测歌唱起始时间显示每个短语(而非平均分配音节),使用漢字假名混写而非全假名,并验证该行的每个字符也出现在背景排版中。

¿Qué es 日文歌词字幕:断句、出字时刻、表记、排版?

这个技能(skill)专门解决日文歌词字幕中常见的四种错误:按字数平均分配时间、在词中间断句、使用全假名表记、以及将出字时刻吸附到音高变化点。它提供了两条核心脚本:一个基于pyin和能量门控的实测起始时间提取器(附带短语结束误报修复),和一个覆盖率检查器。技能详细描述了如何通过人声分离轨、音高检测、能量分析等步骤精确测量每个文節的歌唱起始时间,并强调了验证时间窗口本身(使用换气间隔重新锚定每一句)比句内断句更上游也更容易被忽略。此外,还涵盖了表记(轉換為漢字假名混写)、排版参数(基于商业MV的量化分析)、竖排多列排版规则、自检流程、强制对齐工具对比、以及字体授权等关键议题。该技能适用于Claude Code、Cursor、Codex等AI代理平台,帮助开发者生成符合日本观众阅读习惯的专业级日文歌词字幕。

Compatible con~Claude Code~Codex CLI~Cursor
npx skills add CatsMansion/mv-jp-lyrics-subtitle

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

¿Qué hace 日文歌词字幕:断句、出字时刻、表记、排版?

0. 这个 skill 解决什么

日文歌词字幕最常见的四种错,全都是"看着还行、日本人一眼就错":

  1. 按字数均分时间 —— 一句 5.7 秒 16 个音节,平均每音 0.36 秒。实际唱起来长短差 3 倍。
  2. 断在词中间 —— 君 / を見るたび。助词不能单独起头。
  3. 全平假名 —— 歌词表为了给 TTS/生成模型用而写成全假名,直接贴上屏日本人读起来像儿童读物。
  4. 出字时刻吸附到"音高变化点" —— 会把句末收音符当成下一句的起点,整句提前半秒。

1. 八条硬规则

#规则反例
R1断点只落在文節边界。名词+助词、动词+助动词不得拆开✗ 君/を見るたび ✗ 見/てるのに
R2每段出字时刻=该段首音节实测起唱。⛔ 禁止按字数均分✗ (end-start)/n × k
R3一句最多 3 个出字段,优先 2 段✗ 君を/見る/たび
R4不显示标点;段间用全角空格  ✗ 君を、見るたび
R5禁则(JIS X 4051):小假名 ゃゅょっ、长音 ー、促音、闭括号不得起行✗ 行首出现 っ
R6单行 ≤ 16 字(Netflix 日文字幕口径)—
R7出字后不消失(累积显示),否则观众失去上下文✗ 出第二段时抹掉第一段
R8背景大字必须包含该句全部字,逐字比对 missing 必须为空✗ 背景只有后半句

R1 为什么是文節而不是"词":日文没有空格,机器断行只会在框宽处断。 人读的时候是按文節呼吸的 —— 君を/見るたび 正好是两次呼吸。 判断方法:把这段单独念出来,如果像一次换气,它就是对的。


2. 实测出字时刻(核心,别用估的)

2.1 前置

需要人声分离轨(Demucs / htdemucs 的 vocals.wav)。混音轨上有鼓和贝斯,能量峰值全是鼓点。

python scripts/measure_lyric_onsets.py --vocal vocals.wav --lines lines.json --out timing.json

lines.json 长这样(kana 用于算音节数定预期位置,disp 是上屏表记):

[{"id": 9, "start": 42.26, "end": 48.00,
  "kana": ["きみを", "みるたび", "しんぞうがとまるの"],
  "disp": ["君を", "見るたび", "心臓が止まるの"]}]

2.2 算法(三段,缺一段就会错)

  1. 取 F0:librosa.pyin(fmin=150, fmax=900, hop_length=128),再对 F0 做中值滤波去掉颤音。
  2. 取候选边界:音高跳变 ≥0.8 半音 ∪ librosa.onset.onset_detect(delta=0.30, wait=3)。
  3. ⛔ 门槛过滤(最关键的一步):候选点必须同时满足
    • 之后 40ms 能量不低于之前 40ms 的 55%(不是骤降)
    • 之后 180ms 内 F0 非 NaN 的帧占比 ≥60%(还有人在唱)

然后:把每个文節按"累计音节数 / 总音节数"算出预期相对位置,吸附到最近的合法边界。

2.3 为什么第 3 步不能省(实测反例)

「君を見るたび 心臓が止まるの」的 び 在 44.87 秒收尾降调(F2# → 收),静音 0.33 秒后 しんぞう 才在 45.24 起。

  • 只做第 2 步 → 边界抓到 44.81(び 的降调),把「心臓が止まるの」提前 0.43 秒。
  • 只做第 3 步的第一条(40ms 能量比) → 仍然错。44.81 处能量还没掉下来,40ms 窗口太短。
  • 加上「之后 180ms 仍有发声」 → 45.17,与手工核对差 0.07 秒。✅

⇒ "之后还有没有人在唱"比"这一刻能量高不高"更能区分换字与收句。

2.4 什么时候这条也会失效

  • 英文句:没有音高平台,pyin 测不出音节边界。只能按时长比例分,并在交付里明确标注"估的"。
  • 一字多音的连唱(melisma):音高平台数 > 音节数,吸附会偏。需要人工听。
  • 吸附偏差 > 0.45 秒的句子:一律标 UNSURE 交给人工,不要当成测准了。

2.5 ⛔⛔ 先验证"每句的时间窗"本身,再谈句内断句

这是比 §2.3 更上游的坑,也是最容易被忽略的。

绝大多数歌词时间轴文件(lyrics.ts / SRT / LRC)的 start/end 是按行数线性切分或手工估的, 不是逐句听出来的。带这种文件做字幕,句内测得再准,整句也是错位的。

症状:字幕整体比人声早或晚 1–4 秒,而且越往后漂得越多(因为是线性拉伸)。 用户在副歌听不出来(副歌窗口通常是对的),一听主歌前几句就发现不同步。

修法:用换气间隔重新锚定每一句

人声分轨上,句与句之间一定有静音。这是最硬的分界,不需要任何模型:

rms = librosa.feature.rms(y=vocal, frame_length=1024, hop_length=128)[0]
e = rms / rms.max()
quiet = e < 0.055                      # 相对阈值,实测够用
# 连续 quiet >= 0.16s 记为一个换气间隔

然后:相邻两个间隔之间的有声段,就是一个乐句。

实测例子(同一首歌):

间隔 0.00-12.12(前奏无演唱)
有声 12.12-13.73  ← ドアのとこで
间隔 13.73-14.67
有声 14.67-16.59  ← 立ち止まってる
间隔 16.59-17.07
有声 17.07-17.98  ← 本当は

而歌词表写的是「第 1 句 12.00–15.75」「第 3 句 19.50 起」——第 3 句实际 23.63 起,早了 4.1 秒。

⭐ 判据:把检测出的有声段数,和这一段歌词的文節数比。

  • 数量相等 ⇒ 这个歌手是"一文節一换气",直接 1:1 对应,精度到毫秒级,不用猜。
  • 数量偏少 ⇒ 有连唱,再用 §2.2 的音高法找句内边界。
  • 数量偏多 ⇒ 阈值太松,调高 0.055 或加长 0.16。

⛔ 不要用"能量峰计数"代替换气检测:一个长音里的颤音会产生多个峰。 实测 16 个音节的一句子被数出 33 个能量峰,完全没法用。

⛔ 也不要用"滚动中位数归一 + 浅阈值"去找弱换气:主歌里会冒出 23 个假间隔(真值 19), 噪声和真换气分不开。宁可只锚定有硬静音的那些句,剩下的明确标"重映射/估"。


3. 表记:漢字仮名交じり

歌词表(尤其是 AI 生成歌曲的歌词稿)常写全假名。上屏必须转常用汉字表记。

全假名(歌词稿)上屏表记
きみをみるたび君を見るたび
ほんとは / きみのほう本当は / 君のほう
なかに中に
においだけで匂いだけで
いちばんとおい一番遠い
こわれちゃいそう / ちかづくと壊れちゃいそう / 近づくと
たちどまってる立ち止まってる
わたしのあかし私の証

⛔ 不要过度汉字化:すきだよって 不要写成 好きだよって…其实要写(好き)。 但 なっちゃう、ずるいよね、なんてかな 这类口语副词/终助词保持假名 —— 全改汉字反而生硬。 判据:这个词在日本人的小説/歌詞排版里通常写汉字吗? 不确定就保持假名。


4. 排版参数(有出处,别自己发明)

以下数值来自对四支商业二次元 MV 的量化反向工程(1280×720 基准换算到 1080p):

项1080p 值
主歌词字高97~108px
旁白小字53px
副歌强调句124px
巨型背景字270~670px
底部基线y ≈ 950~1004
单行最宽≤1747px(≤91% 画宽)

⭐ 最重要的发现:商业 MV 的主歌词普遍"无底板",白字 + 2~3px 深描边。 只在强调句才用通栏白带 + 黑字。给每一句都加圆角底板是业余感的主要来源。

配色不要中途换调:一支片只有一个主色相,副歌只加饱和不换色相。

4.1 竖排多列 = 断句可视化

横排用全角空格分隔,观感上等于"没断句"。竖排时把每个文節排成一列,从右往左, 断句就变成版面本身:

君を | 見るたび | 息が止まるの
右列    中列        左列

列位固定按文節序号算(第 k 段永远在第 k 列),新段出现时已有列不位移 —— 否则每次出字整块文字会横跳。

4.2 竖排的物理上限(先算再定字号)

一行 n 字、行距比 1.46:所需高度 = n × 字号 × 1.46。 1080 画高、可用 940px ⇒ 字号上限 = 940 / (n × 1.46)。

  • 8 字(心臓が止まるの)→ 79px,达不到 97px 下限
  • 6 字 → 107px ✅

⇒ 超过 7 字的长句,竖排放不进主歌词字号区间,要改横排两行。 先算,别排完才发现。


5. 自检(每次都要打印,不看就交付必翻车)

python scripts/check_lyric_coverage.py --timing timing.json --disp disp.json

输出必须同时满足:

COVER line='君を見るたび' missing=[] OK=True
ORDER sub_a='君を' sub_b='見るたび' concat_equals_full=True

missing 不为空 ⇒ 这一版不许交付。ORDER 检查拼接后是否等于整句(防漏字/重字)。


6. 开工前必做的一件事

先在项目文档里 grep 一遍字幕规格。 本项目已经裁定过的字号、色板、有无底板, 通常躺在 02_文档/ 或 docs/ 的某份拆解/裁定里:

grep -rn -E "字幕|字体|排版|几何|字高|描边|底板" docs/ 02_文档/ | head -40

⛔ 不要"我觉得这样更好看"就自己定一套 —— 那一定会和已裁定的规格冲突, 而且返工成本是整个片子重渲。这个 skill 的第 4 节就是被这条教训逼出来的。


7. 实测记录(供校准预期)

一支 163.994 BPM 的日文歌,副歌首句:

音节きみをみるたび
起唱(s)42.3142.7643.0943.4143.6143.9844.18
音高B3E4F#4G#4F#4F4F#4

7 音节 ↔ 7 个音高平台一一对应 ⇒ 这首歌副歌是一字一音。 换一首连唱多的歌,平台数会少于音节数,吸附就要标 UNSURE。


8. ⛔ BPM 不是网格(实测漂移)

一首歌的 bpm 字段只能当标签,不能拿来算切点。 实测:某曲 128.28 → 132.39 BPM(beat 468ms → 453ms),固定网格到第 30 小节就整整差一拍。

  • 用 beats[](已吸附到 onset)+ bars[].start 取真实时间
  • beats 用于打击点(切镜/闪/砸字);beats_smooth 用于连续运动(相机速度/循环)
  • ⛔ 永远不要写 t0 + n * 60 / bpm
  • 小节号 ≠ 数组下标:有 pickup 小节时 bars[].index 与位置不同,按 index 查

9. ⛔ ASR 的单词时间戳偏早,不能当出字时刻

实测偏差 0.04–2.0 秒,中位 0.62 秒;长音之后最差。 ⇒ 用 ASR 只能取每句的近似起点(±1s),精确出字必须用强制对齐(MMS CTC / torchaudio)。 判据:每句起唱误差 ≤ 0.15 秒才算测准。

10. ⛔ Whisper 幻觉清单(出现即删)

在静音、气口、vocalise 处会生成:

  • 「ご視聴ありがとうございました」(曾覆盖 0.6–103 秒)
  • 「チャンネル登録…」
  • 「作詞・作曲・編曲 初音ミク」
  • 重复短词、ラララ…

⇒ 规则:官方歌词为准;无官方歌词才用转写,且逐句与官方稿对照(差异法看不见"整句缺失")。 ⇒ VAD 要关掉(Silero 误判歌唱元音);分离出的人声轨 > 任何 DSP 增强。

11. LRC 标记(歌词动画的mini 语言)

标记作用
词组A/词组B强制切行,两半成为不同 beat
*词*强调:更大 + 冲击部件 + 强调色
行尾 !キメ行(最强处理,可带闪/震)——副歌每段 1–2 行
[間奏]器乐行:阻止上一句占着空隙不消失

⛔ 不要重复抵消提前量:歌词引擎通常已内建「比人声早 0.2 秒」; 若你的时间轴已经减过 0.2,这里再加一次就会早 0.4 秒。

12. 副歌收尾的 stop 小节

副歌末尾乐队撤空、近乎 a cappella 的那几个小节,是全片最重要的画面该放的位置, 不是切走的时机。别把它当"这里没鼓所以随便放点什么"。


13. ⭐ 五个强制对齐工具在日文歌词上的实测对比

出处:第三方 benchmark(20 段,含中/英/日),下表取日文列。

工具起点误差(中位)终点误差(中位)覆盖率判
MMS_FA(torchaudio)0.08 s0.11 s8/8✅ 唯一达标
whisper-timestamped—0.16 s8/8⚠️ 次选
WhisperX—1.67 s(p90 2.81)8/8❌ 覆盖全但边界飘
NeMo NFA——0/8❌ 无日文 pretrained CTC
aeneas2.70 s3.66 s8/8❌ 崩盘

⛔ aeneas 不适合唱歌:它自己的文档就写着「Audio is assumed to be spoken: not suitable for song captioning」。 实测日文 2.7–3.7 秒误差,比按字数均分还差。

⇒ 要 ≤0.15 秒,只有 MMS_FA 这条路。 其余都不达标。

⚠️⚠️ 但 MMS_FA 的 License 是 CC-BY-NC(非商用)。我们若上 Steam 商业化,必须另做许可安排, 或只用它做内部对齐、不随产品分发模型权重。这条在商业化前必须确认。

⚠️ MMS_FA 对非拉丁语种靠 uroman 罗马化;日文汉字混排时,长段 p90 会飘到 2.53 s ⇒ 必须逐句核对。

14. 提前量的两个数值不是一回事

数值目的出处
0.15–0.25 s让人读得完(阅读提前量)歌词引擎默认「文字は声より 0.2 秒先」
0.03–0.05 s让人跟得上唱(卡拉OK对齐感)另一来源的歌词视频实践

⇒ 选哪个取决于这支片子是"读的字"还是"跟唱的字"。 我们做的是读的字 ⇒ 用 0.15–0.2 s。⛔ 不要两者叠加。


15. 竖排组版的实际规则(不是"把字竖着排")

规则做法
句読点占一格,写在格子的右上方(横排是左下方)
小假名(っ ゃ ゅ ょ)占一格,写在格内右上方
拗音(きゃ)看作两个假名,各占一格
长音符「ー」占一格
段首空一格
⛔ 禁则句読点不能出现在一行的开头
行末标点如果句末标点落在行末没格子,写在格外或与末字同格
数字竖写用汉字数字;横写可用阿拉伯数字(每格 1–2 个)

⇒ 我们现在的竖排是"每个字一行堆下来",没有处理句読点的位置、小假名的偏移、段首空一格。 这三样加上去,日文读者一眼就会觉得"对了"。

16. 阅读速度判据:4 cps 是广播字幕标准,不是歌词标准(含本项目实测)

⚠️ 可信度分布:数字 B 级(日本字幕认证 AVTpro 的 JA 风格指南、两家字幕公司的公开规格,三源交叉一致); "歌词该怎么用"是我自己的推论,D 级 —— 已明确标注,不许当证据用。

16.1 三个源给出的硬数字(互相一致的部分)

项AVTpro JA(认证标准)GoLocaliseDMPJ取用
最大读速4.0 cps12 字/秒(≈上限口径不同)~4 字/秒4.0–5.0 cps
每行字数(横)15 字16 字13–16 字≤15
每行字数(竖)11 字——≤11
同屏行数2222
最短显示20 帧(0.8 s @25fps)1 s1.5 s≥0.8 s
最长显示6 s(歌词可 7 s)7 s—6 s(歌词 7 s)
标点处理。→ 全角空格;、→ 半角空格——同 AVTpro
字体ゴシック—Gothic 优先(可读性)ゴシック

⭐ 两条验证了我们已有的裁定:

  1. 「⛔ 不用标点」是对的 —— AVTpro 的正式做法就是把标点换成空格(。→全角、、→半角)。 我们直接不加标点,效果等价且更干净 ⇒ R7 不需要改。
  2. 「ゴシック体」是对的 —— 我们用的 Yu Gothic Bold 属于ゴシック,方向没错(但字体授权有问题,见 §17)。

16.2 ⛔ 但 4 cps 不能直接套在歌词上(我的推论,D 级)

4 cps 的存在理由是:对白听不懂就得重看,字幕是唯一信息通道。 而歌词是同时被唱出来的 —— 观众在听到那个词的同一秒看到它,不需要"读完再理解"。

⇒ 歌词的字幕不该按"读完"设计,该按"跟着唱"设计。 (AVTpro 自己也给歌词开了口子:最长显示时长从 6 s 放宽到 7 s,说明行业承认歌词是另一类。)

⚠️ 但这不等于"歌词可以随便快"。超过 ~6 cps 连"跟着唱"都跟不上,因为眼睛找不到当前那个字。 ⇒ 实践判据:峰值 cps ≤ 5.0 可接受,5.0–6.0 要提高,>6.0 必须改排版(拆行 / 两行并出 / 缩短文節)。

16.3 本项目实测(lyric_timing_v2.json,22 行 / 52 文節)

口径结果
按整行(v7 现行 WHOLE_LINE)22 行里 5 行 >4.0 cps,最差 L5 = 5.30 cps(16 字 / 3.02 s)
按文節(若改回逐文節显现)52 个里 11 个 >4.0 cps(21%),最差 L3「ポケットのなかに」6.90 cps
竖排每列字数最长 16 字 → 分两列每列 8 字,未超 11,OK

⇒ 结论分两层,别混:

  • 按 4.0 cps 严格判:5 行不合格,但按 §16.2 的歌词口径(≤5.0)只有 L5 的 5.30 需要处理。
  • ⛔ 若改回逐文節显现,21% 的文節会超速到 5–7 cps ⇒ 这是"整行一起出"这个决定的量化依据 (当初改 WHOLE_LINE 只凭"逐文節会诱发跟着核对同步"的直觉,现在有数字了)。

16.4 ⭐ 顺手查出的真缺陷:L3–L7 的时间轴是等分推算的

症状L3–L7 连续五行时长全部恰好 3.02 s,且首尾严丝合缝(23.63→38.72 = 15.09 s ÷ 5)
根因换气间隔检测在这段(legato 连唱,字与字之间不换气)找不到 gap,REMAP 退化成等分,且没报警
实测反证在 22–44 s 内扫 4 档阈值 × 2 档最短长度,真实静音只有 7 处(22.63 / 23.01 / 24.24 / 25.60 / 28.29 / 31.43 / 41.55),没有一处落在 26.65 / 29.67 / 32.69 / 35.70 / 38.72 上 ⇒ 这五个边界不是测出来的
为什么难发现source 字段只写 REMAP,不区分"有依据的重映射"和"等分兜底"
修法(未做,属程序端)①REMAP 拆成 REMAP_ANCHORED / EQUAL_DIVIDE,后者必须打印警告 ②等分兜底时同时输出 cps,超 5.0 直接判红 ③legato 段改用 pyin 音高连续性定边界,不用静音

⛔ 这条是"判据看着全绿、实际是假的"的又一例:missing=[] 只说明"每行都有时间",不说明"时间是量出来的"。

17. ⛔ 字体授权:我们现在用的是 Windows 系统字体,商用有风险(B 级)

⚠️ 可信度 B(各家字体厂商的正式许可页 / FAQ 原文,逐条读到,不是二手转述)

17.1 四类授权,天差地别

字体视频里烧字幕商用备注
SIL OFL 1.1(Noto/Source Han、M PLUS、Zen Maru、Kosugi、Shippori、侵食明朝…)✅✅ 免标注唯一要遵守:⛔ 不许单独卖字体文件;改作须同协议
IPA Font License 1.0(IPAex、こころ明朝…)✅ 明写"映像内容"可用✅⛔ 衍生字体名不得含「IPA」;再分发须附协议
ヒラギノ(macOS 自带)✅ 产物可贩卖/颁布✅⛔ 但明禁"デジタルコンテンツにおいてフォントとして機能する形態でデータを制作する行為";且绑 macOS,我们在 Windows 上用它 = 越界
日本書技研究所 等商业字库⚠️ 要买需商用授权映像テロップ按资本金 ¥22,000–55,000
ことり文字ふぉんと Pro✅(含 YouTube 收益化)✅⛔ 但"埋め込み/Webフォント"原则禁止 ⇒ 烧进视频 ≠ 嵌入字体,这两件事在不同条款里

⭐ 关键区分(最容易搞错的一条): 「把字烧进画面」和「把字体文件嵌进文档/网页」是两种行为,适用不同条款。 前者产物是像素,一般按"输出物"处理;后者是把字体本体交出去。⛔ 别把 ことり 的"禁止嵌入"读成"禁止做视频"——它自己的 FAQ 里"動画への使用"是明确 OK 的。

17.2 我们现在的实际状态

项值
当前字体C:\Windows\Fonts\YuGothB.ttc(游ゴシック Bold,Windows 自带)
字形方向ゴシック ✅(与 §16.1 的可读性建议一致)
授权状态⚠️ 未核。Windows 自带字体绑 OS 授权,烧进商用成品(Steam DLC)属于灰区,不是明确许可

⇒ 建议换 OFL 字体(零成本、明确可商用、免标注),候选按气质排:

候选许可气质适配
Zen Maru GothicOFL圆润、柔和⭐ 最贴我们的粉紫/可爱调
Shippori MinchoOFL情绪浓的明朝适合抒情句,但与"可爱"有张力
M PLUS 1pOFL几何、现代中性安全
Noto Sans JPOFL最中性兜底,不会错

⚠️ 换字体 = 改观感,必须 Ronny 目检后定,⛔ 不许我单方面换(这是"画面层"改动,按用户级铁律必须他看)。

Skills relacionados