@ssml-builder-js/ssml-editor-react 2.4.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.
- package/CHANGELOG.md +91 -0
- package/dist/index.d.mts +288 -0
- package/dist/index.d.ts +288 -0
- package/dist/index.js +5423 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +5385 -0
- package/dist/index.mjs.map +1 -0
- package/e2e/monaco-editor.spec.ts +126 -0
- package/package.json +57 -0
- package/src/SsmlEditor.tsx +1302 -0
- package/src/buttonVisibility.ts +36 -0
- package/src/clearSsmlDocument.ts +57 -0
- package/src/components/popovers/InsertionPopover.tsx +136 -0
- package/src/components/popovers/InsertionPopovers.tsx +47 -0
- package/src/components/popovers/ProsodyPopovers.tsx +78 -0
- package/src/components/popovers/TextPopovers.tsx +8 -0
- package/src/components/popovers/TimingPopovers.tsx +8 -0
- package/src/constants/ssmlPresets.ts +660 -0
- package/src/constants/ui.ts +11 -0
- package/src/editableSsml.ts +251 -0
- package/src/formatXml.ts +600 -0
- package/src/hooks/useSsmlEditorState.ts +704 -0
- package/src/hooks/useSsmlMonaco.ts +393 -0
- package/src/index.tsx +50 -0
- package/src/locales.ts +435 -0
- package/src/ssmlCodeAction.ts +144 -0
- package/src/ssmlCodeLens.ts +226 -0
- package/src/ssmlCompletion.ts +129 -0
- package/src/ssmlContext.ts +196 -0
- package/src/ssmlDiagnostics.ts +191 -0
- package/src/ssmlHover.ts +703 -0
- package/src/ssmlInsertion.ts +75 -0
- package/src/ssmlInsertions.ts +471 -0
- package/src/styles/editorStyles.ts +282 -0
- package/test/format-xml-edge-cases.test.ts +107 -0
- package/test/index.test.ts +597 -0
- package/test/randomized-editor-invariants.test.ts +520 -0
- package/test/ui-components.test.tsx +854 -0
- package/tsconfig.json +10 -0
- package/tsup.config.ts +17 -0
- package/vitest.config.mjs +10 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
type SsmlPresetDescription = Readonly<Record<"ja" | "en", string>>;
|
|
2
|
+
|
|
3
|
+
export interface SsmlPreset {
|
|
4
|
+
id: string;
|
|
5
|
+
label: string;
|
|
6
|
+
ssml: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const SSML_PRESETS: readonly SsmlPreset[] = [
|
|
11
|
+
{
|
|
12
|
+
id: "basic",
|
|
13
|
+
label: "Basic speech",
|
|
14
|
+
ssml: '<speak version="1.0" xml:lang="en-US">Hello, world!</speak>',
|
|
15
|
+
description: "A minimal SSML document.",
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: "voice",
|
|
19
|
+
label: "Voice selection",
|
|
20
|
+
ssml: '<speak version="1.0" xml:lang="en-US"><voice name="en-US-JennyNeural">Hello, world!</voice></speak>',
|
|
21
|
+
description: "Speaks text with a selected voice.",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: "prosody",
|
|
25
|
+
label: "Prosody",
|
|
26
|
+
ssml: '<speak version="1.0" xml:lang="en-US"><prosody rate="fast" pitch="+2st">Hello, world!</prosody></speak>',
|
|
27
|
+
description: "Adjusts the speech rate and pitch.",
|
|
28
|
+
},
|
|
29
|
+
] as const;
|
|
30
|
+
|
|
31
|
+
export const BREAK_TIME_PRESETS = ["500ms", "1s", "2s", "3s"] as const;
|
|
32
|
+
export const BREAK_STRENGTH_PRESETS = ["none", "x-weak", "weak", "medium", "strong", "x-strong"] as const;
|
|
33
|
+
|
|
34
|
+
export const PROSODY_RATE_PRESETS = ["x-slow", "slow", "medium", "fast", "x-fast"] as const;
|
|
35
|
+
export const PROSODY_RATE_VALUES = [...PROSODY_RATE_PRESETS, "percentage"] as const;
|
|
36
|
+
export const PROSODY_PITCH_PRESETS = ["+2st", "-2st", "0st", "+4st", "-4st", "+8st", "-8st", "+12st", "-12st"] as const;
|
|
37
|
+
export const PROSODY_VOLUME_PRESETS = ["silent", "x-soft", "soft", "medium", "loud", "x-loud"] as const;
|
|
38
|
+
|
|
39
|
+
export const EXPRESS_AS_STYLE_PRESETS = [
|
|
40
|
+
"cheerful",
|
|
41
|
+
"friendly",
|
|
42
|
+
"calm",
|
|
43
|
+
"sad",
|
|
44
|
+
"angry",
|
|
45
|
+
"excited",
|
|
46
|
+
"serious",
|
|
47
|
+
"assistant",
|
|
48
|
+
"chat",
|
|
49
|
+
"customerservice",
|
|
50
|
+
"hopeful",
|
|
51
|
+
"newscast",
|
|
52
|
+
"shouting",
|
|
53
|
+
"terrified",
|
|
54
|
+
"unfriendly",
|
|
55
|
+
"whispering",
|
|
56
|
+
"empathetic",
|
|
57
|
+
"relieved",
|
|
58
|
+
"fearful",
|
|
59
|
+
"depressed",
|
|
60
|
+
"disgruntled",
|
|
61
|
+
"embarrassed",
|
|
62
|
+
"narration-relaxed",
|
|
63
|
+
"poetry-reading",
|
|
64
|
+
"sports_commentary",
|
|
65
|
+
"sports_commentary_excited",
|
|
66
|
+
"story",
|
|
67
|
+
] as const;
|
|
68
|
+
export type ExpressAsStyle = (typeof EXPRESS_AS_STYLE_PRESETS)[number];
|
|
69
|
+
|
|
70
|
+
export const EXPRESS_AS_STYLE_CATEGORIES = {
|
|
71
|
+
emotions: [
|
|
72
|
+
"cheerful",
|
|
73
|
+
"sad",
|
|
74
|
+
"angry",
|
|
75
|
+
"calm",
|
|
76
|
+
"fearful",
|
|
77
|
+
"depressed",
|
|
78
|
+
"disgruntled",
|
|
79
|
+
"embarrassed",
|
|
80
|
+
"empathetic",
|
|
81
|
+
"envious",
|
|
82
|
+
"excited",
|
|
83
|
+
"friendly",
|
|
84
|
+
"gentle",
|
|
85
|
+
"hopeful",
|
|
86
|
+
"relieved",
|
|
87
|
+
"serious",
|
|
88
|
+
"shouting",
|
|
89
|
+
"terrified",
|
|
90
|
+
"unfriendly",
|
|
91
|
+
"whispering",
|
|
92
|
+
],
|
|
93
|
+
scenarios: ["chat", "customerservice", "assistant", "livecommercial", "poetry-reading", "story"],
|
|
94
|
+
media: [
|
|
95
|
+
"newscast",
|
|
96
|
+
"newscast-casual",
|
|
97
|
+
"newscast-formal",
|
|
98
|
+
"narration-professional",
|
|
99
|
+
"narration-relaxed",
|
|
100
|
+
"documentary-narration",
|
|
101
|
+
"advertisement_upbeat",
|
|
102
|
+
"sports_commentary",
|
|
103
|
+
"sports_commentary_excited",
|
|
104
|
+
],
|
|
105
|
+
} as const;
|
|
106
|
+
|
|
107
|
+
export type ExpressAsStyleCategory = keyof typeof EXPRESS_AS_STYLE_CATEGORIES | "other";
|
|
108
|
+
|
|
109
|
+
export function getExpressAsStyleCategory(style: string): ExpressAsStyleCategory {
|
|
110
|
+
for (const [category, styles] of Object.entries(EXPRESS_AS_STYLE_CATEGORIES) as [
|
|
111
|
+
keyof typeof EXPRESS_AS_STYLE_CATEGORIES,
|
|
112
|
+
readonly string[],
|
|
113
|
+
][]) {
|
|
114
|
+
if (styles.includes(style)) {
|
|
115
|
+
return category;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return "other";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export const VOICE_STYLE_MAP = {
|
|
123
|
+
"ja-JP-MayuNeural": ["calm", "cheerful", "sad"],
|
|
124
|
+
"ja-JP-KeitaNeural": ["chat"],
|
|
125
|
+
"ja-JP-NanamiNeural": ["chat", "customerservice", "cheerful", "whispering", "sad"],
|
|
126
|
+
"en-US-JennyMultilingualNeural": [
|
|
127
|
+
"cheerful",
|
|
128
|
+
"empathetic",
|
|
129
|
+
"excited",
|
|
130
|
+
"friendly",
|
|
131
|
+
"hopeful",
|
|
132
|
+
"sad",
|
|
133
|
+
"shouting",
|
|
134
|
+
"terrified",
|
|
135
|
+
"unfriendly",
|
|
136
|
+
"whispering",
|
|
137
|
+
],
|
|
138
|
+
"en-US-AndrewNeural": ["empathetic", "relieved"],
|
|
139
|
+
"en-US-JennyNeural": [
|
|
140
|
+
"assistant",
|
|
141
|
+
"chat",
|
|
142
|
+
"customerservice",
|
|
143
|
+
"newscast",
|
|
144
|
+
"cheerful",
|
|
145
|
+
"empathetic",
|
|
146
|
+
"excited",
|
|
147
|
+
"friendly",
|
|
148
|
+
"hopeful",
|
|
149
|
+
"sad",
|
|
150
|
+
"shouting",
|
|
151
|
+
"terrified",
|
|
152
|
+
"unfriendly",
|
|
153
|
+
"whispering",
|
|
154
|
+
],
|
|
155
|
+
"en-US-GuyNeural": [
|
|
156
|
+
"angry",
|
|
157
|
+
"cheerful",
|
|
158
|
+
"excited",
|
|
159
|
+
"friendly",
|
|
160
|
+
"hopeful",
|
|
161
|
+
"newscast",
|
|
162
|
+
"sad",
|
|
163
|
+
"shouting",
|
|
164
|
+
"terrified",
|
|
165
|
+
"unfriendly",
|
|
166
|
+
"whispering",
|
|
167
|
+
],
|
|
168
|
+
"ko-KR-SunHiNeural": ["cheerful", "sad"],
|
|
169
|
+
"zh-CN-XiaoxiaoNeural": [
|
|
170
|
+
"assistant",
|
|
171
|
+
"chat",
|
|
172
|
+
"customerservice",
|
|
173
|
+
"newscast",
|
|
174
|
+
"cheerful",
|
|
175
|
+
"empathetic",
|
|
176
|
+
"excited",
|
|
177
|
+
"friendly",
|
|
178
|
+
"hopeful",
|
|
179
|
+
"sad",
|
|
180
|
+
"terrified",
|
|
181
|
+
"whispering",
|
|
182
|
+
"poetry-reading",
|
|
183
|
+
"sports_commentary",
|
|
184
|
+
"sports_commentary_excited",
|
|
185
|
+
"story",
|
|
186
|
+
],
|
|
187
|
+
"zh-CN-YunxiNeural": [
|
|
188
|
+
"narration-relaxed",
|
|
189
|
+
"embarrassed",
|
|
190
|
+
"fearful",
|
|
191
|
+
"sad",
|
|
192
|
+
"disgruntled",
|
|
193
|
+
"serious",
|
|
194
|
+
"angry",
|
|
195
|
+
"depressed",
|
|
196
|
+
"chat",
|
|
197
|
+
"cheerful",
|
|
198
|
+
"assistant",
|
|
199
|
+
],
|
|
200
|
+
"fr-FR-DeniseNeural": ["cheerful", "sad"],
|
|
201
|
+
"fr-FR-HenriNeural": ["cheerful", "sad"],
|
|
202
|
+
"pt-BR-FranciscaNeural": ["calm"],
|
|
203
|
+
"it-IT-ElsaNeural": ["cheerful", "sad"],
|
|
204
|
+
"de-DE-KatjaNeural": ["cheerful", "sad"],
|
|
205
|
+
"de-DE-ConradNeural": ["cheerful", "sad"],
|
|
206
|
+
"ru-RU-SvetlanaNeural": ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"],
|
|
207
|
+
} as const satisfies Readonly<Record<string, readonly ExpressAsStyle[]>>;
|
|
208
|
+
|
|
209
|
+
const VOICE_STYLE_MAP_BY_NORMALIZED_NAME = new Map<string, readonly ExpressAsStyle[]>(
|
|
210
|
+
Object.entries(VOICE_STYLE_MAP).map(([voiceName, styles]) => [voiceName.toLowerCase(), styles]),
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
export function resolveExpressAsStyles(
|
|
214
|
+
voiceName: string | null | undefined,
|
|
215
|
+
candidates: readonly string[] = EXPRESS_AS_STYLE_PRESETS,
|
|
216
|
+
): readonly string[] {
|
|
217
|
+
const normalizedVoiceName = voiceName?.trim().toLowerCase();
|
|
218
|
+
if (!normalizedVoiceName) {
|
|
219
|
+
return candidates;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
const supportedStyles = VOICE_STYLE_MAP_BY_NORMALIZED_NAME.get(normalizedVoiceName);
|
|
223
|
+
if (supportedStyles === undefined) {
|
|
224
|
+
return [];
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const supportedStyleSet = new Set<string>(supportedStyles);
|
|
228
|
+
return candidates.filter((style) => supportedStyleSet.has(style));
|
|
229
|
+
}
|
|
230
|
+
export const EXPRESS_AS_ROLE_PRESETS = [
|
|
231
|
+
"Girl",
|
|
232
|
+
"Boy",
|
|
233
|
+
"YoungAdultFemale",
|
|
234
|
+
"YoungAdultMale",
|
|
235
|
+
"OlderAdultFemale",
|
|
236
|
+
"OlderAdultMale",
|
|
237
|
+
"SeniorFemale",
|
|
238
|
+
"SeniorMale",
|
|
239
|
+
] as const;
|
|
240
|
+
export const EMPHASIS_LEVEL_PRESETS = ["strong", "moderate", "reduced", "none"] as const;
|
|
241
|
+
export const SAY_AS_PRESETS = [
|
|
242
|
+
"characters",
|
|
243
|
+
"spell-out",
|
|
244
|
+
"cardinal",
|
|
245
|
+
"ordinal",
|
|
246
|
+
"number",
|
|
247
|
+
"date",
|
|
248
|
+
"time",
|
|
249
|
+
"telephone",
|
|
250
|
+
"fraction",
|
|
251
|
+
"address",
|
|
252
|
+
"name",
|
|
253
|
+
"currency",
|
|
254
|
+
] as const;
|
|
255
|
+
export const LANGUAGE_PRESETS = ["ja-JP", "en-US", "de-DE", "fr-FR"] as const;
|
|
256
|
+
export const SILENCE_VALUE_PRESETS = ["300ms", "500ms", "1s"] as const;
|
|
257
|
+
export const SILENCE_TYPE_PRESETS = [
|
|
258
|
+
"Leading",
|
|
259
|
+
"Tailing",
|
|
260
|
+
"Sentenceboundary",
|
|
261
|
+
"Comma",
|
|
262
|
+
"Semicolon",
|
|
263
|
+
"Enumerationcomma",
|
|
264
|
+
] as const;
|
|
265
|
+
export const PHONEME_ALPHABET_PRESETS = ["ipa", "sapi", "ups", "x-sampa"] as const;
|
|
266
|
+
export const VISEME_TYPE_PRESETS = ["redlips_front", "FacialExpression"] as const;
|
|
267
|
+
|
|
268
|
+
export const SSML_ATTRIBUTE_PRESETS = {
|
|
269
|
+
break: {
|
|
270
|
+
strength: BREAK_STRENGTH_PRESETS,
|
|
271
|
+
time: BREAK_TIME_PRESETS,
|
|
272
|
+
},
|
|
273
|
+
prosody: {
|
|
274
|
+
rate: PROSODY_RATE_PRESETS,
|
|
275
|
+
pitch: PROSODY_PITCH_PRESETS,
|
|
276
|
+
volume: PROSODY_VOLUME_PRESETS,
|
|
277
|
+
},
|
|
278
|
+
"mstts:express-as": {
|
|
279
|
+
style: EXPRESS_AS_STYLE_PRESETS,
|
|
280
|
+
role: EXPRESS_AS_ROLE_PRESETS,
|
|
281
|
+
},
|
|
282
|
+
"express-as": {
|
|
283
|
+
style: EXPRESS_AS_STYLE_PRESETS,
|
|
284
|
+
role: EXPRESS_AS_ROLE_PRESETS,
|
|
285
|
+
},
|
|
286
|
+
expressAs: {
|
|
287
|
+
style: EXPRESS_AS_STYLE_PRESETS,
|
|
288
|
+
role: EXPRESS_AS_ROLE_PRESETS,
|
|
289
|
+
},
|
|
290
|
+
"say-as": {
|
|
291
|
+
"interpret-as": SAY_AS_PRESETS,
|
|
292
|
+
},
|
|
293
|
+
sayAs: {
|
|
294
|
+
"interpret-as": SAY_AS_PRESETS,
|
|
295
|
+
},
|
|
296
|
+
emphasis: {
|
|
297
|
+
level: EMPHASIS_LEVEL_PRESETS,
|
|
298
|
+
},
|
|
299
|
+
lang: {
|
|
300
|
+
"xml:lang": LANGUAGE_PRESETS,
|
|
301
|
+
},
|
|
302
|
+
phoneme: {
|
|
303
|
+
alphabet: PHONEME_ALPHABET_PRESETS,
|
|
304
|
+
},
|
|
305
|
+
"mstts:silence": {
|
|
306
|
+
type: SILENCE_TYPE_PRESETS,
|
|
307
|
+
value: SILENCE_VALUE_PRESETS,
|
|
308
|
+
},
|
|
309
|
+
silence: {
|
|
310
|
+
type: SILENCE_TYPE_PRESETS,
|
|
311
|
+
value: SILENCE_VALUE_PRESETS,
|
|
312
|
+
},
|
|
313
|
+
"mstts:viseme": {
|
|
314
|
+
type: VISEME_TYPE_PRESETS,
|
|
315
|
+
},
|
|
316
|
+
viseme: {
|
|
317
|
+
type: VISEME_TYPE_PRESETS,
|
|
318
|
+
},
|
|
319
|
+
} as const satisfies Readonly<Record<string, Readonly<Record<string, readonly string[]>>>>;
|
|
320
|
+
|
|
321
|
+
export const SSML_PRESET_EXAMPLES = {
|
|
322
|
+
breakTime: "500ms",
|
|
323
|
+
prosodyRate: "fast",
|
|
324
|
+
prosodyPitch: "+2st",
|
|
325
|
+
prosodyVolume: "loud",
|
|
326
|
+
expressAsStyle: "cheerful",
|
|
327
|
+
phonemeAlphabet: "ipa",
|
|
328
|
+
silenceValue: "300ms",
|
|
329
|
+
} as const satisfies {
|
|
330
|
+
breakTime: (typeof BREAK_TIME_PRESETS)[number];
|
|
331
|
+
prosodyRate: (typeof PROSODY_RATE_PRESETS)[number];
|
|
332
|
+
prosodyPitch: (typeof PROSODY_PITCH_PRESETS)[number];
|
|
333
|
+
prosodyVolume: (typeof PROSODY_VOLUME_PRESETS)[number];
|
|
334
|
+
expressAsStyle: (typeof EXPRESS_AS_STYLE_PRESETS)[number];
|
|
335
|
+
phonemeAlphabet: (typeof PHONEME_ALPHABET_PRESETS)[number];
|
|
336
|
+
silenceValue: (typeof SILENCE_VALUE_PRESETS)[number];
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
export const BREAK_TIME_DESCRIPTIONS = {
|
|
340
|
+
"500ms": {
|
|
341
|
+
ja: "500ミリ秒の無音",
|
|
342
|
+
en: "Inserts 500 milliseconds of silence.",
|
|
343
|
+
},
|
|
344
|
+
"1s": {
|
|
345
|
+
ja: "1秒の無音",
|
|
346
|
+
en: "Inserts one second of silence.",
|
|
347
|
+
},
|
|
348
|
+
"2s": {
|
|
349
|
+
ja: "2秒の無音",
|
|
350
|
+
en: "Inserts two seconds of silence.",
|
|
351
|
+
},
|
|
352
|
+
"3s": {
|
|
353
|
+
ja: "3秒の無音",
|
|
354
|
+
en: "Inserts three seconds of silence.",
|
|
355
|
+
},
|
|
356
|
+
} as const satisfies Readonly<Record<(typeof BREAK_TIME_PRESETS)[number], SsmlPresetDescription>>;
|
|
357
|
+
|
|
358
|
+
export const EMPHASIS_LEVEL_DESCRIPTIONS = {
|
|
359
|
+
strong: {
|
|
360
|
+
ja: "強い強調",
|
|
361
|
+
en: "Applies strong emphasis.",
|
|
362
|
+
},
|
|
363
|
+
moderate: {
|
|
364
|
+
ja: "中程度の強調",
|
|
365
|
+
en: "Applies moderate emphasis.",
|
|
366
|
+
},
|
|
367
|
+
reduced: {
|
|
368
|
+
ja: "弱めの強調",
|
|
369
|
+
en: "Applies reduced emphasis.",
|
|
370
|
+
},
|
|
371
|
+
none: {
|
|
372
|
+
ja: "強調なし",
|
|
373
|
+
en: "Applies no emphasis.",
|
|
374
|
+
},
|
|
375
|
+
} as const satisfies Readonly<Record<(typeof EMPHASIS_LEVEL_PRESETS)[number], SsmlPresetDescription>>;
|
|
376
|
+
|
|
377
|
+
export const PROSODY_RATE_DESCRIPTIONS = {
|
|
378
|
+
"x-slow": {
|
|
379
|
+
ja: "最も遅い速度",
|
|
380
|
+
en: "Uses the slowest speech rate.",
|
|
381
|
+
},
|
|
382
|
+
slow: {
|
|
383
|
+
ja: "遅い速度",
|
|
384
|
+
en: "Uses a slow speech rate.",
|
|
385
|
+
},
|
|
386
|
+
medium: {
|
|
387
|
+
ja: "標準的な速度",
|
|
388
|
+
en: "Uses the standard speech rate.",
|
|
389
|
+
},
|
|
390
|
+
fast: {
|
|
391
|
+
ja: "速い速度",
|
|
392
|
+
en: "Uses a fast speech rate.",
|
|
393
|
+
},
|
|
394
|
+
"x-fast": {
|
|
395
|
+
ja: "最も速い速度",
|
|
396
|
+
en: "Uses the fastest speech rate.",
|
|
397
|
+
},
|
|
398
|
+
} as const satisfies Readonly<Record<(typeof PROSODY_RATE_PRESETS)[number], SsmlPresetDescription>>;
|
|
399
|
+
|
|
400
|
+
export const PROSODY_PITCH_DESCRIPTIONS = {
|
|
401
|
+
"+2st": {
|
|
402
|
+
ja: "基準の声の高さより2半音上",
|
|
403
|
+
en: "Raises the pitch by two semitones.",
|
|
404
|
+
},
|
|
405
|
+
"-2st": {
|
|
406
|
+
ja: "基準の声の高さより2半音下",
|
|
407
|
+
en: "Lowers the pitch by two semitones.",
|
|
408
|
+
},
|
|
409
|
+
"0st": {
|
|
410
|
+
ja: "基準の声の高さ",
|
|
411
|
+
en: "Keeps the baseline pitch.",
|
|
412
|
+
},
|
|
413
|
+
"+4st": {
|
|
414
|
+
ja: "基準の声の高さより4半音上",
|
|
415
|
+
en: "Raises the pitch by four semitones.",
|
|
416
|
+
},
|
|
417
|
+
"-4st": {
|
|
418
|
+
ja: "基準の声の高さより4半音下",
|
|
419
|
+
en: "Lowers the pitch by four semitones.",
|
|
420
|
+
},
|
|
421
|
+
"+8st": {
|
|
422
|
+
ja: "基準の声の高さより8半音上",
|
|
423
|
+
en: "Raises the pitch by eight semitones.",
|
|
424
|
+
},
|
|
425
|
+
"-8st": {
|
|
426
|
+
ja: "基準の声の高さより8半音下",
|
|
427
|
+
en: "Lowers the pitch by eight semitones.",
|
|
428
|
+
},
|
|
429
|
+
"+12st": {
|
|
430
|
+
ja: "基準の声の高さより12半音上",
|
|
431
|
+
en: "Raises the pitch by twelve semitones.",
|
|
432
|
+
},
|
|
433
|
+
"-12st": {
|
|
434
|
+
ja: "基準の声の高さより12半音下",
|
|
435
|
+
en: "Lowers the pitch by twelve semitones.",
|
|
436
|
+
},
|
|
437
|
+
} as const satisfies Readonly<Record<(typeof PROSODY_PITCH_PRESETS)[number], SsmlPresetDescription>>;
|
|
438
|
+
|
|
439
|
+
export const PROSODY_VOLUME_DESCRIPTIONS = {
|
|
440
|
+
silent: {
|
|
441
|
+
ja: "無音",
|
|
442
|
+
en: "Makes the selected text silent.",
|
|
443
|
+
},
|
|
444
|
+
"x-soft": {
|
|
445
|
+
ja: "最も小さい音量",
|
|
446
|
+
en: "Uses the quietest volume.",
|
|
447
|
+
},
|
|
448
|
+
soft: {
|
|
449
|
+
ja: "小さい音量",
|
|
450
|
+
en: "Uses a soft volume.",
|
|
451
|
+
},
|
|
452
|
+
medium: {
|
|
453
|
+
ja: "標準的な音量",
|
|
454
|
+
en: "Uses the standard volume.",
|
|
455
|
+
},
|
|
456
|
+
loud: {
|
|
457
|
+
ja: "大きい音量",
|
|
458
|
+
en: "Uses a loud volume.",
|
|
459
|
+
},
|
|
460
|
+
"x-loud": {
|
|
461
|
+
ja: "最も大きい音量",
|
|
462
|
+
en: "Uses the loudest volume.",
|
|
463
|
+
},
|
|
464
|
+
} as const satisfies Readonly<Record<(typeof PROSODY_VOLUME_PRESETS)[number], SsmlPresetDescription>>;
|
|
465
|
+
|
|
466
|
+
export const EXPRESS_AS_STYLE_DESCRIPTIONS = {
|
|
467
|
+
cheerful: {
|
|
468
|
+
ja: "明るく元気なスタイル",
|
|
469
|
+
en: "Uses a cheerful style.",
|
|
470
|
+
},
|
|
471
|
+
friendly: {
|
|
472
|
+
ja: "親しみやすいスタイル",
|
|
473
|
+
en: "Uses a friendly style.",
|
|
474
|
+
},
|
|
475
|
+
calm: {
|
|
476
|
+
ja: "穏やかなスタイル",
|
|
477
|
+
en: "Uses a calm style.",
|
|
478
|
+
},
|
|
479
|
+
sad: {
|
|
480
|
+
ja: "悲しげなスタイル",
|
|
481
|
+
en: "Uses a sad style.",
|
|
482
|
+
},
|
|
483
|
+
angry: {
|
|
484
|
+
ja: "怒ったようなスタイル",
|
|
485
|
+
en: "Uses an angry style.",
|
|
486
|
+
},
|
|
487
|
+
excited: {
|
|
488
|
+
ja: "興奮したスタイル",
|
|
489
|
+
en: "Uses an excited style.",
|
|
490
|
+
},
|
|
491
|
+
empathetic: {
|
|
492
|
+
ja: "共感を示すスタイル",
|
|
493
|
+
en: "Uses an empathetic style.",
|
|
494
|
+
},
|
|
495
|
+
relieved: {
|
|
496
|
+
ja: "安心したスタイル",
|
|
497
|
+
en: "Uses a relieved style.",
|
|
498
|
+
},
|
|
499
|
+
fearful: {
|
|
500
|
+
ja: "恐れを感じさせるスタイル",
|
|
501
|
+
en: "Uses a fearful style.",
|
|
502
|
+
},
|
|
503
|
+
depressed: {
|
|
504
|
+
ja: "落ち込んだスタイル",
|
|
505
|
+
en: "Uses a depressed style.",
|
|
506
|
+
},
|
|
507
|
+
disgruntled: {
|
|
508
|
+
ja: "不満を感じさせるスタイル",
|
|
509
|
+
en: "Uses a disgruntled style.",
|
|
510
|
+
},
|
|
511
|
+
embarrassed: {
|
|
512
|
+
ja: "恥ずかしそうなスタイル",
|
|
513
|
+
en: "Uses an embarrassed style.",
|
|
514
|
+
},
|
|
515
|
+
serious: {
|
|
516
|
+
ja: "真剣なスタイル",
|
|
517
|
+
en: "Uses a serious style.",
|
|
518
|
+
},
|
|
519
|
+
assistant: {
|
|
520
|
+
ja: "デジタルアシスタント向けのスタイル",
|
|
521
|
+
en: "Uses a digital assistant style.",
|
|
522
|
+
},
|
|
523
|
+
chat: {
|
|
524
|
+
ja: "会話向けのスタイル",
|
|
525
|
+
en: "Uses a conversational style.",
|
|
526
|
+
},
|
|
527
|
+
customerservice: {
|
|
528
|
+
ja: "カスタマーサービス向けのスタイル",
|
|
529
|
+
en: "Uses a customer service style.",
|
|
530
|
+
},
|
|
531
|
+
hopeful: {
|
|
532
|
+
ja: "希望に満ちたスタイル",
|
|
533
|
+
en: "Uses a hopeful style.",
|
|
534
|
+
},
|
|
535
|
+
newscast: {
|
|
536
|
+
ja: "ニュース読み上げ向けのスタイル",
|
|
537
|
+
en: "Uses a newscast style.",
|
|
538
|
+
},
|
|
539
|
+
shouting: {
|
|
540
|
+
ja: "叫ぶようなスタイル",
|
|
541
|
+
en: "Uses a shouting style.",
|
|
542
|
+
},
|
|
543
|
+
terrified: {
|
|
544
|
+
ja: "恐怖に満ちたスタイル",
|
|
545
|
+
en: "Uses a terrified style.",
|
|
546
|
+
},
|
|
547
|
+
unfriendly: {
|
|
548
|
+
ja: "無愛想なスタイル",
|
|
549
|
+
en: "Uses an unfriendly style.",
|
|
550
|
+
},
|
|
551
|
+
whispering: {
|
|
552
|
+
ja: "ささやくようなスタイル",
|
|
553
|
+
en: "Uses a whispering style.",
|
|
554
|
+
},
|
|
555
|
+
"narration-relaxed": {
|
|
556
|
+
ja: "リラックスしたナレーション向けのスタイル",
|
|
557
|
+
en: "Uses a relaxed narration style.",
|
|
558
|
+
},
|
|
559
|
+
"poetry-reading": {
|
|
560
|
+
ja: "詩の朗読向けのスタイル",
|
|
561
|
+
en: "Uses a poetry-reading style.",
|
|
562
|
+
},
|
|
563
|
+
sports_commentary: {
|
|
564
|
+
ja: "スポーツ実況向けのスタイル",
|
|
565
|
+
en: "Uses a sports commentary style.",
|
|
566
|
+
},
|
|
567
|
+
sports_commentary_excited: {
|
|
568
|
+
ja: "興奮したスポーツ実況向けのスタイル",
|
|
569
|
+
en: "Uses an excited sports commentary style.",
|
|
570
|
+
},
|
|
571
|
+
story: {
|
|
572
|
+
ja: "物語の朗読向けのスタイル",
|
|
573
|
+
en: "Uses a storytelling style.",
|
|
574
|
+
},
|
|
575
|
+
} as const satisfies Readonly<Record<(typeof EXPRESS_AS_STYLE_PRESETS)[number], SsmlPresetDescription>>;
|
|
576
|
+
|
|
577
|
+
export const SAY_AS_DESCRIPTIONS = {
|
|
578
|
+
characters: {
|
|
579
|
+
ja: "1文字ずつの読み上げ",
|
|
580
|
+
en: "Speaks the characters one by one.",
|
|
581
|
+
},
|
|
582
|
+
"spell-out": {
|
|
583
|
+
ja: "綴りの読み上げ(1文字ずつ)",
|
|
584
|
+
en: "Spells out the text character by character.",
|
|
585
|
+
},
|
|
586
|
+
cardinal: {
|
|
587
|
+
ja: "基数としての読み上げ",
|
|
588
|
+
en: "Speaks the value as a cardinal number.",
|
|
589
|
+
},
|
|
590
|
+
ordinal: {
|
|
591
|
+
ja: "序数としての読み上げ",
|
|
592
|
+
en: "Speaks the value as an ordinal number.",
|
|
593
|
+
},
|
|
594
|
+
number: {
|
|
595
|
+
ja: "数値としての読み上げ",
|
|
596
|
+
en: "Speaks the value as a number.",
|
|
597
|
+
},
|
|
598
|
+
date: {
|
|
599
|
+
ja: "日付としての読み上げ",
|
|
600
|
+
en: "Speaks the value as a date.",
|
|
601
|
+
},
|
|
602
|
+
time: {
|
|
603
|
+
ja: "時刻としての読み上げ",
|
|
604
|
+
en: "Speaks the value as a time.",
|
|
605
|
+
},
|
|
606
|
+
telephone: {
|
|
607
|
+
ja: "電話番号としての読み上げ",
|
|
608
|
+
en: "Speaks the value as a telephone number.",
|
|
609
|
+
},
|
|
610
|
+
fraction: {
|
|
611
|
+
ja: "分数としての読み上げ",
|
|
612
|
+
en: "Speaks the value as a fraction.",
|
|
613
|
+
},
|
|
614
|
+
address: {
|
|
615
|
+
ja: "住所としての読み上げ",
|
|
616
|
+
en: "Speaks the value as an address.",
|
|
617
|
+
},
|
|
618
|
+
name: {
|
|
619
|
+
ja: "名前としての読み上げ",
|
|
620
|
+
en: "Speaks the value as a name.",
|
|
621
|
+
},
|
|
622
|
+
currency: {
|
|
623
|
+
ja: "通貨としての読み上げ",
|
|
624
|
+
en: "Speaks the value as currency.",
|
|
625
|
+
},
|
|
626
|
+
} as const satisfies Readonly<Record<(typeof SAY_AS_PRESETS)[number], SsmlPresetDescription>>;
|
|
627
|
+
|
|
628
|
+
export const LANGUAGE_DESCRIPTIONS = {
|
|
629
|
+
"ja-JP": {
|
|
630
|
+
ja: "日本語(日本)で読み上げます。",
|
|
631
|
+
en: "Speaks the text in Japanese (Japan).",
|
|
632
|
+
},
|
|
633
|
+
"en-US": {
|
|
634
|
+
ja: "英語(米国)で読み上げます。",
|
|
635
|
+
en: "Speaks the text in English (United States).",
|
|
636
|
+
},
|
|
637
|
+
"de-DE": {
|
|
638
|
+
ja: "ドイツ語(ドイツ)で読み上げます。",
|
|
639
|
+
en: "Speaks the text in German (Germany).",
|
|
640
|
+
},
|
|
641
|
+
"fr-FR": {
|
|
642
|
+
ja: "フランス語(フランス)で読み上げます。",
|
|
643
|
+
en: "Speaks the text in French (France).",
|
|
644
|
+
},
|
|
645
|
+
} as const satisfies Readonly<Record<(typeof LANGUAGE_PRESETS)[number], SsmlPresetDescription>>;
|
|
646
|
+
|
|
647
|
+
export const SILENCE_VALUE_DESCRIPTIONS = {
|
|
648
|
+
"300ms": {
|
|
649
|
+
ja: "先頭に300ミリ秒の無音を挿入します。",
|
|
650
|
+
en: "Inserts 300 milliseconds of leading silence.",
|
|
651
|
+
},
|
|
652
|
+
"500ms": {
|
|
653
|
+
ja: "先頭に500ミリ秒の無音を挿入します。",
|
|
654
|
+
en: "Inserts 500 milliseconds of leading silence.",
|
|
655
|
+
},
|
|
656
|
+
"1s": {
|
|
657
|
+
ja: "先頭に1秒の無音を挿入します。",
|
|
658
|
+
en: "Inserts one second of leading silence.",
|
|
659
|
+
},
|
|
660
|
+
} as const satisfies Readonly<Record<(typeof SILENCE_VALUE_PRESETS)[number], SsmlPresetDescription>>;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SsmlEditorLocale } from "../locales.ts";
|
|
2
|
+
|
|
3
|
+
// Keep the editor's existing Japanese default for backward compatibility.
|
|
4
|
+
export const DEFAULT_LOCALE: SsmlEditorLocale = "ja";
|
|
5
|
+
export const OVERLAY_Z_INDEX = 9999;
|
|
6
|
+
export const SELECTION_OVERLAY_ABOVE_THRESHOLD_LINES = 4;
|
|
7
|
+
|
|
8
|
+
export const SSML_INSERTION_MODES = {
|
|
9
|
+
insert: "insert",
|
|
10
|
+
wrap: "wrap",
|
|
11
|
+
} as const;
|