@kongyo2/kongyoroid 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/dist/cache.d.ts +36 -0
  4. package/dist/cache.d.ts.map +1 -0
  5. package/dist/cache.js +109 -0
  6. package/dist/cache.js.map +1 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +502 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/concurrency.d.ts +14 -0
  12. package/dist/concurrency.d.ts.map +1 -0
  13. package/dist/concurrency.js +107 -0
  14. package/dist/concurrency.js.map +1 -0
  15. package/dist/errors.d.ts +35 -0
  16. package/dist/errors.d.ts.map +1 -0
  17. package/dist/errors.js +81 -0
  18. package/dist/errors.js.map +1 -0
  19. package/dist/formant.d.ts +34 -0
  20. package/dist/formant.d.ts.map +1 -0
  21. package/dist/formant.js +622 -0
  22. package/dist/formant.js.map +1 -0
  23. package/dist/index.d.ts +40 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +23 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/io.d.ts +6 -0
  28. package/dist/io.d.ts.map +1 -0
  29. package/dist/io.js +144 -0
  30. package/dist/io.js.map +1 -0
  31. package/dist/kongyoroid.d.ts +82 -0
  32. package/dist/kongyoroid.d.ts.map +1 -0
  33. package/dist/kongyoroid.js +275 -0
  34. package/dist/kongyoroid.js.map +1 -0
  35. package/dist/limits.d.ts +18 -0
  36. package/dist/limits.d.ts.map +1 -0
  37. package/dist/limits.js +15 -0
  38. package/dist/limits.js.map +1 -0
  39. package/dist/mora.d.ts +28 -0
  40. package/dist/mora.d.ts.map +1 -0
  41. package/dist/mora.js +295 -0
  42. package/dist/mora.js.map +1 -0
  43. package/dist/notation.d.ts +15 -0
  44. package/dist/notation.d.ts.map +1 -0
  45. package/dist/notation.js +114 -0
  46. package/dist/notation.js.map +1 -0
  47. package/dist/pitch.d.ts +5 -0
  48. package/dist/pitch.d.ts.map +1 -0
  49. package/dist/pitch.js +36 -0
  50. package/dist/pitch.js.map +1 -0
  51. package/dist/player.d.ts +9 -0
  52. package/dist/player.d.ts.map +1 -0
  53. package/dist/player.js +81 -0
  54. package/dist/player.js.map +1 -0
  55. package/dist/request.d.ts +12 -0
  56. package/dist/request.d.ts.map +1 -0
  57. package/dist/request.js +115 -0
  58. package/dist/request.js.map +1 -0
  59. package/dist/schema.d.ts +9 -0
  60. package/dist/schema.d.ts.map +1 -0
  61. package/dist/schema.js +267 -0
  62. package/dist/schema.js.map +1 -0
  63. package/dist/score.d.ts +37 -0
  64. package/dist/score.d.ts.map +1 -0
  65. package/dist/score.js +166 -0
  66. package/dist/score.js.map +1 -0
  67. package/dist/text.d.ts +3 -0
  68. package/dist/text.d.ts.map +1 -0
  69. package/dist/text.js +54 -0
  70. package/dist/text.js.map +1 -0
  71. package/dist/types.d.ts +121 -0
  72. package/dist/types.d.ts.map +1 -0
  73. package/dist/types.js +2 -0
  74. package/dist/types.js.map +1 -0
  75. package/dist/validate.d.ts +13 -0
  76. package/dist/validate.d.ts.map +1 -0
  77. package/dist/validate.js +59 -0
  78. package/dist/validate.js.map +1 -0
  79. package/dist/version.d.ts +3 -0
  80. package/dist/version.d.ts.map +1 -0
  81. package/dist/version.js +3 -0
  82. package/dist/version.js.map +1 -0
  83. package/dist/voicevox/api.d.ts +79 -0
  84. package/dist/voicevox/api.d.ts.map +1 -0
  85. package/dist/voicevox/api.js +176 -0
  86. package/dist/voicevox/api.js.map +1 -0
  87. package/dist/voicevox/client.d.ts +64 -0
  88. package/dist/voicevox/client.d.ts.map +1 -0
  89. package/dist/voicevox/client.js +321 -0
  90. package/dist/voicevox/client.js.map +1 -0
  91. package/dist/voicevox/dictionary.d.ts +21 -0
  92. package/dist/voicevox/dictionary.d.ts.map +1 -0
  93. package/dist/voicevox/dictionary.js +41 -0
  94. package/dist/voicevox/dictionary.js.map +1 -0
  95. package/dist/voicevox/song.d.ts +12 -0
  96. package/dist/voicevox/song.d.ts.map +1 -0
  97. package/dist/voicevox/song.js +45 -0
  98. package/dist/voicevox/song.js.map +1 -0
  99. package/dist/voicevox/speech.d.ts +17 -0
  100. package/dist/voicevox/speech.d.ts.map +1 -0
  101. package/dist/voicevox/speech.js +100 -0
  102. package/dist/voicevox/speech.js.map +1 -0
  103. package/dist/voicevox/styles.d.ts +15 -0
  104. package/dist/voicevox/styles.d.ts.map +1 -0
  105. package/dist/voicevox/styles.js +103 -0
  106. package/dist/voicevox/styles.js.map +1 -0
  107. package/dist/wav.d.ts +18 -0
  108. package/dist/wav.d.ts.map +1 -0
  109. package/dist/wav.js +198 -0
  110. package/dist/wav.js.map +1 -0
  111. package/examples/batch.jsonl +4 -0
  112. package/examples/song-notes.json +24 -0
  113. package/examples/song.json +12 -0
  114. package/examples/speech-kana.json +6 -0
  115. package/examples/speech.json +8 -0
  116. package/package.json +86 -0
