WPLRC 文件格式规范


前言

WPLRC 是 WpfMusicPlayer 使用的中间歌词格式。它是一种采用 UTF-8 编码的 JSON 格式,用于在传统 LRC 输入完成规范化之后,保留歌词文本、翻译、罗马化行以及可选的词级时间控制节点之间的语义顺序。

该格式旨在作为传统 LRC 的后继格式。传统 LRC 缺乏明确的 schema 和统一标准,也无法保留歌词节点之间的语义信息。

当前格式版本为 2

状态

本文档描述 WPLRC 格式版本 2。该格式由 LrcFileControllerNative::to_intermediate_json 生成,并由 WpfMusicPlayer 中的托管歌词处理流水线读取。

版本 2 将罗马化 schema 信息移动到文档根级。旧版版本 1 文档可能包含行级 schemascheme 属性;WpfMusicPlayer 会通过推断占主导地位的根级 romanization_schema,并移除行级 schema 字段,将这些文档升级到版本 2。

文件格式

一个 WPLRC 文件:

  • 必须是有效的标准 JSON。不允许使用 JSON 注释。
  • 应该采用 UTF-8 编码。WpfMusicPlayer 导出不带 BOM 的 UTF-8。
  • 应该使用 .wplrc 文件扩展名。
  • 必须包含一个顶层 JSON 对象。

所有时间值均为以毫秒为单位的整数。

顶层对象

属性类型v2 生成器是否必需说明
format_versioninteger格式版本。当前值为 2
romanization_schemastring全局罗马化记法 schema。有效值为 romajijyutping。未启用罗马化 schema 时应省略。
offsetinteger全局歌词偏移量,单位为毫秒。正值会延后歌词;负值会提前歌词。为兼容性考虑,消费者应该将缺失的偏移量默认视为 0
metadataobject从 LRC 标签转换而来的元数据。可以是空对象。
lyric_linesarray按顺序排列的歌词行节点数组。

消费者应该拒绝 format_version 不受支持的文档。WpfMusicPlayer 当前接受版本 1 用于升级,并将版本 2 作为当前格式。

Metadata 对象

metadata 对象包含可选的字符串字段。生成器应该省略空的元数据字段。

属性来源 LRC 标签说明
artistar, artist艺术家名称。
albumal, album专辑名称。
authorau, author歌词作者。
bybyLRC 文件创建者或上传者字段。
titleti, title歌曲标题。

LRC 的 [offset:...] 标签由顶层 offset 属性表示,而不是放在 metadata 内部。

歌词行对象

lyric_lines 中的每一项都表示一个带时间的歌词组。

属性类型必需说明
time_start_msinteger该歌词组的开始时间。
time_end_msinteger该歌词组的结束时间。
linesarray属于该带时间歌词组的语义行节点。

lyric_lines 应该按 time_start_ms 升序排序。生成器应该确保 time_end_ms > time_start_ms;如果消费者正在处理的节点满足 time_start_ms > time_end_ms,则应该立即失败。如果某个 LRC 行没有显式结束时间,WpfMusicPlayer 会从下一行开始时间、词级控制器时间、可用的歌曲时长,或一个最小的非零回退时长中推导该结束时间。

行节点对象

歌词行的 lines 数组中的每一项都表示一个语义文本行。

属性类型必需说明
rolestring语义角色。有效值为 lyrictranslationromanizationignored
languagestring推荐检测到的语言分类器输出。有效值见下文。
textstringsync 缺失时必需;当 sync 存在时,该字段会被静默丢弃。该语义行的纯文本。
syncstring同步类型。使用 controller_nodes 表示词级时间。
controller_nodesarraysynccontroller_nodes 时必需词级或片段级时间控制节点。

WpfMusicPlayer 当前生成的有效 language 值如下:

  • zh: 简体中文/繁体中文
  • latin: 英语/法语/意大利语/西班牙语,以及/或其他基于拉丁字母的语言
  • jp: 日语,包含平假名、片假名和汉字
  • kr: 韩语,包含谚文字母和汉字
  • ru: 俄语/蒙古语,以及/或其他基于西里尔字母的语言
  • jyut: 粤语粤拼和汉语拼音
  • roma: 日语和韩语的罗马化
  • onomatopoeia: 没有实际语义的拟声词,或其他未识别语言

