【自用中译】开发自定义vlabeler标注器
开发自定义标注器
标注器(labeler) 是一组配置文件与脚本,针对特定标注场景定义应用的多项行为。
本文档将引导你为 vLabeler 开发自定义标注器。
本文涵盖以下主题:
理解 vLabeler 项目
开始前,需要先了解 vLabeler 的项目结构基础知识。
条目(Entry)
条目(entry) 是 vLabeler 内最小数据单元,代表一段带起始时刻和时长的音频数据。
大多数歌声合成软件都使用类似方式表达音频数据。例如在 UTAU 中,条目对应 oto.ini 里的一行;在 NNSVS 中,条目对应 lab 文件内的一行。
一个条目至少包含:起始时间、结束时间、标签名、音频文件引用。除此之外,还可附带其他时间点与附加信息。
在 vLabeler 中,我们把已有标签数据转换成条目,然后编辑条目——这是标注流程的核心。编辑完成后,再把条目转回原始标签格式。
模块(Module)
模块(module) 是 vLabeler 的子项目(UI 中称为 subproject,代码/开发文档中称为 module)。
一个模块包含一组条目。很多声库的条目以层级形式组织,因此我们用模块来表达这种层级关系。
模块需要拥有名称、指向音频文件所在目录的引用、原始标签文件(如 oto.ini / lab)的引用,以此支持批量导入与导出。
项目(Project)
项目(project) 是模块的集合,同时包含元信息,例如项目名、声库根目录等。
以 UTAU 歌手声库举例,声库结构可能如下:
your_singer
├── some wav files
├── oto.ini
├── C4
│ ├── some wav files
│ └── oto.ini
├── F4
│ ├── some wav files
│ └── oto.ini
└── C5
├── some wav files
└── oto.ini
这是典型的多音高 UTAU 声库结构:每个音高独立拥有 oto.ini,根目录下的 oto.ini 用于存放特殊采样。
vLabeler 内置的 UTAU singer labeler 正是为此场景设计。对于上面这个声库,它会生成一个包含4个模块的项目,每个模块对应一份 oto.ini 内的条目。
your_vlabeler_project
├── (Root) module
│ ├── path: ""(和声库根目录一致)
│ ├── entries: 根目录 oto.ini 内的条目
│ └── sample files: 根目录下的wav文件
├── "C4" module
│ ├── path: "C4"(C4文件夹)
│ ├── entries: C4/oto.ini 内的条目
│ └── sample files: C4文件夹下的wav文件
├── "F4" module
......
至此你已经理解基础项目结构。
本例中声库结构和项目结构高度接近,很容易从声库直接创建项目。但很多场景下二者差异很大:例如 NNSVS 的条目全部写在单个 lab 文件中,音频文件放在另一个目录。此时用户通常希望每条音频+对应的lab单独作为一个子项目。
显然这和 UTAU 示例的处理方式完全不同,我们需要自定义项目构建流程来适配不同场景——这正是标注器的核心功能之一。
除此之外,标注器还需要定义:
- 如何把原始标签解析为条目
- 如何把条目写回原始标签
- 条目在UI中的展示形式
- 条目哪些属性可以在界面查看、编辑
- 等等
下面章节介绍标注器结构,以及如何开发自定义标注器。
标注器结构
标注器是一个文件夹,结构如下:
your_labeler
├── labeler.json
├── parser.js
├── writer.js
├── projectConstructor.js
...(其他脚本与资源)
- 文件夹名(如
your_labeler)作为标注器唯一标识 labeler.json:标注器主配置文件*.js:标注器用到的脚本- 其他文件(如词典)可由脚本读取使用
旧式单文件标注器
1.0.0-beta20(标注器序列化版本号2)之前,标注器是后缀 `.labeler.json` 的单个文件,不支持外部资源文件,所有脚本直接内嵌在JSON内。该格式仍可兼容使用,但推荐使用新目录结构。脚本引用
配置中定义了 EmbeddedScripts 类型,用于在 labeler.json 内引用脚本。
当字段类型为 EmbeddedScripts 时,取值支持两种形式:
- 字符串:脚本路径,相对于
labeler.json - 字符串数组:按行分割的JS代码片段
示例:labeler.json 引用同目录下 parser.js
{
// ...,
"parser": {
"scope": "Entry",
"scripts": "parser.js"
},
// ...
}
简短脚本也可以直接内嵌在 labeler.json:
{
// ...,
"parser": {
"scope": "Entry",
"scripts": [
"// JavaScript code line 1",
"// JavaScript code line 2"
]
},
// ...
}
标注器定义
下面详细说明 labeler.json。下表简述根对象各字段含义。
也可以查看带详细注释的Kotlin源码:LabelerConf.kt
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | String | (必填) | 值必须和标注器文件夹名一致 |
| version | Integer | 1 | 标注器版本号 |
| serialVersion | Integer | 0 | 标注器结构序列化版本 |
| singleFile | Boolean | true | 是否为旧式单文件标注器 |
| extension | String | (必填) | 原始标签文件后缀 |
| defaultInputFilePath | String | null | null | 单模块项目默认原始标签文件路径 |
| displayedName | String (Localized) | name |
UI展示名 |
| author | String | (必填) | 作者 |
| String | ”” | 联系邮箱 | |
| description | String (Localized) | ”” | 简短描述 |
| website | String | ”” | 主页或代码仓库地址 |
| categoryTag | String | ”” | 分类标签;不填归入Other |
| displayOrder | Integer | 0 | 在下拉列表中的排序权重 |
| continuous | Boolean | false | 条目是否连续:即本条结束时间=下一条起始时间 |
| allowSameNameEntry | Boolean | false | 单个模块是否允许同名条目 |
| defaultEntryName | String | null | null | 新建条目默认名称;null则使用不带后缀的采样文件名 |
| defaultValues | Float[] | (必填) | 时序参数默认值,格式 [start, *fields, end],单位毫秒 |
| fields | Field[] | (必填) | 除标准start/end外的自定义时序点定义,详见Field |
| extraFields | ExtraField[] | [] | 条目层级、非时序附加字段,详见Extra Field |
| moduleExtraFields | ExtraField[] | [] | 模块层级附加字段,详见Extra Field |
| lockedDrag | LockedDrag | {} | 锁定拖拽行为定义:拖动一个点时同步移动所有相关参数,详见Locked Drag |
| overflowBeforeStart | PointOverflow | “Error” | 当存在早于start的时间点时的处理策略,详见Point Overflow |
| overflowAfterEnd | PointOverflow | “Error” | 当存在晚于end的时间点时的处理策略,详见Point Overflow |
| postEditNextTrigger | PostEditTrigger | {} | 编辑start/end后自动跳转下一条的触发规则,详见Post-edit Actions |
| postEditDoneTrigger | PostEditTrigger | {} | 编辑start/end后标记完成的触发规则,详见Post-edit Actions |
| decimalDigit | Integer | null | 2 | 属性面板与导出时保留小数位数 |
| entrySimilarityWeights | EntrySimilarityWeights | 默认值 | 条目相似度计算权重,用于标签文件重载比对,详见支持标签文件重载 |
| properties | Property[] | [] | 派生属性定义,详见Property |
| parser | Parser | (必填) | 解析器定义,详见Parser |
| writer | Writer | (必填) | 写入器定义,详见Writer |
| parameters | ParameterHolder[] | [] | 用户可配置参数,详见Parameters |
| projectConstructor | ProjectConstructor | null | null | 项目构建器,详见Project Constructor |
| quickProjectBuilders | QuickProjectBuilder[] | [] | 快速项目构建器,详见Quick Project Builder |
| resourceFiles | String[] | [] | 脚本可读取的资源文件列表;内容按顺序以字符串传入脚本 |
下面解释部分关键字段。
命名与版本管理
一份发布版标注器需要唯一 name 和 version。每次修改并对外发布时,版本号必须递增,同时避免和已有标注器重名。
vLabeler 依靠 name+version 自动处理标注器版本匹配:
如果项目使用的标注器版本高于本地已安装版本(或未安装),程序会从项目文件自动安装新版标注器;
如果本地安装版本更高,则优先使用本地版本。
注意:若标注器使用了外部资源文件,程序不会自动更新(资源不会打包进项目文件),需要用户手动升级到同版或更高版本。发布新版标注器时尽量保持向前兼容——vLabeler 不会阻止用户使用旧版标注器打开项目。
serialVersion(序列化版本)
序列化版本用于校验标注器结构和当前应用是否兼容。查阅标注器结构更新记录获取最新serialVersion并填写。
使用本文档开发新版目录式标注器时,务必设置 singleFile: false;该字段用来区分旧式单文件标注器。
extension(后缀名)
extension 是原始标签文件后缀,用于文件选择器过滤、插件兼容性判断。不要加前置点:例如写 lab,而非 .lab。
continuous(连续条目)
布尔标记,控制条目是否连续(本条end=下一条start)。 开启后默认启用多条联动编辑。 该字段会显著改变程序多处行为,请务必配置正确。
Field(时序点字段)
fields 定义除内置 start、end 之外的自定义时序点。
所有时序点(含内置start/end)都是毫秒浮点数,以采样文件起点为基准。
每个字段在编辑器渲染为一条可拖拽控制线。
自定义字段值存储在条目对象 entry 的 points 数组内,顺序必须和fields数组严格对应。
fields 是 Field 对象数组,成员如下:
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | String | (必填) | 字段内部名 |
| label | String (Localized) | (必填) | 编辑器展示文本 |
| color | String | (必填) | 十六进制色号,波形图控制线颜色 |
| height | Float | (必填) | 控制线相对波形高度,取值0~1 |
| dragBase | Boolean | false | 是否作为锁定拖拽基准点,详见Locked Drag |
| constraints | Constraint[] | [] | 字段约束,详见Constraint |
| shortcutIndex | Integer | null | null | 快捷键序号,取值1~8;0预留给start,详见Shortcut |
| replaceStart | Boolean | false | 是否替代原生start字段展示,详见Replace Standard Fields |
| replaceEnd | Boolean | false | 是否替代原生end字段展示,详见Replace Standard Fields |
| triggerPostEditNext | Boolean | false | 修改此字段是否触发「编辑后跳到下一条」,详见Post-edit actions |
| triggerPostEditDone | Boolean | false | 修改此字段是否触发「编辑后标记完成」,详见Post-edit actions |
Constraint(约束)
constraints 定义字段大小约束,数组内每条格式:
{
"min": 1,
"max": 2
}
min:本字段必须大于等于索引为此值的字段(可选)max:本字段必须小于等于索引为此值的字段(可选)
start/end 标准字段不计入该索引体系;所有自定义时序点默认被约束在 start 和 end 之间。
约束只需要单向配置。举例:fields = ["field1", "field2"],若要求 field1 ≤ field2,可以在field1写max:1,或在field2写min:0,二选一即可,无需双向配置。
约束仅限制拖拽操作;用户直接输入数值、插件脚本写入时不会校验。强约束需要在属性setter、writer脚本内手动判断并抛出错误。
Shortcut(快捷键)
vLabeler提供快捷键,一键把选中时序点设为当前播放光标位置:默认 Q W E R ... I O P。
Q 绑定原生start;后续快捷键按shortcutIndex顺序分配给自定义Field;end占用自定义字段之后的下一个快捷键。
建议按时序线上从左到右的顺序分配shortcutIndex。
Replace Standard Fields(替换标准起止点)
vLabeler默认所有自定义时序点必须落在 start ~ end 之间。但部分格式(如UTAU)允许overlap落在start左侧。
内置UTAU标注器的做法:新增left自定义字段,设置 replaceStart: true。
此时编辑器展示的是left而非原生start,overlap不再被限制在left右侧。
用户修改left时,底层原生start会自动更新为所有相关时序点最小值。
注意:该特性仅非连续标注器(continuous=false)可用。 使用替换字段后,parser、属性读写脚本都必须同时维护替换字段与原生start/end的值。
Extra Field(附加字段)
extraFields 用于条目层级、非时序附加字段;moduleExtraFields 用于模块层级附加字段。
和fields不同:附加字段不渲染时序控制线,值只能是字符串或null。
典型用途:存储和时间无关的元信息;一部分仅脚本内部读取,一部分可在弹窗展示/编辑。
- 条目附加字段:值存入entry对象
entry的extras数组,顺序严格匹配extraFields;null值也要保留占位,保证索引对齐。 - 模块附加字段:以字典形式存储,见模块域解析、模块域写入。
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | String | (必填) | 内部名称 |
| displayedName | String (Localized) | 同name | UI展示名 |
| defaultValue | String | null | (必填) | 默认值 |
| isVisible | Boolean | false | 是否在配置界面可见 |
| isEditable | Boolean | false | 是否允许用户编辑 |
| isOptional | Boolean | false | 是否允许为null |
示例:UTAU标注器的rawRight就是典型附加字段。
UTAU的cutoff/right支持两种语义:负数=相对采样起点,非负=相对采样终点。
vLabeler内部统一转为相对采样起点的毫秒值,但导出时需要原始正负信息,因此把原始值存入rawRight附加字段。
Locked Drag(锁定拖拽/固定联动)
锁定拖拽:拖动一个基准点时,所有时序点保持相对间距同步平移(UTAU oto编辑常用)。
UI内称为fixed-drag。
按住Shift拖拽时,锁定逻辑反转:基准点不动,其余点联动平移。
用户首选项可以选择「以start为主基准」或「使用标注器配置」。标注器配置项lockedDrag格式:
{
"useStart": false,
"useDragBase": false
}
useStart:是否把原生start作为基准;若存在replaceStart=true字段,则使用该替代字段而非原生startuseDragBase:是否把标记dragBase=true的字段作为基准
Point Overflow(越界策略)
overflowBeforeStart / overflowAfterEnd 分别控制:出现早于start、晚于end的时间点时如何处理。
可选值:
Error:抛出错误,禁止AdjustBorder:自动拉伸start/end到极值点AdjustPoint:把越界点钳位到start/end
默认值:Error
Post-edit Actions(编辑后自动动作)
vLabeler内置两种编辑后自动动作:
Go to next entry after editing:编辑完成后自动切下一条Mark as done after editing:编辑完成后标记本条完成
这里的「编辑」特指触发字段被修改。
根配置内 postEditNextTrigger / postEditDoneTrigger 控制标准起止点的触发,格式:
{
"useStart": false,
"useEnd": false
}
useStart:修改start触发;存在replaceStart=true时作用于替代字段useEnd:修改end触发;存在replaceEnd=true时作用于替代字段
自定义时序点则直接在Field内使用 triggerPostEditNext / triggerPostEditDone 开关。
支持标签文件重载
用户重载标签文件时,程序比对新旧条目差异。为计算条目相似度,需要在entrySimilarityWeights配置各属性权重。
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | Float | 0.5 | 条目名称权重 |
| sample | Float | 0.3 | 采样文件名权重 |
| start | Float | 0.1 | start权重 |
| end | Float | 0.1 | end权重 |
| points | Float[] | [] | 自定义时序点权重,长度必须和points数组一致 |
| extras | Float[] | [] | 附加字段权重,长度必须和extras数组一致 |
| tag | Float | 0 | tag权重 |
| threshold | Float | 0.75 | 相似度阈值;高于此值判定为同一条目 |
可参考官方内置标注器的权重配置。
Property(派生属性)
fields时序点、extraFields附加字段可以存储原始数据,但UI展示/用户输入经常需要转换后的派生值。
例:UTAU的preutterance是相对left的值(用户习惯看到这个),但vLabeler内部统一存为相对采样起点的绝对毫秒。这时就需要Property做双向转换:展示时转为相对值、用户输入相对值时回写底层时序点。
根配置properties是Property数组,定义条目属性面板展示、脚本可读取的派生属性。
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | String | (必填) | 属性内部名 |
| displayedName | String (Localized) | (必填) | 属性面板展示名 |
| valueGetter | EmbeddedScripts | (必填) | 读取脚本 |
| valueSetter | EmbeddedScripts | null | null | 写入脚本,可空(只读属性) |
| shortcutIndex | Integer | null | null | 「设置属性」快捷键序号0~9;可写属性建议和数组下标保持一致 |
Parser(解析器)
parser定义原始标签→条目的逻辑。
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| scope | “Entry” | “Modules” | (必填) | 解析器域,决定脚本输入输出形式 |
| defaultEncoding | String | “UTF-8” | 读取标签文件默认编码 |
| extractionPattern | String (Regex) | ”” | 正则提取行内变量,仅Entry域生效 |
| variableNames | String[] | [] | 提取变量名列表,仅Entry域生效;变量会传入脚本 |
| scripts | EmbeddedScripts | (必填) | 解析逻辑脚本 |
详见解析原始标签。
Writer(写入器)
writer定义条目→原始标签的逻辑。
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| scope | “Entry” | “Modules” | “Entry” | 写入器域 |
| format | String | null | null | 单行模板字符串,仅Entry域生效 |
| scripts | EmbeddedScripts | null | null | 自定义写入脚本 |
format和scripts二选一;同时存在时优先使用scripts。
使用 format 模板
format是字符串模板,{变量名}作为占位符。
示例:{sample}:{name}={start},{middle},{end} 渲染为 a.wav:a:100,220.5,300
模板可用变量:
sample:采样文件名name:条目名start:条目start数值end:条目end数值- 自定义Field名称:对应时序点数值
- Property名称:派生属性值
- ExtraField名称:附加字段字符串/null
若Field/ExtraField和Property重名,优先取Property。
使用 scripts 脚本
详见写入原始标签。
Parameters(用户参数)
标注器配置本身不适合让用户直接修改JSON,但很多场景需要开放运行时选项。 例:UTAU标注器提供开关,控制是否允许负overlap。这类配置在项目创建弹窗展示,部分支持项目编辑时修改。
parameters数组存放ParameterHolder对象:
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| parameter | Parameter | (必填) | 参数本体定义,见[parameter.md] |
| injector | EmbeddedScripts | null | null | 参数注入脚本,可动态修改labeler配置,详见注入参数值 |
| changeable | Boolean | false | 项目创建后是否允许修改该参数 |
Project Constructor(项目构建器)
对象仅包含scripts字段(EmbeddedScripts),示例:
{
"scripts": "projectConstructor.js"
}
详见构建项目。
Quick Project Builder(快速项目构建器)
数组存放快速构建器定义,用于首页「快速编辑」,直接从单个文件/文件夹一键生成项目。
| 键 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| name | String | (必填) | 构建器内部唯一名称 |
| displayedName | String (Localized) | name | UI展示名 |
| description | String (Localized) | null | null | 提示文字 |
| extension | String | (必填) | 输入文件后缀;空字符串""代表接受文件夹 |
| scripts | EmbeddedScripts | (必填) | 构建逻辑脚本 |
详见启用快速编辑。
标注器内脚本编写
前面介绍了标注器结构,本节讲解脚本写法。 开始前请先阅读vLabeler脚本基础,了解脚本运行环境。 也可以回看脚本引用,确认配置内如何关联js文件。
构建项目
前面讲过项目结构,这里说明如何用脚本从声库生成项目。
最简场景:单模块、所有条目写在根目录单一标签文件,无需自定义构建脚本。
只需在labeler.json设置defaultInputFilePath(相对根目录的标签文件路径),projectConstructor设为null。
{
// ...,
"defaultInputFilePath": "a raw label file",
"projectConstructor": null,
// ...
}
程序自动生成结构:
your_project
└── (Root) module
├── path: ""(和声库根目录一致)
├── entries: 原始标签内所有条目
└── sample files: 根目录wav
如果需要多模块、多文件夹独立标签文件,就需要在projectConstructor内指定脚本。
输入变量
脚本执行前环境预定义变量:
| 名称 | 类型 | 说明 |
|---|---|---|
| root | File | 项目根目录 |
| params | Dictionary | 标注器所有参数,用name作为key读取 |
| resources | String[] | 资源文件文本,顺序和labeler.json内resourceFiles一致 |
| encoding | String | 用户选定的标签文件编码 |
| acceptedSampleExtensions | String[] | vLabeler支持的采样后缀,如["wav", "mp3"] |
| debug | Boolean | 是否调试模式(Gradle run任务) |
输出约定
脚本执行完成后,全局变量modules必须正确赋值,程序据此创建项目。
modules是ModuleDefinition对象数组。
ModuleDefinition字段:
| 名称 | 类型 | 说明 |
|---|---|---|
| name | String | 模块名 |
| sampleDirectoryPath | String | 采样目录绝对路径 |
| sampleFileNames | String[] | 模块使用的采样文件名列表 |
| inputFilePaths | String[] | null | 读取用标签文件绝对路径 |
| labelFilePath | String | null | 导出写入路径;null则每次导出需要用户手动选择 |
示例构建脚本:
let modules = []
for (let folder of root.listChildDirectories()) {
let sampleFiles = folder.listChildFiles().filter(file => acceptedSampleExtensions.includes(file.getExtension()))
if (sampleFiles.length > 0) {
let labelPath = folder.resolve("label.txt").getAbsolutePath()
let def = new ModuleDefinition(
folder.getName(),
folder.getAbsolutePath(),
sampleFiles.map(file => file.getName()),
[labelPath],
labelPath
)
modules.push(def)
}
}
if (modules.length === 0) {
error("No sample files found. Please check the labeler settings to ensure your sample folders are included.")
}
逻辑:遍历根目录所有子文件夹,包含有效采样文件就新建模块;模块采样目录=当前文件夹,采样文件=目录内所有支持格式音频,读取/导出文件固定为目录下label.txt。
最后建议判断modules是否为空,抛出友好错误提示。
启用快速编辑
快速编辑功能:首页直接选择文件/文件夹,一键生成项目。
需要在quickProjectBuilders定义构建器。
注意:快速编辑依赖自动导出,因此要求标注器提供projectConstructor或defaultInputFilePath。
QuickProjectBuilder内scripts实现从输入生成项目。
输入变量
input:File对象,选中的文件或文件夹savedParams:标注器持久化参数,原始值;调试前建议打印确认类型
输出约定
projectFile:File,生成的.lbp项目文件sampleDirectory:File,采样根目录cacheDirectory:File,缓存目录;不设置则使用默认缓存encoding:标签文件编码,默认UTF-8params:项目使用的标注器参数;不设置则沿用savedParams
属性读取器(Property Getter)
Property的valueGetter脚本用于获取派生属性值。
输入变量
entry:当前条目对象entry
输出约定
全局变量value 赋值为计算得到的数值。
注意:
let value = .../const value = ...不会生效!
示例:计算时长属性
value = entry.end - entry.start
错误处理
读取器不预期抛出异常;出错静默返回0并打印日志。
属性写入器(Property Setter)
Property的valueSetter脚本,用户修改派生属性后回写底层条目。
输入变量
entry:当前条目对象value:用户输入的新数值
输出约定
直接修改entry对象字段,无需额外返回变量。
示例:通过时长修改end
entry.end = entry.start + value
错误处理
写入器可使用全局error()API抛出校验错误。
解析原始标签
项目创建流程简要回顾:
- 执行项目构建器生成ModuleDefinition列表
- 逐个模块生成条目
- Entry域+模板插件:插件执行生成条目;插件自带input finder则按其规则找文件,否则使用module定义内inputFiles;不存在的文件传入
null - Entry域、无模板插件:仅使用第一个inputFile;文件存在则执行Entry解析器;文件不存在则按labeler.json的defaultValues为每个采样生成默认条目
- Modules域解析器:按除name以外的字段分组ModuleDefinition,每组执行一次Modules域解析器(因此项目构建器要保证同组仅name不同)
- Entry域+模板插件:插件执行生成条目;插件自带input finder则按其规则找文件,否则使用module定义内inputFiles;不存在的文件传入
- 使用条目列表创建模块
- 组装项目
小结:Entry域解析器按模块执行一次;Modules域解析器按模块组执行一次。
公共输入(Entry / Modules 域共用)
| 名称 | 类型 | 说明 | |——|——|——| | inputFileNames | String[] | 输入标签文件名;Entry域仅1个元素 | | sampleFileNames | String[] | 当前模块所有采样文件名 | | params | Dictionary | 标注器参数 | | resources | String[] | 资源文本数组 | | encoding | String | 文件编码 | | debug | Boolean | 调试标记 |
Entry域解析
Entry域配合extractionPattern正则、variableNames提取每行变量:程序逐行读入,正则捕获变量后再执行脚本。
除公共变量外,额外传入:
input:当前行原始文本- variableNames内定义的各个变量:正则捕获值
全局变量entry 需要赋值为新Entry对象。
注意:
let entry = .../const entry = ...无效!
示例(两种写法二选一)
// 假设正则已经提取 name, sample, start, end
entry = new Entry(sample, name, parseFloat(start), parseFloat(end), [], [])
// 直接手动分割解析
parts = input.split(",")
entry = new Entry(parts[0], parts[1], parseFloat(parts[2]), parseFloat(parts[3]), [], [])
Modules域解析
Modules域按模块组执行。先判断组内inputFiles是否存在;无文件则 fallback:按defaultValues为每个采样生成默认条目。
存在有效输入文件时,脚本环境额外传入:
moduleDefinitions:本组ModuleDefinition数组inputs:输入文件内容数组;不存在的文件对应null
输出约定:
全局变量modules = Entry[][],外层数组顺序和moduleDefinitions一一对应,内层是每个模块的条目列表。
可选:全局变量moduleExtras = Dictionary[],顺序和modules对齐;字典key对应moduleExtraFields名称;值为字符串,null值不要写入key。
写入原始标签
Writer支持Entry域、Modules域;简单场景用format模板,复杂逻辑用scripts。
公共输入(Entry / Modules 域共用)
| 名称 | 类型 | 说明 | |——|——|——| | params | Dictionary | 标注器参数 | | resources | String[] | 资源文本数组 | | debug | Boolean | 调试标记 |
Entry域写入
Entry域每条目执行一次脚本。可用变量同format模板。
全局变量output 赋值为单行输出文本。
注意:
let output = .../const output = ...无效!
Modules域写入
整组模块一次性执行写入脚本。额外传入变量:
moduleNames:本组模块名数组modules:Entry[][],条目二维数组,顺序和moduleNames对齐moduleExtras:Dictionary[],模块附加字段字典数组
全局变量output 赋值为完整标签文件文本,程序写入该模块组对应的labelFilePath。
注入参数值(Injector)
标注器json本身一般不允许用户直接修改,但参数注入器可以根据用户参数动态修改labeler配置对象。
典型场景:UTAU的useNegativeOvl开关,动态修改ovl字段约束。
参数的injector脚本在项目创建时执行;若changeable=true,参数修改后也会重新执行。
脚本预定义变量:
labeler:从labeler.json加载的配置对象(可直接修改)value:当前参数值,原生类型
示例(UTAU负重叠开关)
labeler.fields[2].constraints[0].min = value ? null : 3
含义:开启负overlap时,该约束min设为null(允许越过left左侧);关闭则min=3(索引3的字段,禁止左移越过left)。
注入器禁止修改以下字段:
nameversionextensiondisplayedNamedescriptionauthorwebsitecontinuousparametersfields数组长度defaultValues数组长度extraFields数组长度fields内每个元素的nameextraFields内每个元素的nameproperties内每个元素的name
其他说明
示例项目
所有官方标注器开源,可直接参考:
- UTAU singer labeler:多音高UTAU声库,覆盖绝大多数脚本能力
- NNSVS singer labeler:NNSVS声库,演示声库结构和vLabeler项目结构不同时的适配方案
- Textgrid labeler:Praat TextGrid标注器,使用模块组+Modules域解析/写入
本地化
查看本地化字符串规范,了解文档中 String (Localized) 类型。
错误处理
脚本异常策略详见vLabeler脚本基础。
调试
使用console.log()打印日志:标准输出写入.logs/info.log,错误输出写入.logs/error.log。
如果标注器不在下拉列表,大概率加载阶段JSON解析/脚本报错,优先查看错误日志。