@@ -0,0 +1,115 @@
1
+ import { invalid } from "./errors.js";
2
+ import { DEFAULT_SAMPLE_RATE, LIMITS, SAMPLE_RATE_RANGE } from "./limits.js";
3
+ import { formatKanaNotation, parseKanaNotation } from "./notation.js";
4
+ import { SONG_PROPERTIES, SPEECH_PROPERTIES } from "./schema.js";
5
+ import { MAX_NOTES, parseNotes, resolveNotes, scoreSeconds, transposeInput } from "./score.js";
6
+ import { boolean, has, integer, keys, literal, number, object, optional, string } from "./validate.js";
7
+ const ENGINES = ["voicevox", "formant", "auto"];
8
+ export function parseEngine(value, path = "$.engine") {
9
+ return literal(value, path, ENGINES);
10
+ }
11
+ export function parseStyleRef(value, path) {
12
+ if (typeof value === "number")
13
+ return integer(value, path, 0, 4_294_967_295);
14
+ if (typeof value === "string") {
15
+ const name = value.trim();
16
+ if (name.length === 0 || name.length > 200)
17
+ invalid(path, "Expected a style id or a non-empty style name.");
18
+ return /^\d+$/u.test(name) ? Number(name) : name;
19
+ }
20
+ return invalid(path, "Expected a style id (integer) or a style name (string).");
21
+ }
22
+ function parseSampleRate(value, path) {
23
+ return integer(value, path, SAMPLE_RATE_RANGE.min, SAMPLE_RATE_RANGE.max);
24
+ }
25
+ function parseSeed(value, path) {
26
+ return integer(value, path, 1, 4_294_967_295);
27
+ }
28
+ function parseCommon(o) {
29
+ const implied = has(o, "speaker") || has(o, "singer") || has(o, "teacher") ? "voicevox" : "formant";
30
+ return {
31
+ engine: optional(o, "engine", "$", implied, parseEngine),
32
+ sampleRate: has(o, "sampleRate") ? parseSampleRate(o["sampleRate"], "$.sampleRate") : undefined,
33
+ volume: optional(o, "volume", "$", 1, (v, p) => number(v, p, 0, 3)),
34
+ seed: optional(o, "seed", "$", 1, parseSeed),
35
+ };
36
+ }
37
+ export function parseSpeechRequest(o) {
38
+ keys(o, Object.keys(SPEECH_PROPERTIES), "$");
39
+ const text = string(o["text"], "$.text", 1, LIMITS.textChars);
40
+ if (text.trim().length === 0)
41
+ invalid("$.text", "Text must not be blank.");
42
+ const kana = has(o, "kana")
43
+ ? formatKanaNotation(parseKanaNotation(string(o["kana"], "$.kana", 1, LIMITS.kanaChars), "$.kana"))
44
+ : undefined;
45
+ return {
46
+ kind: "speech",
47
+ ...parseCommon(o),
48
+ text,
49
+ kana,
50
+ speaker: has(o, "speaker") ? parseStyleRef(o["speaker"], "$.speaker") : undefined,
51
+ speed: optional(o, "speed", "$", 1, (v, p) => number(v, p, 0.25, 4)),
52
+ pitch: optional(o, "pitch", "$", 0, (v, p) => number(v, p, -1, 1)),
53
+ intonation: optional(o, "intonation", "$", 1, (v, p) => number(v, p, 0, 3)),
54
+ prePause: optional(o, "prePause", "$", 0.1, (v, p) => number(v, p, 0, 10)),
55
+ postPause: optional(o, "postPause", "$", 0.1, (v, p) => number(v, p, 0, 10)),
56
+ pauseLength: has(o, "pauseLength") ? number(o["pauseLength"], "$.pauseLength", 0, 10) : undefined,
57
+ pauseScale: optional(o, "pauseScale", "$", 1, (v, p) => number(v, p, 0, 10)),
58
+ upspeak: optional(o, "upspeak", "$", true, boolean),
59
+ split: optional(o, "split", "$", "sentence", (v, p) => literal(v, p, ["sentence", "paragraph", "none"])),
60
+ };
61
+ }
62
+ export function parseSongRequest(o) {
63
+ keys(o, Object.keys(SONG_PROPERTIES), "$");
64
+ const tempo = optional(o, "tempo", "$", 120, (v, p) => number(v, p, 20, 400));
65
+ const transpose = optional(o, "transpose", "$", 0, transposeInput);
66
+ const inputNotes = parseNotes(o["notes"], "$.notes");
67
+ if (inputNotes.length > MAX_NOTES)
68
+ invalid("$.notes", `A score is limited to ${MAX_NOTES} notes.`);
69
+ const notes = resolveNotes(inputNotes, "$.notes", transpose);
70
+ const leadIn = optional(o, "leadIn", "$", 0.16, (v, p) => number(v, p, 0, 5));
71
+ const leadOut = optional(o, "leadOut", "$", 0.16, (v, p) => number(v, p, 0, 5));
72
+ const seconds = scoreSeconds(notes, tempo) + leadIn + leadOut;
73
+ if (seconds > LIMITS.audioSeconds) {
74
+ invalid("$.notes", `The score lasts ${seconds.toFixed(1)} s; the limit is ${LIMITS.audioSeconds} s.`);
75
+ }
76
+ return {
77
+ kind: "song",
78
+ ...parseCommon(o),
79
+ notes,
80
+ tempo,
81
+ singer: has(o, "singer") ? parseStyleRef(o["singer"], "$.singer") : undefined,
82
+ teacher: has(o, "teacher") ? parseStyleRef(o["teacher"], "$.teacher") : undefined,
83
+ transpose,
84
+ vibratoDepth: has(o, "vibratoDepth") ? number(o["vibratoDepth"], "$.vibratoDepth", 0, 200) : undefined,
85
+ vibratoRate: optional(o, "vibratoRate", "$", 5.5, (v, p) => number(v, p, 0, 12)),
86
+ leadIn,
87
+ leadOut,
88
+ };
89
+ }
90
+ export function parseRequest(value) {
91
+ const o = object(value, "$");
92
+ const kind = literal(o["kind"], "$.kind", ["speech", "song"]);
93
+ return kind === "speech" ? parseSpeechRequest(o) : parseSongRequest(o);
94
+ }
95
+ export function speechRequest(input) {
96
+ return { kind: "speech", ...input };
97
+ }
98
+ export function songRequest(input) {
99
+ return { kind: "song", ...input };
100
+ }
101
+ export function parseJson(text, path = "$") {
102
+ if (Buffer.byteLength(text, "utf8") > LIMITS.inputBytes) {
103
+ invalid(path, `Input exceeds ${LIMITS.inputBytes} bytes.`);
104
+ }
105
+ try {
106
+ return JSON.parse(text);
107
+ }
108
+ catch (error) {
109
+ return invalid(path, `Invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
110
+ }
111
+ }
112
+ export function effectiveSampleRate(request, engineDefault = DEFAULT_SAMPLE_RATE) {
113
+ return request.sampleRate ?? engineDefault;
114
+ }
115
+ //# sourceMappingURL=request.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"request.js","sourceRoot":"","sources":["../src/request.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ export type JsonSchema = Readonly<Record<string, unknown>>;
2
+ export declare const SPEECH_PROPERTIES: JsonSchema;
3
+ export declare const NOTE_SCHEMA: JsonSchema;
4
+ export declare const SCORE_TEXT_SCHEMA: JsonSchema;
5
+ export declare const SONG_PROPERTIES: JsonSchema;
6
+ export declare const REQUEST_SCHEMA: JsonSchema;
7
+ export declare const BATCH_JOB_SCHEMA: JsonSchema;
8
+ export declare const CAPABILITIES: JsonSchema;
9
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAKA,MAAM,MAAM,UAAU,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;AAkC3D,eAAO,MAAM,iBAAiB,EAAE,UA8C/B,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,UA8BzB,CAAC;AAEF,eAAO,MAAM,iBAAiB,EAAE,UA0B/B,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,UAgC7B,CAAC;AAmBF,eAAO,MAAM,cAAc,EAAE,UAK5B,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,UAa9B,CAAC;AAEF,eAAO,MAAM,YAAY,EAAE,UAsE1B,CAAC"}
package/dist/schema.js ADDED
@@ -0,0 +1,267 @@
1
+ import { EXIT_CODES } from "./errors.js";
2
+ import { LIMITS, SAMPLE_RATE_RANGE } from "./limits.js";
3
+ import { MAX_BEATS, MAX_NOTES, MIN_BEATS } from "./score.js";
4
+ import { PACKAGE_NAME, VERSION } from "./version.js";
5
+ const styleRef = {
6
+ anyOf: [
7
+ { type: "integer", minimum: 0, maximum: 4_294_967_295 },
8
+ { type: "string", minLength: 1, maxLength: 200 },
9
+ ],
10
+ description: 'A VOICEVOX style id from the voices command, or a name: "ずんだもん" (first matching style) or "ずんだもん/ノーマル" (exact style).',
11
+ };
12
+ const common = {
13
+ engine: {
14
+ enum: ["formant", "voicevox", "auto"],
15
+ default: "formant",
16
+ description: "formant is the built-in engine: kana text or kana notation, deterministic, no external service. voicevox drives a running VOICEVOX ENGINE (character voices, kanji text, user dictionary) and is selected automatically when speaker, singer, or teacher is given. auto uses voicevox when the endpoint answers, otherwise formant.",
17
+ },
18
+ sampleRate: {
19
+ type: "integer",
20
+ minimum: SAMPLE_RATE_RANGE.min,
21
+ maximum: SAMPLE_RATE_RANGE.max,
22
+ description: "Output sample rate in Hz. Defaults to the engine default (24000).",
23
+ },
24
+ volume: { type: "number", minimum: 0, maximum: 3, default: 1, description: "Linear output gain." },
25
+ seed: {
26
+ type: "integer",
27
+ minimum: 1,
28
+ maximum: 4_294_967_295,
29
+ default: 1,
30
+ description: "Noise seed for the formant engine; ignored by voicevox.",
31
+ },
32
+ };
33
+ export const SPEECH_PROPERTIES = {
34
+ kind: { const: "speech" },
35
+ ...common,
36
+ text: {
37
+ type: "string",
38
+ minLength: 1,
39
+ maxLength: LIMITS.textChars,
40
+ description: "Text to read. voicevox accepts ordinary Japanese including kanji; formant accepts kana only.",
41
+ },
42
+ kana: {
43
+ type: "string",
44
+ minLength: 1,
45
+ maxLength: LIMITS.kanaChars,
46
+ description: "Optional reading in AquesTalk-style notation, e.g. コンニチワ'/セカ'イ? (katakana, / between accent phrases, 、 for a pause, ' after the accent nucleus, _ before a devoiced vowel, ? for a question). When given, it replaces the engine's own reading of text and disables splitting.",
47
+ },
48
+ speaker: styleRef,
49
+ speed: { type: "number", minimum: 0.25, maximum: 4, default: 1, description: "Speaking rate multiplier." },
50
+ pitch: {
51
+ type: "number",
52
+ minimum: -1,
53
+ maximum: 1,
54
+ default: 0,
55
+ description: "VOICEVOX pitchScale semantics; the practical range is about -0.15 to 0.15.",
56
+ },
57
+ intonation: { type: "number", minimum: 0, maximum: 3, default: 1, description: "Pitch-range multiplier." },
58
+ prePause: { type: "number", minimum: 0, maximum: 10, default: 0.1, description: "Leading silence in seconds." },
59
+ postPause: { type: "number", minimum: 0, maximum: 10, default: 0.1, description: "Trailing silence in seconds." },
60
+ pauseLength: {
61
+ type: "number",
62
+ minimum: 0,
63
+ maximum: 10,
64
+ description: "Absolute punctuation pause in seconds. Omit to keep the engine's own pause lengths.",
65
+ },
66
+ pauseScale: { type: "number", minimum: 0, maximum: 10, default: 1, description: "Punctuation pause multiplier." },
67
+ upspeak: {
68
+ type: "boolean",
69
+ default: true,
70
+ description: "Raise the final mora of interrogative phrases (VOICEVOX enable_interrogative_upspeak).",
71
+ },
72
+ split: {
73
+ enum: ["sentence", "paragraph", "none"],
74
+ default: "sentence",
75
+ description: "How long text is chunked before synthesis. Chunks are synthesized concurrently and concatenated in order.",
76
+ },
77
+ };
78
+ export const NOTE_SCHEMA = {
79
+ type: "object",
80
+ additionalProperties: false,
81
+ required: ["key", "beats"],
82
+ properties: {
83
+ key: {
84
+ anyOf: [
85
+ { type: "integer", minimum: 0, maximum: 127 },
86
+ { type: "string", pattern: "^[A-Ga-g](#|♯|b|♭|x)?-?[0-9]$" },
87
+ { type: "null" },
88
+ ],
89
+ description: "MIDI key (60 = C4), a note name such as C4 or F#3, or null for a rest.",
90
+ },
91
+ beats: {
92
+ type: "number",
93
+ minimum: MIN_BEATS,
94
+ maximum: MAX_BEATS,
95
+ description: "Duration in quarter-note beats at the score tempo.",
96
+ },
97
+ lyric: {
98
+ type: "string",
99
+ maxLength: 8,
100
+ default: "",
101
+ description: 'Exactly one kana mora for a pitched note (ア, キャ, ン, ...), "ー" to hold the previous vowel, empty for a rest.',
102
+ },
103
+ },
104
+ if: { properties: { key: { type: "null" } } },
105
+ then: { properties: { lyric: { const: "" } } },
106
+ else: { properties: { lyric: { type: "string", minLength: 1 } } },
107
+ };
108
+ export const SCORE_TEXT_SCHEMA = {
109
+ type: "object",
110
+ additionalProperties: false,
111
+ required: ["lyrics", "melody"],
112
+ properties: {
113
+ lyrics: {
114
+ type: "string",
115
+ maxLength: LIMITS.textChars,
116
+ description: "Kana lyrics; one mora is consumed per pitched melody note. ー holds the previous vowel.",
117
+ },
118
+ melody: {
119
+ type: "string",
120
+ minLength: 1,
121
+ maxLength: LIMITS.textChars,
122
+ description: 'Whitespace-separated notes: note names (C4, F#4, Bb3), MIDI integers, R for a rest, ~ for a tie. Example: "C4 D4 E4 R ~".',
123
+ },
124
+ beats: {
125
+ anyOf: [
126
+ { type: "string" },
127
+ { type: "array", items: { type: "number", minimum: MIN_BEATS, maximum: MAX_BEATS }, minItems: 1 },
128
+ ],
129
+ description: 'Beat lengths per melody token ("1 1 2 0.5 1/2"). One value applies to every note; omitted means 1 beat each.',
130
+ },
131
+ },
132
+ };
133
+ export const SONG_PROPERTIES = {
134
+ kind: { const: "song" },
135
+ ...common,
136
+ notes: {
137
+ anyOf: [
138
+ { type: "array", minItems: 1, maxItems: MAX_NOTES, items: { $ref: "#/$defs/note" } },
139
+ { $ref: "#/$defs/scoreText" },
140
+ ],
141
+ description: "Either an explicit note list or a compact lyrics/melody/beats score.",
142
+ },
143
+ tempo: { type: "number", minimum: 20, maximum: 400, default: 120, description: "Beats per minute." },
144
+ singer: {
145
+ ...styleRef,
146
+ description: "VOICEVOX style that renders the voice (type frame_decode or sing). Defaults to the first singing style the engine offers.",
147
+ },
148
+ teacher: {
149
+ ...styleRef,
150
+ description: "VOICEVOX style that predicts timing and pitch (type singing_teacher or sing). Defaults to the singer when it can teach, otherwise to the first teacher.",
151
+ },
152
+ transpose: { type: "integer", minimum: -48, maximum: 48, default: 0, description: "Semitones added to every key." },
153
+ vibratoDepth: {
154
+ type: "number",
155
+ minimum: 0,
156
+ maximum: 200,
157
+ description: "Vibrato depth in cents. voicevox defaults to 0 (the engine's own expression); formant defaults to 25.",
158
+ },
159
+ vibratoRate: { type: "number", minimum: 0, maximum: 12, default: 5.5, description: "Vibrato rate in Hz." },
160
+ leadIn: { type: "number", minimum: 0, maximum: 5, default: 0.16, description: "Rest added before the first note." },
161
+ leadOut: { type: "number", minimum: 0, maximum: 5, default: 0.16, description: "Rest added after the last note." },
162
+ };
163
+ function variant(kind, properties, required) {
164
+ return {
165
+ type: "object",
166
+ title: kind === "speech" ? "SpeechRequest" : "SongRequest",
167
+ additionalProperties: false,
168
+ required: ["kind", ...required],
169
+ properties,
170
+ };
171
+ }
172
+ const REQUEST_BODY = {
173
+ title: "kongyoroid RenderRequest",
174
+ description: "One speech (読み上げ) or song (歌唱) render. Unknown properties are rejected. Output is a mono PCM16 WAV.",
175
+ oneOf: [variant("speech", SPEECH_PROPERTIES, ["text"]), variant("song", SONG_PROPERTIES, ["notes"])],
176
+ };
177
+ export const REQUEST_SCHEMA = {
178
+ $schema: "http://json-schema.org/draft-07/schema#",
179
+ $id: "https://github.com/kongyo2/kongyoroid/schema/render-request.json",
180
+ ...REQUEST_BODY,
181
+ $defs: { note: NOTE_SCHEMA, scoreText: SCORE_TEXT_SCHEMA },
182
+ };
183
+ export const BATCH_JOB_SCHEMA = {
184
+ $schema: "http://json-schema.org/draft-07/schema#",
185
+ $id: "https://github.com/kongyo2/kongyoroid/schema/batch-job.json",
186
+ title: "kongyoroid BatchJob",
187
+ description: "One JSONL line for the batch command. The request schema is embedded under $defs.request.",
188
+ type: "object",
189
+ additionalProperties: false,
190
+ required: ["id", "request"],
191
+ properties: {
192
+ id: { type: "string", pattern: "^[A-Za-z0-9_-]{1,64}$", description: "Output file name stem, unique per batch." },
193
+ request: { $ref: "#/$defs/request" },
194
+ },
195
+ $defs: { request: REQUEST_BODY, note: NOTE_SCHEMA, scoreText: SCORE_TEXT_SCHEMA },
196
+ };
197
+ export const CAPABILITIES = {
198
+ name: PACKAGE_NAME,
199
+ version: VERSION,
200
+ description: "Japanese speech and singing synthesis for LLM agents.",
201
+ output: { container: "wav", encoding: "pcm16", channels: 1, sampleRate: { default: 24000, ...SAMPLE_RATE_RANGE } },
202
+ defaultEngine: "formant",
203
+ engines: {
204
+ formant: {
205
+ external: false,
206
+ speech: "Kana text or kana notation (accent phrases, pauses, devoicing, questions); deterministic for a request and seed.",
207
+ song: "Score of one-mora notes with ties and rests; deterministic.",
208
+ maxSeconds: LIMITS.formantSeconds,
209
+ },
210
+ voicevox: {
211
+ external: true,
212
+ endpoint: { default: "http://127.0.0.1:50021", env: ["KONGYOROID_ENDPOINT", "VOICEVOX_URL"] },
213
+ speech: "Japanese text with kanji; readings adjustable via kana notation and the user dictionary.",
214
+ song: "Score of one-mora notes rendered by a singing style; timing and pitch predicted by a teacher style.",
215
+ styles: "Discover with the voices command; select by id or by name. Giving a style selects this engine.",
216
+ },
217
+ },
218
+ commands: [
219
+ "speak",
220
+ "sing",
221
+ "render",
222
+ "batch",
223
+ "reading",
224
+ "voices",
225
+ "doctor",
226
+ "dict",
227
+ "play",
228
+ "schema",
229
+ "capabilities",
230
+ ],
231
+ env: {
232
+ KONGYOROID_ENDPOINT: "VOICEVOX ENGINE base URL.",
233
+ VOICEVOX_URL: "Alias of KONGYOROID_ENDPOINT.",
234
+ KONGYOROID_ENGINE: "Default engine: voicevox, formant, or auto.",
235
+ KONGYOROID_SPEAKER: "Default speech style (id or name).",
236
+ KONGYOROID_SINGER: "Default singing style (id or name).",
237
+ KONGYOROID_TEACHER: "Default singing teacher style (id or name).",
238
+ KONGYOROID_CACHE_DIR: "Directory for the on-disk render cache.",
239
+ },
240
+ limits: {
241
+ textChars: LIMITS.textChars,
242
+ notes: LIMITS.notes,
243
+ audioSeconds: LIMITS.audioSeconds,
244
+ formantSeconds: LIMITS.formantSeconds,
245
+ inputBytes: LIMITS.inputBytes,
246
+ batchLines: LIMITS.batchLines,
247
+ concurrency: LIMITS.concurrency,
248
+ },
249
+ exitCodes: EXIT_CODES,
250
+ errorCodes: [
251
+ "INVALID_INPUT",
252
+ "UNSUPPORTED_TEXT",
253
+ "ENGINE_UNAVAILABLE",
254
+ "ENGINE_HTTP",
255
+ "ENGINE_REJECTED",
256
+ "ENGINE_PROTOCOL",
257
+ "TIMEOUT",
258
+ "ABORTED",
259
+ "QUEUE_FULL",
260
+ "IO_ERROR",
261
+ "PLAYER_UNAVAILABLE",
262
+ "INTERNAL",
263
+ ],
264
+ stdout: "One JSON object per line. With --output - the WAV bytes go to stdout and the JSON goes to stderr.",
265
+ stderr: "Errors as one JSON object: { ok: false, error: { code, message, path?, hint?, retryable } }.",
266
+ };
267
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ export declare const DEFAULT_FRAME_RATE: number;
2
+ export declare const MAX_NOTES: number;
3
+ export declare const MIN_BEATS: number;
4
+ export declare const MAX_BEATS: number;
5
+ export interface NoteInput {
6
+ readonly key: number | string | null;
7
+ readonly beats: number;
8
+ readonly lyric: string;
9
+ }
10
+ export interface Note {
11
+ readonly key: number | null;
12
+ readonly beats: number;
13
+ readonly lyric: string;
14
+ }
15
+ export interface EngineNote {
16
+ readonly id: string;
17
+ readonly key: number | null;
18
+ readonly frame_length: number;
19
+ readonly lyric: string;
20
+ }
21
+ export interface EngineScore {
22
+ readonly notes: readonly EngineNote[];
23
+ }
24
+ export interface ScoreText {
25
+ readonly lyrics: string;
26
+ readonly melody: string;
27
+ readonly beats?: string | readonly number[];
28
+ }
29
+ export declare function parseScoreText(input: ScoreText, path?: string): readonly NoteInput[];
30
+ export declare function parseNoteInput(value: unknown, path: string): NoteInput;
31
+ export declare function resolveNotes(input: readonly NoteInput[], path?: string, transpose?: number): readonly Note[];
32
+ export declare function parseNotes(value: unknown, path?: string): readonly NoteInput[];
33
+ export declare function scoreSeconds(notes: readonly Note[], tempo: number): number;
34
+ export declare function notesToEngineScore(notes: readonly Note[], tempo: number, frameRate?: number, leadIn?: number, leadOut?: number): EngineScore;
35
+ export declare function engineScoreFrames(score: EngineScore): number;
36
+ export declare function transposeInput(value: unknown, path: string): number;
37
+ //# sourceMappingURL=score.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"score.d.ts","sourceRoot":"","sources":["../src/score.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,kBAAkB,EAAE,MAAc,CAAC;AAChD,eAAO,MAAM,SAAS,EAAE,MAAa,CAAC;AACtC,eAAO,MAAM,SAAS,EAAE,MAAe,CAAC;AACxC,eAAO,MAAM,SAAS,EAAE,MAAW,CAAC;AAEpC,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,IAAI;IACnB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;CAC7C;AAkBD,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,GAAE,MAAY,GAAG,SAAS,SAAS,EAAE,CAgDzF;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,CAUtE;AAED,wBAAgB,YAAY,CAC1B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,IAAI,GAAE,MAAkB,EACxB,SAAS,GAAE,MAAU,GACpB,SAAS,IAAI,EAAE,CAkCjB;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,GAAE,MAAkB,GAAG,SAAS,SAAS,EAAE,CAwBzF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAI1E;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,SAAS,IAAI,EAAE,EACtB,KAAK,EAAE,MAAM,EACb,SAAS,GAAE,MAA2B,EACtC,MAAM,GAAE,MAAa,EACrB,OAAO,GAAE,MAAa,GACrB,WAAW,CAsBb;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,MAAM,CAI5D;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAEnE"}
package/dist/score.js ADDED
@@ -0,0 +1,166 @@
1
+ import { invalid } from "./errors.js";
2
+ import { kanaToUnits, lookupMora, toKatakana, vowelToKana } from "./mora.js";
3
+ import { noteToMidi } from "./pitch.js";
4
+ import { array, has, integer, isObject, keys, number, object, string } from "./validate.js";
5
+ export const DEFAULT_FRAME_RATE = 93.75;
6
+ export const MAX_NOTES = 4096;
7
+ export const MIN_BEATS = 1 / 64;
8
+ export const MAX_BEATS = 64;
9
+ const REST_TOKENS = new Set(["r", "rest", "_"]);
10
+ const TIE_TOKENS = new Set(["~", "-", "ー"]);
11
+ function parseBeat(token, path) {
12
+ const fraction = /^(\d+(?:\.\d+)?)\/(\d+(?:\.\d+)?)$/u.exec(token);
13
+ const value = fraction === null ? Number(token) : Number(fraction[1]) / Number(fraction[2]);
14
+ return number(value, path, MIN_BEATS, MAX_BEATS);
15
+ }
16
+ function tokens(text) {
17
+ return text
18
+ .normalize("NFKC")
19
+ .split(/[\s,]+/u)
20
+ .filter((token) => token.length > 0);
21
+ }
22
+ export function parseScoreText(input, path = "$") {
23
+ const melody = tokens(string(input.melody, `${path}.melody`, 1, 100_000));
24
+ if (melody.length === 0)
25
+ invalid(`${path}.melody`, "The melody has no notes.");
26
+ if (melody.length > MAX_NOTES)
27
+ invalid(`${path}.melody`, `A score is limited to ${MAX_NOTES} notes.`);
28
+ const rawBeats = input.beats;
29
+ const beatTokens = rawBeats === undefined ? [] : typeof rawBeats === "string" ? tokens(rawBeats) : rawBeats.map((b) => String(b));
30
+ const beats = beatTokens.map((token, index) => parseBeat(token, `${path}.beats[${index}]`));
31
+ if (beats.length > 1 && beats.length !== melody.length) {
32
+ invalid(`${path}.beats`, `Expected ${melody.length} beat values to match the melody, got ${beats.length}.`);
33
+ }
34
+ const moras = kanaToUnits(string(input.lyrics, `${path}.lyrics`, 0, 100_000), `${path}.lyrics`).filter((unit) => unit.kind === "mora");
35
+ const notes = [];
36
+ let moraIndex = 0;
37
+ for (const [index, token] of melody.entries()) {
38
+ const beat = beats.length === 0 ? 1 : (beats[beats.length === 1 ? 0 : index] ?? 1);
39
+ const lower = token.toLowerCase();
40
+ if (REST_TOKENS.has(lower)) {
41
+ notes.push({ key: null, beats: beat, lyric: "" });
42
+ continue;
43
+ }
44
+ if (TIE_TOKENS.has(token)) {
45
+ const previous = notes.findLast((note) => note.key !== null);
46
+ if (previous === undefined)
47
+ invalid(`${path}.melody[${index}]`, "A tie needs a preceding pitched note.");
48
+ notes.push({ key: previous.key, beats: beat, lyric: "ー" });
49
+ continue;
50
+ }
51
+ const mora = moras[moraIndex];
52
+ if (mora === undefined) {
53
+ invalid(`${path}.lyrics`, `The melody has more pitched notes than the lyrics have moras (${moras.length} moras).`, "Give one kana mora per pitched note, or use R for rests and ~ for ties in the melody.");
54
+ }
55
+ moraIndex += 1;
56
+ notes.push({ key: noteToMidi(token, `${path}.melody[${index}]`), beats: beat, lyric: mora.text });
57
+ }
58
+ if (moraIndex < moras.length) {
59
+ invalid(`${path}.lyrics`, `The lyrics have ${moras.length - moraIndex} more moras than the melody has pitched notes.`, "Add notes to the melody or shorten the lyrics.");
60
+ }
61
+ return notes;
62
+ }
63
+ export function parseNoteInput(value, path) {
64
+ const note = object(value, path);
65
+ keys(note, ["key", "beats", "lyric"], path);
66
+ const key = note["key"];
67
+ if (key !== null && typeof key !== "number" && typeof key !== "string") {
68
+ invalid(`${path}.key`, "Expected a MIDI integer, a note name such as C4, or null for a rest.");
69
+ }
70
+ const beats = number(note["beats"], `${path}.beats`, MIN_BEATS, MAX_BEATS);
71
+ const lyric = has(note, "lyric") ? string(note["lyric"], `${path}.lyric`, 0, 8) : "";
72
+ return { key, beats, lyric };
73
+ }
74
+ export function resolveNotes(input, path = "$.notes", transpose = 0) {
75
+ const resolved = [];
76
+ for (const [index, note] of input.entries()) {
77
+ const notePath = `${path}[${index}]`;
78
+ const lyric = toKatakana(note.lyric).replaceAll(/\s+/gu, "");
79
+ if (note.key === null) {
80
+ if (lyric !== "")
81
+ invalid(`${notePath}.lyric`, "A rest (key null) must have an empty lyric.");
82
+ resolved.push({ key: null, beats: note.beats, lyric: "" });
83
+ continue;
84
+ }
85
+ const key = noteToMidi(note.key, `${notePath}.key`) + transpose;
86
+ if (key < 0 || key > 127)
87
+ invalid(`${notePath}.key`, `Transposed key ${key} is outside the MIDI range 0–127.`);
88
+ if (lyric.length === 0)
89
+ invalid(`${notePath}.lyric`, "A pitched note needs a one-mora lyric such as ア or キャ.");
90
+ if (TIE_TOKENS.has(lyric)) {
91
+ const previous = resolved.findLast((n) => n.key !== null);
92
+ if (previous === undefined)
93
+ invalid(`${notePath}.lyric`, "A tie needs a preceding pitched note.");
94
+ const phonemes = lookupMora(previous.lyric);
95
+ if (phonemes === undefined || phonemes.vowel === "cl" || phonemes.vowel === "pau") {
96
+ invalid(`${notePath}.lyric`, "A tie must follow a note with a voiced vowel.");
97
+ }
98
+ resolved.push({ key, beats: note.beats, lyric: vowelToKana(phonemes.vowel) });
99
+ continue;
100
+ }
101
+ const phonemes = lookupMora(lyric);
102
+ if (phonemes === undefined) {
103
+ invalid(`${notePath}.lyric`, `Lyric ${JSON.stringify(note.lyric)} is not a single kana mora.`, "VOICEVOX sings exactly one mora per note; split multi-mora lyrics across notes.");
104
+ }
105
+ resolved.push({ key, beats: note.beats, lyric });
106
+ }
107
+ return resolved;
108
+ }
109
+ export function parseNotes(value, path = "$.notes") {
110
+ if (isObject(value)) {
111
+ keys(value, ["lyrics", "melody", "beats"], path);
112
+ const beats = value["beats"];
113
+ if (beats !== undefined && typeof beats !== "string" && !Array.isArray(beats)) {
114
+ invalid(`${path}.beats`, "Expected a string of beat values or an array of numbers.");
115
+ }
116
+ return parseScoreText({
117
+ lyrics: string(value["lyrics"], `${path}.lyrics`, 0, 100_000),
118
+ melody: string(value["melody"], `${path}.melody`, 1, 100_000),
119
+ ...(beats === undefined
120
+ ? {}
121
+ : {
122
+ beats: typeof beats === "string"
123
+ ? beats
124
+ : beats.map((b, i) => number(b, `${path}.beats[${i}]`, MIN_BEATS, MAX_BEATS)),
125
+ }),
126
+ }, path);
127
+ }
128
+ return array(value, path, 1, MAX_NOTES).map((note, index) => parseNoteInput(note, `${path}[${index}]`));
129
+ }
130
+ export function scoreSeconds(notes, tempo) {
131
+ let beats = 0;
132
+ for (const note of notes)
133
+ beats += note.beats;
134
+ return (beats * 60) / tempo;
135
+ }
136
+ export function notesToEngineScore(notes, tempo, frameRate = DEFAULT_FRAME_RATE, leadIn = 0.16, leadOut = 0.16) {
137
+ const engineNotes = [];
138
+ const padding = (seconds) => Math.max(1, Math.round(seconds * frameRate));
139
+ if (notes[0]?.key !== null)
140
+ engineNotes.push({ id: "lead-in", key: null, frame_length: padding(leadIn), lyric: "" });
141
+ let frames = 0;
142
+ let seconds = 0;
143
+ for (const [index, note] of notes.entries()) {
144
+ seconds += (note.beats * 60) / tempo;
145
+ const boundary = Math.round(seconds * frameRate);
146
+ const length = boundary - frames;
147
+ if (length < 1) {
148
+ invalid(`$.notes[${index}].beats`, `Note ${index} is shorter than one engine frame (${(1000 / frameRate).toFixed(2)} ms at ${tempo} BPM).`);
149
+ }
150
+ engineNotes.push({ id: `n${index}`, key: note.key, frame_length: length, lyric: note.lyric });
151
+ frames = boundary;
152
+ }
153
+ if (notes.at(-1)?.key !== null)
154
+ engineNotes.push({ id: "lead-out", key: null, frame_length: padding(leadOut), lyric: "" });
155
+ return { notes: engineNotes };
156
+ }
157
+ export function engineScoreFrames(score) {
158
+ let total = 0;
159
+ for (const note of score.notes)
160
+ total += note.frame_length;
161
+ return total;
162
+ }
163
+ export function transposeInput(value, path) {
164
+ return integer(value, path, -48, 48);
165
+ }
166
+ //# sourceMappingURL=score.js.map