消费者应该保守处理未知的 rolelanguagesync 值。WpfMusicPlayer 以大小写不敏感的方式读取 role,并使用第一个 lyric role 作为主显示行。如果不存在 lyric role,则回退到该组中的第一个行节点。

控制器节点

当某个行节点包含 "sync": "controller_nodes" 时,该行使用带时间的控制器节点,而不是普通文本时间。

属性类型必需说明
time_start_msinteger该控制器片段的开始时间。
time_end_msinteger该控制器片段的结束时间。
textstring该控制器片段期间显示的文本。

控制器节点时间是文档中的绝对时间,单位为毫秒,而不是相对于父行的偏移量。

控制器同步行应该忽略 text;消费者可以通过按顺序拼接 controller_nodes[*].text 来重构显示文本,并静默丢弃 text 属性。WpfMusicPlayer 会在 text 缺失时执行这种重构。

在高亮进度方面,WpfMusicPlayer 会在每个控制器节点内部进行线性插值,并将当前活动节点索引加上节点内局部进度映射为整行进度。空的控制器节点文本会被托管显示流水线忽略。

出于旧版兼容性考虑,WpfMusicPlayer 也会将 sync: "controller_node" 识别为等价于 sync: "controller_nodes",但版本 2 生成器应该只写入 controller_nodes

Romanization Schema

romanization_schema 是版本 2 中的文档级属性。它描述 role 为 romanization 的行所使用的记法系统。

有效值:

  • romaji: 由 WpfMusicPlayer 歌词引擎分类出的日语罗马字,或韩语风格的罗马化。
  • jyutping: 粤语粤拼,以及/或汉语拼音。

如果 romanization_schema 缺失或未知,消费者应该仅在存在 romanization role 行时将罗马化视为存在,但不应该假定具体的记法系统。

版本 2 行节点不得使用 schemascheme。消费者可以在规范化过程中移除这些字段。

示例

下面是一个完整的 WPLRC 版本 2 文档。该示例是有效 JSON,且不包含注释。

{
  "format_version": 2,
  "romanization_schema": "romaji",
  "offset": 0,
  "metadata": {
    "artist": "sample",
    "album": "sample",
    "author": "sample",
    "by": "sample",
    "title": "sample"
  },
  "lyric_lines": [
    {
      "time_start_ms": 12060,
      "time_end_ms": 13843,
      "lines": [
        {
          "role": "romanization",
          "sync": "controller_nodes",
          "language": "roma",
          "controller_nodes": [
            {
              "time_start_ms": 12060,
              "time_end_ms": 12215,
              "text": "ba "
            },
            {
              "time_start_ms": 12216,
              "time_end_ms": 12371,
              "text": "'d "
            },
            {
              "time_start_ms": 12371,
              "time_end_ms": 12507,
              "text": "do "
            },
            {
              "time_start_ms": 12508,
              "time_end_ms": 12655,
              "text": "ra "
            },
            {
              "time_start_ms": 12656,
              "time_end_ms": 12803,
              "text": "n "
            },
            {
              "time_start_ms": 12803,
              "time_end_ms": 12955,
              "text": "do "
            },
            {
              "time_start_ms": 12956,
              "time_end_ms": 13123,
              "text": "ni "
            },
            {
              "time_start_ms": 13123,
              "time_end_ms": 13275,
              "text": "u "
            },
            {
              "time_start_ms": 13276,
              "time_end_ms": 13428,
              "text": "ma "
            },
            {
              "time_start_ms": 13428,
              "time_end_ms": 13556,
              "text": "re "
            },
            {
              "time_start_ms": 13557,
              "time_end_ms": 13843,
              "text": "ta "
            }
          ]
        },
        {
          "role": "lyric",
          "language": "jp",
          "text": "バッドランドに生まれた"
        },
        {
          "role": "translation",
          "language": "zh",
          "text": "只因诞生于劣地"
        }
      ]
    },
    {
      "time_start_ms": 13843,
      "time_end_ms": 16267,
      "lines": [
        {
          "role": "romanization",
          "sync": "controller_nodes",
          "language": "roma",
          "controller_nodes": [
            {
              "time_start_ms": 13843,
              "time_end_ms": 13987,
              "text": "da "
            },
            {
              "time_start_ms": 13987,
              "time_end_ms": 14154,
              "text": "ke "
            },
            {
              "time_start_ms": 14154,
              "time_end_ms": 14313,
              "text": "de "
            },
            {
              "time_start_ms": 14314,
              "time_end_ms": 14474,
              "text": "ba "
            },
            {
              "time_start_ms": 14474,
              "time_end_ms": 14634,
              "text": "'d "
            },
            {
              "time_start_ms": 14635,
              "time_end_ms": 14786,
              "text": "do "
            },
            {
              "time_start_ms": 14786,
              "time_end_ms": 14938,
              "text": "ra "
            },
            {
              "time_start_ms": 14939,
              "time_end_ms": 15116,
              "text": "i "
            },
            {
              "time_start_ms": 15116,
              "time_end_ms": 15258,
              "text": "fu "
            },
            {
              "time_start_ms": 15258,
              "time_end_ms": 15417,
              "text": "ga "
            },
            {
              "time_start_ms": 15418,
              "time_end_ms": 15554,
              "text": "de "
            },
            {
              "time_start_ms": 15555,
              "time_end_ms": 15630,
              "text": "fo "
            },
            {
              "time_start_ms": 15706,
              "time_end_ms": 15842,
              "text": "to "
            },
            {
              "time_start_ms": 15843,
              "time_end_ms": 16266,
              "text": "ka "
            }
          ]
        },
        {
          "role": "lyric",
          "language": "jp",
          "text": "だけでバッドライフがデフォとか"
        },
        {
          "role": "translation",
          "language": "zh",
          "text": "就默认会拥有糟糕的人生吗"
        }
      ]
    }
  ]
}

最小示例

{
  "format_version": 2,
  "offset": 0,
  "metadata": {},
  "lyric_lines": [
    {
      "time_start_ms": 1000,
      "time_end_ms": 5500,
      "lines": [
        {
          "role": "lyric",
          "language": "latin",
          "text": "First line"
        }
      ]
    }
  ]
}

版本兼容性

WPLRC 版本 1 是旧版中间格式。它的已知差异是:罗马化 schema 可能以 schemascheme 的形式出现在各个行节点上。

当将版本 1 升级到版本 2,且当前 WPLRC 版本 1 文档包含罗马化行节点时,WpfMusicPlayer 会:

  1. 统计可识别的行级 schema 值。
  2. 如果 jyutping 出现次数多于 romaji,则选择 jyutping;否则,只要存在任何可识别的罗马化 schema,就选择 romaji
  3. 将所选值写入根级 romanization_schema
  4. format_version 设置为 2
  5. 移除所有行级 scheme(以及 schemea,如存在)属性。

否则,它不会生成根级 romanization_schema 属性。

版本 2 生成器不应该输出版本 1 字段。

生成器说明

WpfMusicPlayer 的 LRC 转换器当前会将若干 LRC 模式规范化为 WPLRC:

  • 普通的逐行定时 LRC。
  • 一个 LRC 行中包含多个时间标签。
  • 时间戳顺序错乱的 LRC,按时间进行稳定排序。
  • 交错或同步的翻译行。
  • 受支持模式中的行内翻译。
  • 使用 <mm:ss.xxx>[mm:ss.xxx] 风格控制器标记的扩展 LRC 或词级时间节点。

这些针对源 LRC 的启发式规则属于生成器行为,不是额外的 WPLRC 语法。WPLRC 读取器只需要实现上文描述的 JSON 格式。