ssml-builder-js 2.12.0 → 2.13.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/README.md +26 -1
- package/dist/{chunk-4BVNAUVR.mjs → chunk-25LOR4AJ.mjs} +134 -10
- package/dist/chunk-25LOR4AJ.mjs.map +1 -0
- package/dist/chunk-CZ2F3TET.mjs +1745 -0
- package/dist/chunk-CZ2F3TET.mjs.map +1 -0
- package/dist/{chunk-FHDFRM2F.mjs → chunk-LCTE26LS.mjs} +248 -1722
- package/dist/chunk-LCTE26LS.mjs.map +1 -0
- package/dist/core.d.mts +45 -3
- package/dist/core.d.ts +45 -3
- package/dist/core.js +133 -9
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +1 -1
- package/dist/elements.js +118 -1
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +6 -4
- package/dist/elements.mjs.map +1 -1
- package/dist/index.d-Xbr6ZWnK.d.mts +214 -0
- package/dist/index.d-Xbr6ZWnK.d.ts +214 -0
- package/dist/index.d.mts +95 -2
- package/dist/index.d.ts +95 -2
- package/dist/index.js +1732 -30
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +135 -4
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +61 -163
- package/dist/react.d.ts +61 -163
- package/dist/react.js +397 -15
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +139 -18
- package/dist/react.mjs.map +1 -1
- package/package.json +3 -2
- package/dist/chunk-4BVNAUVR.mjs.map +0 -1
- package/dist/chunk-FHDFRM2F.mjs.map +0 -1
|
@@ -1,8 +1,158 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildSsml,
|
|
3
|
+
parseSsml
|
|
4
|
+
} from "./chunk-CZ2F3TET.mjs";
|
|
1
5
|
import {
|
|
2
6
|
__export
|
|
3
7
|
} from "./chunk-6S5ODO6A.mjs";
|
|
4
8
|
|
|
5
9
|
// packages/ssml-editor-react/src/locales.ts
|
|
10
|
+
var SSML_ELEMENT_COPY = {
|
|
11
|
+
ja: {
|
|
12
|
+
dialog: {
|
|
13
|
+
label: "\u30C0\u30A4\u30A2\u30ED\u30B0",
|
|
14
|
+
description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002",
|
|
15
|
+
validationErrors: { turn: "turn \u306F dialog \u306E\u76F4\u4E0B\u306B\u914D\u7F6E\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
16
|
+
},
|
|
17
|
+
"mstts:dialog": {
|
|
18
|
+
label: "\u30C0\u30A4\u30A2\u30ED\u30B0",
|
|
19
|
+
description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002",
|
|
20
|
+
validationErrors: { turn: "turn \u306F dialog \u306E\u76F4\u4E0B\u306B\u914D\u7F6E\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
21
|
+
},
|
|
22
|
+
turn: {
|
|
23
|
+
label: "\u8A71\u8005\u30BF\u30FC\u30F3",
|
|
24
|
+
description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
25
|
+
validationErrors: { voice: "voice \u307E\u305F\u306F speaker \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
26
|
+
},
|
|
27
|
+
"mstts:turn": {
|
|
28
|
+
label: "\u8A71\u8005\u30BF\u30FC\u30F3",
|
|
29
|
+
description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
30
|
+
validationErrors: { voice: "voice \u307E\u305F\u306F speaker \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
31
|
+
},
|
|
32
|
+
backgroundaudio: {
|
|
33
|
+
label: "\u80CC\u666F\u97F3\u58F0",
|
|
34
|
+
description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
35
|
+
validationErrors: { src: "\u80CC\u666F\u97F3\u58F0\u306B\u306F URL \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
36
|
+
},
|
|
37
|
+
"mstts:backgroundaudio": {
|
|
38
|
+
label: "\u80CC\u666F\u97F3\u58F0",
|
|
39
|
+
description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
40
|
+
validationErrors: { src: "\u80CC\u666F\u97F3\u58F0\u306B\u306F URL \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
41
|
+
},
|
|
42
|
+
ttsembedding: {
|
|
43
|
+
label: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
|
|
44
|
+
description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
|
|
45
|
+
validationErrors: { speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
46
|
+
},
|
|
47
|
+
"mstts:ttsembedding": {
|
|
48
|
+
label: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
|
|
49
|
+
description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
|
|
50
|
+
validationErrors: { speakerProfileId: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID \u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
51
|
+
},
|
|
52
|
+
embedding: {
|
|
53
|
+
label: "\u57CB\u3081\u8FBC\u307F",
|
|
54
|
+
description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
55
|
+
validationErrors: {}
|
|
56
|
+
},
|
|
57
|
+
"mstts:embedding": {
|
|
58
|
+
label: "\u57CB\u3081\u8FBC\u307F",
|
|
59
|
+
description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
60
|
+
validationErrors: {}
|
|
61
|
+
},
|
|
62
|
+
silence: {
|
|
63
|
+
label: "\u7121\u97F3",
|
|
64
|
+
description: "\u767A\u8A71\u524D\u5F8C\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u306B\u7121\u97F3\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002",
|
|
65
|
+
validationErrors: { value: "\u7121\u97F3\u306E\u9577\u3055\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
66
|
+
},
|
|
67
|
+
"mstts:silence": {
|
|
68
|
+
label: "\u7121\u97F3",
|
|
69
|
+
description: "\u767A\u8A71\u524D\u5F8C\u307E\u305F\u306F\u53E5\u8AAD\u70B9\u306E\u5883\u754C\u306B\u7121\u97F3\u3092\u8FFD\u52A0\u3057\u307E\u3059\u3002",
|
|
70
|
+
validationErrors: { value: "\u7121\u97F3\u306E\u9577\u3055\u3092\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002" }
|
|
71
|
+
},
|
|
72
|
+
voiceconversion: {
|
|
73
|
+
label: "\u97F3\u58F0\u5909\u63DB",
|
|
74
|
+
description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
75
|
+
validationErrors: {}
|
|
76
|
+
},
|
|
77
|
+
"mstts:voiceconversion": {
|
|
78
|
+
label: "\u97F3\u58F0\u5909\u63DB",
|
|
79
|
+
description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
80
|
+
validationErrors: {}
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
en: {
|
|
84
|
+
dialog: {
|
|
85
|
+
label: "Dialog",
|
|
86
|
+
description: "Groups multiple speaker turns.",
|
|
87
|
+
validationErrors: { turn: "Place turns directly inside a dialog." }
|
|
88
|
+
},
|
|
89
|
+
"mstts:dialog": {
|
|
90
|
+
label: "Dialog",
|
|
91
|
+
description: "Groups multiple speaker turns.",
|
|
92
|
+
validationErrors: { turn: "Place turns directly inside a dialog." }
|
|
93
|
+
},
|
|
94
|
+
turn: {
|
|
95
|
+
label: "Dialog turn",
|
|
96
|
+
description: "Specifies a speaker and utterance within a dialog.",
|
|
97
|
+
validationErrors: { voice: "Provide a voice or speaker." }
|
|
98
|
+
},
|
|
99
|
+
"mstts:turn": {
|
|
100
|
+
label: "Dialog turn",
|
|
101
|
+
description: "Specifies a speaker and utterance within a dialog.",
|
|
102
|
+
validationErrors: { voice: "Provide a voice or speaker." }
|
|
103
|
+
},
|
|
104
|
+
backgroundaudio: {
|
|
105
|
+
label: "Background audio",
|
|
106
|
+
description: "Specifies an audio file played behind synthesized speech.",
|
|
107
|
+
validationErrors: { src: "Provide a background audio URL." }
|
|
108
|
+
},
|
|
109
|
+
"mstts:backgroundaudio": {
|
|
110
|
+
label: "Background audio",
|
|
111
|
+
description: "Specifies an audio file played behind synthesized speech.",
|
|
112
|
+
validationErrors: { src: "Provide a background audio URL." }
|
|
113
|
+
},
|
|
114
|
+
ttsembedding: {
|
|
115
|
+
label: "TTS embedding",
|
|
116
|
+
description: "Embeds custom voice or speaker profile metadata.",
|
|
117
|
+
validationErrors: { speakerProfileId: "Provide a speaker profile ID." }
|
|
118
|
+
},
|
|
119
|
+
"mstts:ttsembedding": {
|
|
120
|
+
label: "TTS embedding",
|
|
121
|
+
description: "Embeds custom voice or speaker profile metadata.",
|
|
122
|
+
validationErrors: { speakerProfileId: "Provide a speaker profile ID." }
|
|
123
|
+
},
|
|
124
|
+
embedding: {
|
|
125
|
+
label: "Embedding",
|
|
126
|
+
description: "Specifies speaker embedding metadata.",
|
|
127
|
+
validationErrors: {}
|
|
128
|
+
},
|
|
129
|
+
"mstts:embedding": {
|
|
130
|
+
label: "Embedding",
|
|
131
|
+
description: "Specifies speaker embedding metadata.",
|
|
132
|
+
validationErrors: {}
|
|
133
|
+
},
|
|
134
|
+
silence: {
|
|
135
|
+
label: "Silence",
|
|
136
|
+
description: "Adds silence before, after, or around punctuation.",
|
|
137
|
+
validationErrors: { value: "Provide a silence duration." }
|
|
138
|
+
},
|
|
139
|
+
"mstts:silence": {
|
|
140
|
+
label: "Silence",
|
|
141
|
+
description: "Adds silence before, after, or around punctuation.",
|
|
142
|
+
validationErrors: { value: "Provide a silence duration." }
|
|
143
|
+
},
|
|
144
|
+
voiceconversion: {
|
|
145
|
+
label: "Voice conversion",
|
|
146
|
+
description: "Specifies custom voice conversion metadata.",
|
|
147
|
+
validationErrors: {}
|
|
148
|
+
},
|
|
149
|
+
"mstts:voiceconversion": {
|
|
150
|
+
label: "Voice conversion",
|
|
151
|
+
description: "Specifies custom voice conversion metadata.",
|
|
152
|
+
validationErrors: {}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
6
156
|
var EDITOR_COPY = {
|
|
7
157
|
ja: {
|
|
8
158
|
editorAriaLabel: "SSML\u30A8\u30C7\u30A3\u30BF\u30FC",
|
|
@@ -220,6 +370,42 @@ var SSML_HOVER_COPY = {
|
|
|
220
370
|
parameters: {
|
|
221
371
|
value: { title: "value", description: "\u76EE\u6A19\u6642\u9593\u3002\u4F8B: `10s`\u3001`5000ms`\u3001`00:00:10`\u3002" }
|
|
222
372
|
}
|
|
373
|
+
},
|
|
374
|
+
"mstts:dialog": { title: "\u30C0\u30A4\u30A2\u30ED\u30B0", description: "\u8907\u6570\u306E\u8A71\u8005\u30BF\u30FC\u30F3\u3092\u307E\u3068\u3081\u307E\u3059\u3002", parameters: {} },
|
|
375
|
+
"mstts:turn": {
|
|
376
|
+
title: "\u8A71\u8005\u30BF\u30FC\u30F3",
|
|
377
|
+
description: "\u30C0\u30A4\u30A2\u30ED\u30B0\u5185\u306E\u8A71\u8005\u3068\u767A\u8A71\u5185\u5BB9\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
378
|
+
parameters: {
|
|
379
|
+
voice: { title: "voice", description: "\u3053\u306E\u30BF\u30FC\u30F3\u3067\u4F7F\u7528\u3059\u308B Azure \u97F3\u58F0\u540D\u3002" },
|
|
380
|
+
speaker: { title: "speaker", description: "\u8A71\u8005\u8B58\u5225\u5B50\u3002" }
|
|
381
|
+
}
|
|
382
|
+
},
|
|
383
|
+
"mstts:backgroundaudio": {
|
|
384
|
+
title: "\u80CC\u666F\u97F3\u58F0",
|
|
385
|
+
description: "\u5408\u6210\u97F3\u58F0\u306E\u80CC\u5F8C\u3067\u518D\u751F\u3059\u308B\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
386
|
+
parameters: { src: { title: "src", description: "\u97F3\u58F0\u30D5\u30A1\u30A4\u30EB\u306E HTTPS URL\u3002" } }
|
|
387
|
+
},
|
|
388
|
+
"mstts:ttsembedding": {
|
|
389
|
+
title: "\u97F3\u58F0\u57CB\u3081\u8FBC\u307F",
|
|
390
|
+
description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u307E\u305F\u306F\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u57CB\u3081\u8FBC\u307F\u307E\u3059\u3002",
|
|
391
|
+
parameters: { speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" } }
|
|
392
|
+
},
|
|
393
|
+
"mstts:embedding": {
|
|
394
|
+
title: "\u57CB\u3081\u8FBC\u307F",
|
|
395
|
+
description: "\u8A71\u8005\u57CB\u3081\u8FBC\u307F\u306E\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
396
|
+
parameters: {
|
|
397
|
+
id: { title: "id", description: "\u57CB\u3081\u8FBC\u307F ID\u3002" },
|
|
398
|
+
speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
"mstts:voiceconversion": {
|
|
402
|
+
title: "\u97F3\u58F0\u5909\u63DB",
|
|
403
|
+
description: "\u30AB\u30B9\u30BF\u30E0\u97F3\u58F0\u306E\u5909\u63DB\u30E1\u30BF\u30C7\u30FC\u30BF\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
404
|
+
parameters: {
|
|
405
|
+
url: { title: "url", description: "\u97F3\u58F0\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB URL\u3002" },
|
|
406
|
+
profile: { title: "profile", description: "\u5909\u63DB\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB\u3002" },
|
|
407
|
+
speakerProfileId: { title: "speakerProfileId", description: "\u8A71\u8005\u30D7\u30ED\u30D5\u30A1\u30A4\u30EB ID\u3002" }
|
|
408
|
+
}
|
|
223
409
|
}
|
|
224
410
|
}
|
|
225
411
|
},
|
|
@@ -384,6 +570,42 @@ var SSML_HOVER_COPY = {
|
|
|
384
570
|
parameters: {
|
|
385
571
|
value: { title: "value", description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`." }
|
|
386
572
|
}
|
|
573
|
+
},
|
|
574
|
+
"mstts:dialog": { title: "Dialog", description: "Groups multiple speaker turns.", parameters: {} },
|
|
575
|
+
"mstts:turn": {
|
|
576
|
+
title: "Dialog turn",
|
|
577
|
+
description: "Specifies a speaker and utterance within a dialog.",
|
|
578
|
+
parameters: {
|
|
579
|
+
voice: { title: "voice", description: "The Azure voice used by this turn." },
|
|
580
|
+
speaker: { title: "speaker", description: "The speaker identifier." }
|
|
581
|
+
}
|
|
582
|
+
},
|
|
583
|
+
"mstts:backgroundaudio": {
|
|
584
|
+
title: "Background audio",
|
|
585
|
+
description: "Specifies an audio file played behind synthesized speech.",
|
|
586
|
+
parameters: { src: { title: "src", description: "The HTTPS URL of the audio file." } }
|
|
587
|
+
},
|
|
588
|
+
"mstts:ttsembedding": {
|
|
589
|
+
title: "TTS embedding",
|
|
590
|
+
description: "Embeds custom voice or speaker profile metadata.",
|
|
591
|
+
parameters: { speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." } }
|
|
592
|
+
},
|
|
593
|
+
"mstts:embedding": {
|
|
594
|
+
title: "Embedding",
|
|
595
|
+
description: "Specifies speaker embedding metadata.",
|
|
596
|
+
parameters: {
|
|
597
|
+
id: { title: "id", description: "The embedding ID." },
|
|
598
|
+
speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
|
|
599
|
+
}
|
|
600
|
+
},
|
|
601
|
+
"mstts:voiceconversion": {
|
|
602
|
+
title: "Voice conversion",
|
|
603
|
+
description: "Specifies custom voice conversion metadata.",
|
|
604
|
+
parameters: {
|
|
605
|
+
url: { title: "url", description: "The voice conversion profile URL." },
|
|
606
|
+
profile: { title: "profile", description: "The conversion profile." },
|
|
607
|
+
speakerProfileId: { title: "speakerProfileId", description: "The speaker profile ID." }
|
|
608
|
+
}
|
|
387
609
|
}
|
|
388
610
|
}
|
|
389
611
|
}
|
|
@@ -1313,1698 +1535,6 @@ var SSML_INSERTION_MODES = {
|
|
|
1313
1535
|
wrap: "wrap"
|
|
1314
1536
|
};
|
|
1315
1537
|
|
|
1316
|
-
// packages/ssml-core/dist/index.mjs
|
|
1317
|
-
var __typeError = (msg) => {
|
|
1318
|
-
throw TypeError(msg);
|
|
1319
|
-
};
|
|
1320
|
-
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
|
|
1321
|
-
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
1322
|
-
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
1323
|
-
var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
1324
|
-
var SYNTHESIS_NAMESPACE = "http://www.w3.org/2001/10/synthesis";
|
|
1325
|
-
var MSTTS_NAMESPACE = "http://www.w3.org/2001/mstts";
|
|
1326
|
-
var DEFAULT_SSML_VERSION = "1.0";
|
|
1327
|
-
var DEFAULT_SSML_LANGUAGE = "en-US";
|
|
1328
|
-
var MAX_NESTING_DEPTH = 1e3;
|
|
1329
|
-
var XML_NAME_PATTERN = /^[A-Za-z_][A-Za-z0-9_.:-]*$/;
|
|
1330
|
-
var MSTTS_TAG_PREFIX = "mstts:";
|
|
1331
|
-
var SSML_TAGS = {
|
|
1332
|
-
SPEAK: "speak",
|
|
1333
|
-
VOICE: "voice",
|
|
1334
|
-
PROSODY: "prosody",
|
|
1335
|
-
BREAK: "break",
|
|
1336
|
-
EXPRESS_AS: "express-as",
|
|
1337
|
-
EXPRESS_AS_CAMEL: "expressAs",
|
|
1338
|
-
MSTTS_EXPRESS_AS: "mstts:express-as",
|
|
1339
|
-
SAY_AS: "say-as",
|
|
1340
|
-
SAY_AS_CAMEL: "sayAs",
|
|
1341
|
-
PHONEME: "phoneme",
|
|
1342
|
-
EMPHASIS: "emphasis",
|
|
1343
|
-
AUDIO: "audio",
|
|
1344
|
-
SUB: "sub",
|
|
1345
|
-
LANG: "lang",
|
|
1346
|
-
MARK: "mark",
|
|
1347
|
-
BOOKMARK: "bookmark",
|
|
1348
|
-
LEXICON: "lexicon",
|
|
1349
|
-
PARAGRAPH: "p",
|
|
1350
|
-
SENTENCE: "s",
|
|
1351
|
-
WORD: "w",
|
|
1352
|
-
MSTTS_SILENCE: "mstts:silence",
|
|
1353
|
-
SILENCE: "silence",
|
|
1354
|
-
MSTTS_VISEME: "mstts:viseme",
|
|
1355
|
-
VISEME: "viseme",
|
|
1356
|
-
MSTTS_AUDIO_DURATION: "mstts:audioduration",
|
|
1357
|
-
MSTTS_DIALOG: "mstts:dialog",
|
|
1358
|
-
MSTTS_TURN: "mstts:turn",
|
|
1359
|
-
MSTTS_BACKGROUND_AUDIO: "mstts:backgroundaudio",
|
|
1360
|
-
MSTTS_TTS_EMBEDDING: "mstts:ttsembedding",
|
|
1361
|
-
MSTTS_EMBEDDING: "mstts:embedding",
|
|
1362
|
-
MSTTS_VOICE_CONVERSION: "mstts:voiceconversion"
|
|
1363
|
-
};
|
|
1364
|
-
var SSML_ATTRS = {
|
|
1365
|
-
VERSION: "version",
|
|
1366
|
-
XMLNS: "xmlns",
|
|
1367
|
-
XML_LANG: "xml:lang",
|
|
1368
|
-
LANG: "lang",
|
|
1369
|
-
MSTTS_XMLNS: "xmlns:mstts",
|
|
1370
|
-
NAME: "name",
|
|
1371
|
-
VOICE: "voice",
|
|
1372
|
-
SPEAKER: "speaker",
|
|
1373
|
-
EFFECT: "effect",
|
|
1374
|
-
RATE: "rate",
|
|
1375
|
-
PITCH: "pitch",
|
|
1376
|
-
VOLUME: "volume",
|
|
1377
|
-
CONTOUR: "contour",
|
|
1378
|
-
RANGE: "range",
|
|
1379
|
-
TIME: "time",
|
|
1380
|
-
STRENGTH: "strength",
|
|
1381
|
-
STYLE: "style",
|
|
1382
|
-
STYLE_DEGREE: "styledegree",
|
|
1383
|
-
STYLE_DEGREE_CAMEL: "styleDegree",
|
|
1384
|
-
STYLE_DEGREE_HYPHEN: "style-degree",
|
|
1385
|
-
ROLE: "role",
|
|
1386
|
-
INTERPRET_AS: "interpret-as",
|
|
1387
|
-
FORMAT: "format",
|
|
1388
|
-
DETAIL: "detail",
|
|
1389
|
-
ALPHABET: "alphabet",
|
|
1390
|
-
PH: "ph",
|
|
1391
|
-
LEVEL: "level",
|
|
1392
|
-
SRC: "src",
|
|
1393
|
-
DESC: "desc",
|
|
1394
|
-
CLIP_BEGIN: "clipBegin",
|
|
1395
|
-
CLIP_END: "clipEnd",
|
|
1396
|
-
SPEED: "speed",
|
|
1397
|
-
REPEAT_COUNT: "repeatCount",
|
|
1398
|
-
REPEAT_DURATION: "repeatDuration",
|
|
1399
|
-
SOUND_LEVEL: "soundLevel",
|
|
1400
|
-
ALIAS: "alias",
|
|
1401
|
-
MARK: "mark",
|
|
1402
|
-
URI: "uri",
|
|
1403
|
-
ID: "id",
|
|
1404
|
-
MODEL: "model",
|
|
1405
|
-
PROFILE: "profile",
|
|
1406
|
-
URL: "url",
|
|
1407
|
-
SPEAKER_PROFILE_ID: "speakerProfileId",
|
|
1408
|
-
TYPE: "type",
|
|
1409
|
-
VALUE: "value",
|
|
1410
|
-
FADE_IN: "fadein",
|
|
1411
|
-
FADE_OUT: "fadeout"
|
|
1412
|
-
};
|
|
1413
|
-
function escapeText(value) {
|
|
1414
|
-
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1415
|
-
}
|
|
1416
|
-
function escapeAttribute(value) {
|
|
1417
|
-
return escapeText(value).replace(/"/g, """).replace(/'/g, "'");
|
|
1418
|
-
}
|
|
1419
|
-
function addAttribute(attributes, name, value) {
|
|
1420
|
-
if (value !== void 0) {
|
|
1421
|
-
attributes[name] = value;
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
function getAttributes(element) {
|
|
1425
|
-
const attributes = {
|
|
1426
|
-
...element.attributes ?? {}
|
|
1427
|
-
};
|
|
1428
|
-
switch (element.type) {
|
|
1429
|
-
case SSML_TAGS.VOICE:
|
|
1430
|
-
addAttribute(attributes, SSML_ATTRS.NAME, element.name);
|
|
1431
|
-
addAttribute(attributes, SSML_ATTRS.EFFECT, element.effect);
|
|
1432
|
-
break;
|
|
1433
|
-
case SSML_TAGS.PROSODY:
|
|
1434
|
-
addAttribute(attributes, SSML_ATTRS.RATE, element.rate);
|
|
1435
|
-
addAttribute(attributes, SSML_ATTRS.PITCH, element.pitch);
|
|
1436
|
-
addAttribute(attributes, SSML_ATTRS.VOLUME, element.volume);
|
|
1437
|
-
addAttribute(attributes, SSML_ATTRS.CONTOUR, element.contour);
|
|
1438
|
-
addAttribute(attributes, SSML_ATTRS.RANGE, element.range);
|
|
1439
|
-
break;
|
|
1440
|
-
case SSML_TAGS.BREAK:
|
|
1441
|
-
addAttribute(attributes, SSML_ATTRS.TIME, element.time);
|
|
1442
|
-
addAttribute(attributes, SSML_ATTRS.STRENGTH, element.strength);
|
|
1443
|
-
break;
|
|
1444
|
-
case SSML_TAGS.EXPRESS_AS:
|
|
1445
|
-
case SSML_TAGS.EXPRESS_AS_CAMEL:
|
|
1446
|
-
case SSML_TAGS.MSTTS_EXPRESS_AS:
|
|
1447
|
-
addAttribute(attributes, SSML_ATTRS.STYLE, element.style);
|
|
1448
|
-
addAttribute(attributes, SSML_ATTRS.STYLE_DEGREE, element.styleDegree);
|
|
1449
|
-
addAttribute(attributes, SSML_ATTRS.ROLE, element.role);
|
|
1450
|
-
break;
|
|
1451
|
-
case SSML_TAGS.SAY_AS:
|
|
1452
|
-
case SSML_TAGS.SAY_AS_CAMEL:
|
|
1453
|
-
addAttribute(attributes, SSML_ATTRS.INTERPRET_AS, element.interpretAs);
|
|
1454
|
-
addAttribute(attributes, SSML_ATTRS.FORMAT, element.format);
|
|
1455
|
-
addAttribute(attributes, SSML_ATTRS.DETAIL, element.detail);
|
|
1456
|
-
break;
|
|
1457
|
-
case SSML_TAGS.PHONEME:
|
|
1458
|
-
addAttribute(attributes, SSML_ATTRS.ALPHABET, element.alphabet);
|
|
1459
|
-
addAttribute(attributes, SSML_ATTRS.PH, element.ph);
|
|
1460
|
-
break;
|
|
1461
|
-
case SSML_TAGS.EMPHASIS:
|
|
1462
|
-
addAttribute(attributes, SSML_ATTRS.LEVEL, element.level);
|
|
1463
|
-
break;
|
|
1464
|
-
case SSML_TAGS.AUDIO:
|
|
1465
|
-
addAttribute(attributes, SSML_ATTRS.SRC, element.src);
|
|
1466
|
-
addAttribute(attributes, SSML_ATTRS.DESC, element.desc);
|
|
1467
|
-
addAttribute(attributes, SSML_ATTRS.CLIP_BEGIN, element.clipBegin);
|
|
1468
|
-
addAttribute(attributes, SSML_ATTRS.CLIP_END, element.clipEnd);
|
|
1469
|
-
addAttribute(attributes, SSML_ATTRS.SPEED, element.speed);
|
|
1470
|
-
addAttribute(attributes, SSML_ATTRS.REPEAT_COUNT, element.repeatCount);
|
|
1471
|
-
addAttribute(attributes, SSML_ATTRS.REPEAT_DURATION, element.repeatDuration);
|
|
1472
|
-
addAttribute(attributes, SSML_ATTRS.SOUND_LEVEL, element.soundLevel);
|
|
1473
|
-
break;
|
|
1474
|
-
case SSML_TAGS.SUB:
|
|
1475
|
-
addAttribute(attributes, SSML_ATTRS.ALIAS, element.alias);
|
|
1476
|
-
break;
|
|
1477
|
-
case SSML_TAGS.LANG:
|
|
1478
|
-
addAttribute(attributes, SSML_ATTRS.XML_LANG, element.lang);
|
|
1479
|
-
break;
|
|
1480
|
-
case SSML_TAGS.MARK:
|
|
1481
|
-
addAttribute(attributes, SSML_ATTRS.NAME, element.name);
|
|
1482
|
-
break;
|
|
1483
|
-
case SSML_TAGS.BOOKMARK:
|
|
1484
|
-
addAttribute(attributes, SSML_ATTRS.MARK, element.mark);
|
|
1485
|
-
break;
|
|
1486
|
-
case SSML_TAGS.LEXICON:
|
|
1487
|
-
addAttribute(attributes, SSML_ATTRS.URI, element.uri);
|
|
1488
|
-
break;
|
|
1489
|
-
case SSML_TAGS.MSTTS_SILENCE:
|
|
1490
|
-
case SSML_TAGS.SILENCE:
|
|
1491
|
-
addAttribute(attributes, SSML_ATTRS.TYPE, element.typeValue ?? element.silenceType);
|
|
1492
|
-
addAttribute(attributes, SSML_ATTRS.VALUE, element.value);
|
|
1493
|
-
break;
|
|
1494
|
-
case SSML_TAGS.MSTTS_VISEME:
|
|
1495
|
-
case SSML_TAGS.VISEME:
|
|
1496
|
-
addAttribute(attributes, SSML_ATTRS.TYPE, element.typeValue ?? element.visemeType);
|
|
1497
|
-
break;
|
|
1498
|
-
case SSML_TAGS.MSTTS_AUDIO_DURATION:
|
|
1499
|
-
addAttribute(attributes, SSML_ATTRS.VALUE, element.value);
|
|
1500
|
-
break;
|
|
1501
|
-
case SSML_TAGS.MSTTS_TURN:
|
|
1502
|
-
addAttribute(attributes, SSML_ATTRS.VOICE, element.voice);
|
|
1503
|
-
addAttribute(attributes, SSML_ATTRS.SPEAKER, element.speaker);
|
|
1504
|
-
break;
|
|
1505
|
-
case SSML_TAGS.MSTTS_BACKGROUND_AUDIO:
|
|
1506
|
-
addAttribute(attributes, SSML_ATTRS.SRC, element.src);
|
|
1507
|
-
addAttribute(attributes, SSML_ATTRS.VOLUME, element.volume);
|
|
1508
|
-
addAttribute(attributes, SSML_ATTRS.FADE_IN, element.fadeIn ?? element.fadein);
|
|
1509
|
-
addAttribute(attributes, SSML_ATTRS.FADE_OUT, element.fadeOut ?? element.fadeout);
|
|
1510
|
-
break;
|
|
1511
|
-
case SSML_TAGS.MSTTS_DIALOG:
|
|
1512
|
-
break;
|
|
1513
|
-
case SSML_TAGS.MSTTS_TTS_EMBEDDING:
|
|
1514
|
-
addAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID, element.speakerProfileId);
|
|
1515
|
-
break;
|
|
1516
|
-
case SSML_TAGS.MSTTS_EMBEDDING:
|
|
1517
|
-
addAttribute(attributes, SSML_ATTRS.ID, element.id);
|
|
1518
|
-
addAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID, element.speakerProfileId);
|
|
1519
|
-
break;
|
|
1520
|
-
case SSML_TAGS.MSTTS_VOICE_CONVERSION:
|
|
1521
|
-
addAttribute(attributes, SSML_ATTRS.URL, element.url);
|
|
1522
|
-
addAttribute(attributes, SSML_ATTRS.PROFILE, element.profile);
|
|
1523
|
-
addAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID, element.speakerProfileId);
|
|
1524
|
-
break;
|
|
1525
|
-
case SSML_TAGS.PARAGRAPH:
|
|
1526
|
-
case SSML_TAGS.SENTENCE:
|
|
1527
|
-
case SSML_TAGS.WORD:
|
|
1528
|
-
case "element":
|
|
1529
|
-
case "custom":
|
|
1530
|
-
break;
|
|
1531
|
-
}
|
|
1532
|
-
return attributes;
|
|
1533
|
-
}
|
|
1534
|
-
function getTagName(element) {
|
|
1535
|
-
switch (element.type) {
|
|
1536
|
-
case SSML_TAGS.EXPRESS_AS:
|
|
1537
|
-
case SSML_TAGS.EXPRESS_AS_CAMEL:
|
|
1538
|
-
case SSML_TAGS.MSTTS_EXPRESS_AS:
|
|
1539
|
-
return SSML_TAGS.MSTTS_EXPRESS_AS;
|
|
1540
|
-
case SSML_TAGS.SAY_AS:
|
|
1541
|
-
case SSML_TAGS.SAY_AS_CAMEL:
|
|
1542
|
-
return SSML_TAGS.SAY_AS;
|
|
1543
|
-
case SSML_TAGS.SILENCE:
|
|
1544
|
-
case SSML_TAGS.MSTTS_SILENCE:
|
|
1545
|
-
return SSML_TAGS.MSTTS_SILENCE;
|
|
1546
|
-
case SSML_TAGS.VISEME:
|
|
1547
|
-
case SSML_TAGS.MSTTS_VISEME:
|
|
1548
|
-
return SSML_TAGS.MSTTS_VISEME;
|
|
1549
|
-
case SSML_TAGS.MSTTS_AUDIO_DURATION:
|
|
1550
|
-
return SSML_TAGS.MSTTS_AUDIO_DURATION;
|
|
1551
|
-
case "element":
|
|
1552
|
-
case "custom":
|
|
1553
|
-
return element.name;
|
|
1554
|
-
default:
|
|
1555
|
-
return element.type;
|
|
1556
|
-
}
|
|
1557
|
-
}
|
|
1558
|
-
function getChildren(element) {
|
|
1559
|
-
return element.children ?? [];
|
|
1560
|
-
}
|
|
1561
|
-
function validateName(name, kind) {
|
|
1562
|
-
if (!XML_NAME_PATTERN.test(name)) {
|
|
1563
|
-
throw new Error(`Invalid XML ${kind} name: ${name}`);
|
|
1564
|
-
}
|
|
1565
|
-
}
|
|
1566
|
-
function serializeAttributes(attributes) {
|
|
1567
|
-
return Object.entries(attributes).map(([name, value]) => {
|
|
1568
|
-
validateName(name, "attribute");
|
|
1569
|
-
return ` ${name}="${escapeAttribute(String(value))}"`;
|
|
1570
|
-
}).join("");
|
|
1571
|
-
}
|
|
1572
|
-
function serializeNode(node) {
|
|
1573
|
-
if (typeof node === "string") {
|
|
1574
|
-
return escapeText(node);
|
|
1575
|
-
}
|
|
1576
|
-
if (node.type === "text") {
|
|
1577
|
-
return escapeText(node.value);
|
|
1578
|
-
}
|
|
1579
|
-
const tagName = getTagName(node);
|
|
1580
|
-
validateName(tagName, "element");
|
|
1581
|
-
const attributes = serializeAttributes(getAttributes(node));
|
|
1582
|
-
const children = getChildren(node);
|
|
1583
|
-
if (children.length === 0) {
|
|
1584
|
-
return `<${tagName}${attributes}/>`;
|
|
1585
|
-
}
|
|
1586
|
-
return `<${tagName}${attributes}>${children.map(serializeNode).join("")}</${tagName}>`;
|
|
1587
|
-
}
|
|
1588
|
-
function usesMsttsNamespace(nodes) {
|
|
1589
|
-
return nodes.some((node) => {
|
|
1590
|
-
if (typeof node === "string" || node.type === "text") {
|
|
1591
|
-
return false;
|
|
1592
|
-
}
|
|
1593
|
-
const tagName = getTagName(node);
|
|
1594
|
-
return tagName.startsWith(MSTTS_TAG_PREFIX) || usesMsttsNamespace(getChildren(node));
|
|
1595
|
-
});
|
|
1596
|
-
}
|
|
1597
|
-
function serializeDocument(document) {
|
|
1598
|
-
const children = document.children ?? (document.content === void 0 ? [] : [document.content]);
|
|
1599
|
-
const attributes = {
|
|
1600
|
-
...document.attributes ?? {},
|
|
1601
|
-
[SSML_ATTRS.VERSION]: document.version,
|
|
1602
|
-
[SSML_ATTRS.XMLNS]: SYNTHESIS_NAMESPACE,
|
|
1603
|
-
[SSML_ATTRS.XML_LANG]: document.lang
|
|
1604
|
-
};
|
|
1605
|
-
if (usesMsttsNamespace(children) && attributes[SSML_ATTRS.MSTTS_XMLNS] === void 0) {
|
|
1606
|
-
attributes[SSML_ATTRS.MSTTS_XMLNS] = MSTTS_NAMESPACE;
|
|
1607
|
-
}
|
|
1608
|
-
return `<${SSML_TAGS.SPEAK}${serializeAttributes(attributes)}>${children.map(serializeNode).join("")}</${SSML_TAGS.SPEAK}>`;
|
|
1609
|
-
}
|
|
1610
|
-
function buildSsml(documentOrContent, lang = DEFAULT_SSML_LANGUAGE) {
|
|
1611
|
-
if (typeof documentOrContent === "string") {
|
|
1612
|
-
return {
|
|
1613
|
-
version: DEFAULT_SSML_VERSION,
|
|
1614
|
-
lang,
|
|
1615
|
-
content: documentOrContent
|
|
1616
|
-
};
|
|
1617
|
-
}
|
|
1618
|
-
return serializeDocument(documentOrContent);
|
|
1619
|
-
}
|
|
1620
|
-
var XML_ENTITIES = {
|
|
1621
|
-
amp: "&",
|
|
1622
|
-
apos: "'",
|
|
1623
|
-
gt: ">",
|
|
1624
|
-
lt: "<",
|
|
1625
|
-
quot: '"'
|
|
1626
|
-
};
|
|
1627
|
-
function hasOwn(object, property) {
|
|
1628
|
-
return Object.getOwnPropertyDescriptor(object, property) !== void 0;
|
|
1629
|
-
}
|
|
1630
|
-
function setAttribute(attributes, name, value) {
|
|
1631
|
-
Object.defineProperty(attributes, name, {
|
|
1632
|
-
configurable: true,
|
|
1633
|
-
enumerable: true,
|
|
1634
|
-
value,
|
|
1635
|
-
writable: true
|
|
1636
|
-
});
|
|
1637
|
-
}
|
|
1638
|
-
function decodeEntity(entity) {
|
|
1639
|
-
const namedValue = hasOwn(XML_ENTITIES, entity) ? XML_ENTITIES[entity] : void 0;
|
|
1640
|
-
if (namedValue !== void 0) {
|
|
1641
|
-
return namedValue;
|
|
1642
|
-
}
|
|
1643
|
-
const isHexadecimal = entity.startsWith("#x") || entity.startsWith("#X");
|
|
1644
|
-
const isDecimal = entity.startsWith("#");
|
|
1645
|
-
if (!isHexadecimal && !isDecimal) {
|
|
1646
|
-
throw new Error(`Unknown XML entity: &${entity};`);
|
|
1647
|
-
}
|
|
1648
|
-
const digits = entity.slice(isHexadecimal ? 2 : 1);
|
|
1649
|
-
const codePoint = Number.parseInt(digits, isHexadecimal ? 16 : 10);
|
|
1650
|
-
if (!digits || !Number.isInteger(codePoint) || codePoint < 0 || codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343 || codePoint < 32 && ![9, 10, 13].includes(codePoint)) {
|
|
1651
|
-
throw new Error(`Invalid XML character reference: &${entity};`);
|
|
1652
|
-
}
|
|
1653
|
-
return String.fromCodePoint(codePoint);
|
|
1654
|
-
}
|
|
1655
|
-
function decodeXmlEntities(value) {
|
|
1656
|
-
let result = "";
|
|
1657
|
-
let start = 0;
|
|
1658
|
-
while (true) {
|
|
1659
|
-
const ampersand = value.indexOf("&", start);
|
|
1660
|
-
if (ampersand === -1) {
|
|
1661
|
-
return result + value.slice(start);
|
|
1662
|
-
}
|
|
1663
|
-
result += value.slice(start, ampersand);
|
|
1664
|
-
const semicolon = value.indexOf(";", ampersand + 1);
|
|
1665
|
-
if (semicolon === -1) {
|
|
1666
|
-
throw new Error("Unterminated XML entity reference");
|
|
1667
|
-
}
|
|
1668
|
-
result += decodeEntity(value.slice(ampersand + 1, semicolon));
|
|
1669
|
-
start = semicolon + 1;
|
|
1670
|
-
}
|
|
1671
|
-
}
|
|
1672
|
-
function isXmlNameStart(value) {
|
|
1673
|
-
return value !== void 0 && /[A-Za-z_]/.test(value);
|
|
1674
|
-
}
|
|
1675
|
-
function isXmlNameCharacter(value) {
|
|
1676
|
-
return value !== void 0 && /[A-Za-z0-9_.:-]/.test(value);
|
|
1677
|
-
}
|
|
1678
|
-
function isXmlWhitespace(value) {
|
|
1679
|
-
return value === " " || value === " " || value === "\r" || value === "\n";
|
|
1680
|
-
}
|
|
1681
|
-
function removeStandardNamespaceAttributes(attributes) {
|
|
1682
|
-
if (attributes[SSML_ATTRS.XMLNS] === SYNTHESIS_NAMESPACE) {
|
|
1683
|
-
delete attributes[SSML_ATTRS.XMLNS];
|
|
1684
|
-
}
|
|
1685
|
-
if (attributes[SSML_ATTRS.MSTTS_XMLNS] === MSTTS_NAMESPACE) {
|
|
1686
|
-
delete attributes[SSML_ATTRS.MSTTS_XMLNS];
|
|
1687
|
-
}
|
|
1688
|
-
}
|
|
1689
|
-
var _index;
|
|
1690
|
-
var XmlParser = class {
|
|
1691
|
-
constructor(source) {
|
|
1692
|
-
__privateAdd(this, _index, 0);
|
|
1693
|
-
this.source = source;
|
|
1694
|
-
}
|
|
1695
|
-
parse() {
|
|
1696
|
-
if (this.source.charCodeAt(0) === 65279) {
|
|
1697
|
-
__privateSet(this, _index, __privateGet(this, _index) + 1);
|
|
1698
|
-
}
|
|
1699
|
-
this.skipMisc();
|
|
1700
|
-
if (__privateGet(this, _index) >= this.source.length) {
|
|
1701
|
-
this.fail("SSML input is empty");
|
|
1702
|
-
}
|
|
1703
|
-
if (this.source[__privateGet(this, _index)] !== "<") {
|
|
1704
|
-
this.fail("SSML input must start with an XML element");
|
|
1705
|
-
}
|
|
1706
|
-
const root = this.parseElement(0);
|
|
1707
|
-
this.skipMisc();
|
|
1708
|
-
if (__privateGet(this, _index) !== this.source.length) {
|
|
1709
|
-
this.fail("Unexpected content after the root XML element");
|
|
1710
|
-
}
|
|
1711
|
-
return root;
|
|
1712
|
-
}
|
|
1713
|
-
parseElement(depth) {
|
|
1714
|
-
if (depth > MAX_NESTING_DEPTH) {
|
|
1715
|
-
this.fail("XML nesting depth exceeds the supported limit");
|
|
1716
|
-
}
|
|
1717
|
-
this.expect("<");
|
|
1718
|
-
if (this.source[__privateGet(this, _index)] === "/") {
|
|
1719
|
-
this.fail("Unexpected closing XML element");
|
|
1720
|
-
}
|
|
1721
|
-
const name = this.parseName();
|
|
1722
|
-
const { attributes, selfClosing } = this.parseStartTag();
|
|
1723
|
-
if (selfClosing) {
|
|
1724
|
-
return { name, attributes, children: [] };
|
|
1725
|
-
}
|
|
1726
|
-
const children = [];
|
|
1727
|
-
while (__privateGet(this, _index) < this.source.length) {
|
|
1728
|
-
if (this.source.startsWith("</", __privateGet(this, _index))) {
|
|
1729
|
-
__privateSet(this, _index, __privateGet(this, _index) + 2);
|
|
1730
|
-
const closingName = this.parseName();
|
|
1731
|
-
this.skipWhitespace();
|
|
1732
|
-
this.expect(">");
|
|
1733
|
-
if (closingName !== name) {
|
|
1734
|
-
this.fail(`Mismatched closing element: expected </${name}> but found </${closingName}>`);
|
|
1735
|
-
}
|
|
1736
|
-
return { name, attributes, children };
|
|
1737
|
-
}
|
|
1738
|
-
if (this.source.startsWith("<!--", __privateGet(this, _index))) {
|
|
1739
|
-
this.skipComment();
|
|
1740
|
-
continue;
|
|
1741
|
-
}
|
|
1742
|
-
if (this.source.startsWith("<![CDATA[", __privateGet(this, _index))) {
|
|
1743
|
-
this.appendText(children, this.parseCdata());
|
|
1744
|
-
continue;
|
|
1745
|
-
}
|
|
1746
|
-
if (this.source.startsWith("<?", __privateGet(this, _index))) {
|
|
1747
|
-
this.skipProcessingInstruction();
|
|
1748
|
-
continue;
|
|
1749
|
-
}
|
|
1750
|
-
if (this.source.startsWith("<!", __privateGet(this, _index))) {
|
|
1751
|
-
this.fail("Unsupported XML declaration inside an element");
|
|
1752
|
-
}
|
|
1753
|
-
if (this.source[__privateGet(this, _index)] === "<") {
|
|
1754
|
-
children.push(this.parseElement(depth + 1));
|
|
1755
|
-
} else {
|
|
1756
|
-
this.appendText(children, this.parseText());
|
|
1757
|
-
}
|
|
1758
|
-
}
|
|
1759
|
-
this.fail(`Unclosed XML element: <${name}>`);
|
|
1760
|
-
}
|
|
1761
|
-
parseStartTag() {
|
|
1762
|
-
const attributes = {};
|
|
1763
|
-
while (__privateGet(this, _index) < this.source.length) {
|
|
1764
|
-
this.skipWhitespace();
|
|
1765
|
-
if (this.source.startsWith("/>", __privateGet(this, _index))) {
|
|
1766
|
-
__privateSet(this, _index, __privateGet(this, _index) + 2);
|
|
1767
|
-
return { attributes, selfClosing: true };
|
|
1768
|
-
}
|
|
1769
|
-
if (this.source[__privateGet(this, _index)] === ">") {
|
|
1770
|
-
__privateSet(this, _index, __privateGet(this, _index) + 1);
|
|
1771
|
-
return { attributes, selfClosing: false };
|
|
1772
|
-
}
|
|
1773
|
-
const name = this.parseName();
|
|
1774
|
-
this.skipWhitespace();
|
|
1775
|
-
this.expect("=");
|
|
1776
|
-
this.skipWhitespace();
|
|
1777
|
-
const quote = this.source[__privateGet(this, _index)];
|
|
1778
|
-
if (quote !== '"' && quote !== "'") {
|
|
1779
|
-
this.fail(`XML attribute ${name} must use a quoted value`);
|
|
1780
|
-
}
|
|
1781
|
-
__privateSet(this, _index, __privateGet(this, _index) + 1);
|
|
1782
|
-
const valueStart = __privateGet(this, _index);
|
|
1783
|
-
while (__privateGet(this, _index) < this.source.length && this.source[__privateGet(this, _index)] !== quote) {
|
|
1784
|
-
if (this.source[__privateGet(this, _index)] === "<") {
|
|
1785
|
-
this.fail(`Invalid "<" in XML attribute ${name}`);
|
|
1786
|
-
}
|
|
1787
|
-
__privateSet(this, _index, __privateGet(this, _index) + 1);
|
|
1788
|
-
}
|
|
1789
|
-
if (__privateGet(this, _index) >= this.source.length) {
|
|
1790
|
-
this.fail(`Unclosed XML attribute ${name}`);
|
|
1791
|
-
}
|
|
1792
|
-
const value = decodeXmlEntities(this.source.slice(valueStart, __privateGet(this, _index)));
|
|
1793
|
-
__privateSet(this, _index, __privateGet(this, _index) + 1);
|
|
1794
|
-
if (hasOwn(attributes, name)) {
|
|
1795
|
-
this.fail(`Duplicate XML attribute: ${name}`);
|
|
1796
|
-
}
|
|
1797
|
-
setAttribute(attributes, name, value);
|
|
1798
|
-
}
|
|
1799
|
-
this.fail("Unclosed XML start tag");
|
|
1800
|
-
}
|
|
1801
|
-
parseText() {
|
|
1802
|
-
const start = __privateGet(this, _index);
|
|
1803
|
-
while (__privateGet(this, _index) < this.source.length && this.source[__privateGet(this, _index)] !== "<") {
|
|
1804
|
-
__privateSet(this, _index, __privateGet(this, _index) + 1);
|
|
1805
|
-
}
|
|
1806
|
-
const value = this.source.slice(start, __privateGet(this, _index));
|
|
1807
|
-
if (value.includes("]]>")) {
|
|
1808
|
-
this.fail("CDATA termination is not valid in ordinary XML text");
|
|
1809
|
-
}
|
|
1810
|
-
return decodeXmlEntities(value);
|
|
1811
|
-
}
|
|
1812
|
-
parseCdata() {
|
|
1813
|
-
__privateSet(this, _index, __privateGet(this, _index) + "<![CDATA[".length);
|
|
1814
|
-
const end = this.source.indexOf("]]>", __privateGet(this, _index));
|
|
1815
|
-
if (end === -1) {
|
|
1816
|
-
this.fail("Unclosed XML CDATA section");
|
|
1817
|
-
}
|
|
1818
|
-
const value = this.source.slice(__privateGet(this, _index), end);
|
|
1819
|
-
__privateSet(this, _index, end + 3);
|
|
1820
|
-
return value;
|
|
1821
|
-
}
|
|
1822
|
-
skipComment() {
|
|
1823
|
-
__privateSet(this, _index, __privateGet(this, _index) + "<!--".length);
|
|
1824
|
-
const end = this.source.indexOf("-->", __privateGet(this, _index));
|
|
1825
|
-
if (end === -1) {
|
|
1826
|
-
this.fail("Unclosed XML comment");
|
|
1827
|
-
}
|
|
1828
|
-
if (this.source.slice(__privateGet(this, _index), end).includes("--")) {
|
|
1829
|
-
this.fail("XML comments cannot contain consecutive hyphens");
|
|
1830
|
-
}
|
|
1831
|
-
__privateSet(this, _index, end + 3);
|
|
1832
|
-
}
|
|
1833
|
-
skipProcessingInstruction() {
|
|
1834
|
-
__privateSet(this, _index, __privateGet(this, _index) + "<?".length);
|
|
1835
|
-
this.parseName();
|
|
1836
|
-
const end = this.source.indexOf("?>", __privateGet(this, _index));
|
|
1837
|
-
if (end === -1) {
|
|
1838
|
-
this.fail("Unclosed XML processing instruction");
|
|
1839
|
-
}
|
|
1840
|
-
__privateSet(this, _index, end + 2);
|
|
1841
|
-
}
|
|
1842
|
-
skipMisc() {
|
|
1843
|
-
while (__privateGet(this, _index) < this.source.length) {
|
|
1844
|
-
this.skipWhitespace();
|
|
1845
|
-
if (this.source.startsWith("<!--", __privateGet(this, _index))) {
|
|
1846
|
-
this.skipComment();
|
|
1847
|
-
continue;
|
|
1848
|
-
}
|
|
1849
|
-
if (this.source.startsWith("<?", __privateGet(this, _index))) {
|
|
1850
|
-
this.skipProcessingInstruction();
|
|
1851
|
-
continue;
|
|
1852
|
-
}
|
|
1853
|
-
if (this.source.startsWith("<!DOCTYPE", __privateGet(this, _index))) {
|
|
1854
|
-
this.fail("DOCTYPE declarations are not supported");
|
|
1855
|
-
}
|
|
1856
|
-
break;
|
|
1857
|
-
}
|
|
1858
|
-
}
|
|
1859
|
-
parseName() {
|
|
1860
|
-
const first = this.source[__privateGet(this, _index)];
|
|
1861
|
-
if (!isXmlNameStart(first)) {
|
|
1862
|
-
this.fail("Invalid XML name");
|
|
1863
|
-
}
|
|
1864
|
-
const start = __privateGet(this, _index);
|
|
1865
|
-
__privateSet(this, _index, __privateGet(this, _index) + 1);
|
|
1866
|
-
while (isXmlNameCharacter(this.source[__privateGet(this, _index)])) {
|
|
1867
|
-
__privateSet(this, _index, __privateGet(this, _index) + 1);
|
|
1868
|
-
}
|
|
1869
|
-
return this.source.slice(start, __privateGet(this, _index));
|
|
1870
|
-
}
|
|
1871
|
-
appendText(children, value) {
|
|
1872
|
-
if (!value) {
|
|
1873
|
-
return;
|
|
1874
|
-
}
|
|
1875
|
-
const previous = children[children.length - 1];
|
|
1876
|
-
if (typeof previous === "string") {
|
|
1877
|
-
children[children.length - 1] = previous + value;
|
|
1878
|
-
} else {
|
|
1879
|
-
children.push(value);
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
skipWhitespace() {
|
|
1883
|
-
while (isXmlWhitespace(this.source[__privateGet(this, _index)])) {
|
|
1884
|
-
__privateSet(this, _index, __privateGet(this, _index) + 1);
|
|
1885
|
-
}
|
|
1886
|
-
}
|
|
1887
|
-
expect(value) {
|
|
1888
|
-
if (!this.source.startsWith(value, __privateGet(this, _index))) {
|
|
1889
|
-
this.fail(`Expected "${value}"`);
|
|
1890
|
-
}
|
|
1891
|
-
__privateSet(this, _index, __privateGet(this, _index) + value.length);
|
|
1892
|
-
}
|
|
1893
|
-
fail(message) {
|
|
1894
|
-
throw new Error(`${message} at position ${__privateGet(this, _index)}`);
|
|
1895
|
-
}
|
|
1896
|
-
};
|
|
1897
|
-
_index = /* @__PURE__ */ new WeakMap();
|
|
1898
|
-
function readAttribute(attributes, ...names) {
|
|
1899
|
-
let found = false;
|
|
1900
|
-
let value;
|
|
1901
|
-
for (const name of names) {
|
|
1902
|
-
if (hasOwn(attributes, name)) {
|
|
1903
|
-
if (!found) {
|
|
1904
|
-
value = String(attributes[name]);
|
|
1905
|
-
found = true;
|
|
1906
|
-
}
|
|
1907
|
-
delete attributes[name];
|
|
1908
|
-
}
|
|
1909
|
-
}
|
|
1910
|
-
return value;
|
|
1911
|
-
}
|
|
1912
|
-
function getElementAttributes(node) {
|
|
1913
|
-
const attributes = { ...node.attributes };
|
|
1914
|
-
removeStandardNamespaceAttributes(attributes);
|
|
1915
|
-
return attributes;
|
|
1916
|
-
}
|
|
1917
|
-
function finishElement(element, node, attributes) {
|
|
1918
|
-
if (node.children.length > 0) {
|
|
1919
|
-
element.children = node.children.map(convertNode);
|
|
1920
|
-
}
|
|
1921
|
-
if (Object.keys(attributes).length > 0) {
|
|
1922
|
-
element.attributes = attributes;
|
|
1923
|
-
}
|
|
1924
|
-
return element;
|
|
1925
|
-
}
|
|
1926
|
-
function convertElement(node) {
|
|
1927
|
-
const attributes = getElementAttributes(node);
|
|
1928
|
-
switch (node.name) {
|
|
1929
|
-
case SSML_TAGS.VOICE: {
|
|
1930
|
-
const element = { type: SSML_TAGS.VOICE };
|
|
1931
|
-
const name = readAttribute(attributes, SSML_ATTRS.NAME);
|
|
1932
|
-
const effect = readAttribute(attributes, SSML_ATTRS.EFFECT);
|
|
1933
|
-
if (name !== void 0) element.name = name;
|
|
1934
|
-
if (effect !== void 0) element.effect = effect;
|
|
1935
|
-
return finishElement(element, node, attributes);
|
|
1936
|
-
}
|
|
1937
|
-
case SSML_TAGS.PROSODY: {
|
|
1938
|
-
const element = { type: SSML_TAGS.PROSODY };
|
|
1939
|
-
const rate = readAttribute(attributes, SSML_ATTRS.RATE);
|
|
1940
|
-
const pitch = readAttribute(attributes, SSML_ATTRS.PITCH);
|
|
1941
|
-
const volume = readAttribute(attributes, SSML_ATTRS.VOLUME);
|
|
1942
|
-
const contour = readAttribute(attributes, SSML_ATTRS.CONTOUR);
|
|
1943
|
-
const range = readAttribute(attributes, SSML_ATTRS.RANGE);
|
|
1944
|
-
if (rate !== void 0) element.rate = rate;
|
|
1945
|
-
if (pitch !== void 0) element.pitch = pitch;
|
|
1946
|
-
if (volume !== void 0) element.volume = volume;
|
|
1947
|
-
if (contour !== void 0) element.contour = contour;
|
|
1948
|
-
if (range !== void 0) element.range = range;
|
|
1949
|
-
return finishElement(element, node, attributes);
|
|
1950
|
-
}
|
|
1951
|
-
case SSML_TAGS.BREAK: {
|
|
1952
|
-
const element = { type: SSML_TAGS.BREAK };
|
|
1953
|
-
const time = readAttribute(attributes, SSML_ATTRS.TIME);
|
|
1954
|
-
const strength = readAttribute(attributes, SSML_ATTRS.STRENGTH);
|
|
1955
|
-
if (time !== void 0) element.time = time;
|
|
1956
|
-
if (strength !== void 0) element.strength = strength;
|
|
1957
|
-
return finishElement(element, node, attributes);
|
|
1958
|
-
}
|
|
1959
|
-
case SSML_TAGS.EXPRESS_AS:
|
|
1960
|
-
case SSML_TAGS.EXPRESS_AS_CAMEL:
|
|
1961
|
-
case SSML_TAGS.MSTTS_EXPRESS_AS: {
|
|
1962
|
-
const element = { type: node.name };
|
|
1963
|
-
const style = readAttribute(attributes, SSML_ATTRS.STYLE);
|
|
1964
|
-
const styleDegree = readAttribute(
|
|
1965
|
-
attributes,
|
|
1966
|
-
SSML_ATTRS.STYLE_DEGREE,
|
|
1967
|
-
SSML_ATTRS.STYLE_DEGREE_CAMEL,
|
|
1968
|
-
SSML_ATTRS.STYLE_DEGREE_HYPHEN
|
|
1969
|
-
);
|
|
1970
|
-
const role = readAttribute(attributes, SSML_ATTRS.ROLE);
|
|
1971
|
-
if (style !== void 0) element.style = style;
|
|
1972
|
-
if (styleDegree !== void 0) element.styleDegree = styleDegree;
|
|
1973
|
-
if (role !== void 0) element.role = role;
|
|
1974
|
-
return finishElement(element, node, attributes);
|
|
1975
|
-
}
|
|
1976
|
-
case SSML_TAGS.SAY_AS:
|
|
1977
|
-
case SSML_TAGS.SAY_AS_CAMEL: {
|
|
1978
|
-
const element = { type: node.name };
|
|
1979
|
-
const interpretAs = readAttribute(attributes, SSML_ATTRS.INTERPRET_AS);
|
|
1980
|
-
const format = readAttribute(attributes, SSML_ATTRS.FORMAT);
|
|
1981
|
-
const detail = readAttribute(attributes, SSML_ATTRS.DETAIL);
|
|
1982
|
-
if (interpretAs !== void 0) element.interpretAs = interpretAs;
|
|
1983
|
-
if (format !== void 0) element.format = format;
|
|
1984
|
-
if (detail !== void 0) element.detail = detail;
|
|
1985
|
-
return finishElement(element, node, attributes);
|
|
1986
|
-
}
|
|
1987
|
-
case SSML_TAGS.PHONEME: {
|
|
1988
|
-
const element = { type: SSML_TAGS.PHONEME };
|
|
1989
|
-
const alphabet = readAttribute(attributes, SSML_ATTRS.ALPHABET);
|
|
1990
|
-
const ph = readAttribute(attributes, SSML_ATTRS.PH);
|
|
1991
|
-
if (alphabet !== void 0) element.alphabet = alphabet;
|
|
1992
|
-
if (ph !== void 0) element.ph = ph;
|
|
1993
|
-
return finishElement(element, node, attributes);
|
|
1994
|
-
}
|
|
1995
|
-
case SSML_TAGS.EMPHASIS: {
|
|
1996
|
-
const element = { type: SSML_TAGS.EMPHASIS };
|
|
1997
|
-
const level = readAttribute(attributes, SSML_ATTRS.LEVEL);
|
|
1998
|
-
if (level !== void 0) element.level = level;
|
|
1999
|
-
return finishElement(element, node, attributes);
|
|
2000
|
-
}
|
|
2001
|
-
case SSML_TAGS.AUDIO: {
|
|
2002
|
-
const element = { type: SSML_TAGS.AUDIO };
|
|
2003
|
-
const src = readAttribute(attributes, SSML_ATTRS.SRC);
|
|
2004
|
-
const desc = readAttribute(attributes, SSML_ATTRS.DESC);
|
|
2005
|
-
const clipBegin = readAttribute(attributes, SSML_ATTRS.CLIP_BEGIN);
|
|
2006
|
-
const clipEnd = readAttribute(attributes, SSML_ATTRS.CLIP_END);
|
|
2007
|
-
const speed = readAttribute(attributes, SSML_ATTRS.SPEED);
|
|
2008
|
-
const repeatCount = readAttribute(attributes, SSML_ATTRS.REPEAT_COUNT);
|
|
2009
|
-
const repeatDuration = readAttribute(attributes, SSML_ATTRS.REPEAT_DURATION);
|
|
2010
|
-
const soundLevel = readAttribute(attributes, SSML_ATTRS.SOUND_LEVEL);
|
|
2011
|
-
if (src !== void 0) element.src = src;
|
|
2012
|
-
if (desc !== void 0) element.desc = desc;
|
|
2013
|
-
if (clipBegin !== void 0) element.clipBegin = clipBegin;
|
|
2014
|
-
if (clipEnd !== void 0) element.clipEnd = clipEnd;
|
|
2015
|
-
if (speed !== void 0) element.speed = speed;
|
|
2016
|
-
if (repeatCount !== void 0) element.repeatCount = repeatCount;
|
|
2017
|
-
if (repeatDuration !== void 0) element.repeatDuration = repeatDuration;
|
|
2018
|
-
if (soundLevel !== void 0) element.soundLevel = soundLevel;
|
|
2019
|
-
return finishElement(element, node, attributes);
|
|
2020
|
-
}
|
|
2021
|
-
case SSML_TAGS.SUB: {
|
|
2022
|
-
const element = { type: SSML_TAGS.SUB };
|
|
2023
|
-
const alias = readAttribute(attributes, SSML_ATTRS.ALIAS);
|
|
2024
|
-
if (alias !== void 0) element.alias = alias;
|
|
2025
|
-
return finishElement(element, node, attributes);
|
|
2026
|
-
}
|
|
2027
|
-
case SSML_TAGS.LANG: {
|
|
2028
|
-
const element = { type: SSML_TAGS.LANG };
|
|
2029
|
-
const lang = readAttribute(attributes, SSML_ATTRS.XML_LANG, SSML_ATTRS.LANG);
|
|
2030
|
-
if (lang !== void 0) element.lang = lang;
|
|
2031
|
-
return finishElement(element, node, attributes);
|
|
2032
|
-
}
|
|
2033
|
-
case SSML_TAGS.MARK: {
|
|
2034
|
-
const element = { type: SSML_TAGS.MARK };
|
|
2035
|
-
const name = readAttribute(attributes, SSML_ATTRS.NAME);
|
|
2036
|
-
if (name !== void 0) element.name = name;
|
|
2037
|
-
return finishElement(element, node, attributes);
|
|
2038
|
-
}
|
|
2039
|
-
case SSML_TAGS.BOOKMARK: {
|
|
2040
|
-
const element = { type: SSML_TAGS.BOOKMARK };
|
|
2041
|
-
const mark = readAttribute(attributes, SSML_ATTRS.MARK);
|
|
2042
|
-
if (mark !== void 0) element.mark = mark;
|
|
2043
|
-
return finishElement(element, node, attributes);
|
|
2044
|
-
}
|
|
2045
|
-
case SSML_TAGS.LEXICON: {
|
|
2046
|
-
const element = { type: SSML_TAGS.LEXICON };
|
|
2047
|
-
const uri = readAttribute(attributes, SSML_ATTRS.URI);
|
|
2048
|
-
if (uri !== void 0) element.uri = uri;
|
|
2049
|
-
return finishElement(element, node, attributes);
|
|
2050
|
-
}
|
|
2051
|
-
case SSML_TAGS.PARAGRAPH: {
|
|
2052
|
-
const element = { type: SSML_TAGS.PARAGRAPH };
|
|
2053
|
-
return finishElement(element, node, attributes);
|
|
2054
|
-
}
|
|
2055
|
-
case SSML_TAGS.SENTENCE: {
|
|
2056
|
-
const element = { type: SSML_TAGS.SENTENCE };
|
|
2057
|
-
return finishElement(element, node, attributes);
|
|
2058
|
-
}
|
|
2059
|
-
case SSML_TAGS.WORD: {
|
|
2060
|
-
const element = { type: SSML_TAGS.WORD };
|
|
2061
|
-
return finishElement(element, node, attributes);
|
|
2062
|
-
}
|
|
2063
|
-
case SSML_TAGS.MSTTS_SILENCE:
|
|
2064
|
-
case SSML_TAGS.SILENCE: {
|
|
2065
|
-
const element = {
|
|
2066
|
-
type: node.name === SSML_TAGS.MSTTS_SILENCE ? SSML_TAGS.MSTTS_SILENCE : SSML_TAGS.SILENCE
|
|
2067
|
-
};
|
|
2068
|
-
const typeValue = readAttribute(attributes, SSML_ATTRS.TYPE);
|
|
2069
|
-
const value = readAttribute(attributes, SSML_ATTRS.VALUE);
|
|
2070
|
-
if (typeValue !== void 0) element.typeValue = typeValue;
|
|
2071
|
-
if (value !== void 0) element.value = value;
|
|
2072
|
-
return finishElement(element, node, attributes);
|
|
2073
|
-
}
|
|
2074
|
-
case SSML_TAGS.MSTTS_VISEME:
|
|
2075
|
-
case SSML_TAGS.VISEME: {
|
|
2076
|
-
const element = {
|
|
2077
|
-
type: node.name === SSML_TAGS.MSTTS_VISEME ? SSML_TAGS.MSTTS_VISEME : SSML_TAGS.VISEME
|
|
2078
|
-
};
|
|
2079
|
-
const typeValue = readAttribute(attributes, SSML_ATTRS.TYPE);
|
|
2080
|
-
if (typeValue !== void 0) element.typeValue = typeValue;
|
|
2081
|
-
return finishElement(element, node, attributes);
|
|
2082
|
-
}
|
|
2083
|
-
case SSML_TAGS.MSTTS_AUDIO_DURATION: {
|
|
2084
|
-
const element = { type: SSML_TAGS.MSTTS_AUDIO_DURATION };
|
|
2085
|
-
const value = readAttribute(attributes, SSML_ATTRS.VALUE);
|
|
2086
|
-
if (value !== void 0) element.value = value;
|
|
2087
|
-
return finishElement(element, node, attributes);
|
|
2088
|
-
}
|
|
2089
|
-
case SSML_TAGS.MSTTS_DIALOG: {
|
|
2090
|
-
const element = { type: SSML_TAGS.MSTTS_DIALOG };
|
|
2091
|
-
return finishElement(element, node, attributes);
|
|
2092
|
-
}
|
|
2093
|
-
case SSML_TAGS.MSTTS_TURN: {
|
|
2094
|
-
const element = { type: SSML_TAGS.MSTTS_TURN };
|
|
2095
|
-
const voice = readAttribute(attributes, SSML_ATTRS.VOICE);
|
|
2096
|
-
const speaker = readAttribute(attributes, SSML_ATTRS.SPEAKER);
|
|
2097
|
-
if (voice !== void 0) element.voice = voice;
|
|
2098
|
-
if (speaker !== void 0) element.speaker = speaker;
|
|
2099
|
-
return finishElement(element, node, attributes);
|
|
2100
|
-
}
|
|
2101
|
-
case SSML_TAGS.MSTTS_BACKGROUND_AUDIO: {
|
|
2102
|
-
const element = { type: SSML_TAGS.MSTTS_BACKGROUND_AUDIO };
|
|
2103
|
-
const src = readAttribute(attributes, SSML_ATTRS.SRC);
|
|
2104
|
-
const volume = readAttribute(attributes, SSML_ATTRS.VOLUME);
|
|
2105
|
-
const fadeIn = readAttribute(attributes, SSML_ATTRS.FADE_IN);
|
|
2106
|
-
const fadeOut = readAttribute(attributes, SSML_ATTRS.FADE_OUT);
|
|
2107
|
-
if (src !== void 0) element.src = src;
|
|
2108
|
-
if (volume !== void 0) element.volume = volume;
|
|
2109
|
-
if (fadeIn !== void 0) element.fadeIn = fadeIn;
|
|
2110
|
-
if (fadeOut !== void 0) element.fadeOut = fadeOut;
|
|
2111
|
-
return finishElement(element, node, attributes);
|
|
2112
|
-
}
|
|
2113
|
-
case SSML_TAGS.MSTTS_TTS_EMBEDDING: {
|
|
2114
|
-
const element = { type: SSML_TAGS.MSTTS_TTS_EMBEDDING };
|
|
2115
|
-
const speakerProfileId = readAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID);
|
|
2116
|
-
if (speakerProfileId !== void 0) element.speakerProfileId = speakerProfileId;
|
|
2117
|
-
return finishElement(element, node, attributes);
|
|
2118
|
-
}
|
|
2119
|
-
case SSML_TAGS.MSTTS_EMBEDDING: {
|
|
2120
|
-
const element = { type: SSML_TAGS.MSTTS_EMBEDDING };
|
|
2121
|
-
const id = readAttribute(attributes, SSML_ATTRS.ID);
|
|
2122
|
-
const speakerProfileId = readAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID);
|
|
2123
|
-
if (id !== void 0) element.id = id;
|
|
2124
|
-
if (speakerProfileId !== void 0) element.speakerProfileId = speakerProfileId;
|
|
2125
|
-
return finishElement(element, node, attributes);
|
|
2126
|
-
}
|
|
2127
|
-
case SSML_TAGS.MSTTS_VOICE_CONVERSION: {
|
|
2128
|
-
const element = { type: SSML_TAGS.MSTTS_VOICE_CONVERSION };
|
|
2129
|
-
const url = readAttribute(attributes, SSML_ATTRS.URL);
|
|
2130
|
-
const profile = readAttribute(attributes, SSML_ATTRS.PROFILE);
|
|
2131
|
-
const speakerProfileId = readAttribute(attributes, SSML_ATTRS.SPEAKER_PROFILE_ID);
|
|
2132
|
-
if (url !== void 0) element.url = url;
|
|
2133
|
-
if (profile !== void 0) element.profile = profile;
|
|
2134
|
-
if (speakerProfileId !== void 0) element.speakerProfileId = speakerProfileId;
|
|
2135
|
-
return finishElement(element, node, attributes);
|
|
2136
|
-
}
|
|
2137
|
-
default: {
|
|
2138
|
-
const element = {
|
|
2139
|
-
name: node.name,
|
|
2140
|
-
type: "custom"
|
|
2141
|
-
};
|
|
2142
|
-
return finishElement(element, node, attributes);
|
|
2143
|
-
}
|
|
2144
|
-
}
|
|
2145
|
-
}
|
|
2146
|
-
function convertNode(node) {
|
|
2147
|
-
return typeof node === "string" ? node : convertElement(node);
|
|
2148
|
-
}
|
|
2149
|
-
function parseSsml(xmlString) {
|
|
2150
|
-
if (typeof xmlString !== "string") {
|
|
2151
|
-
throw new TypeError("SSML input must be a string");
|
|
2152
|
-
}
|
|
2153
|
-
const root = new XmlParser(xmlString).parse();
|
|
2154
|
-
if (root.name !== SSML_TAGS.SPEAK) {
|
|
2155
|
-
throw new Error(`SSML root element must be <${SSML_TAGS.SPEAK}>, found <${root.name}>`);
|
|
2156
|
-
}
|
|
2157
|
-
const attributes = { ...root.attributes };
|
|
2158
|
-
const version = readAttribute(attributes, SSML_ATTRS.VERSION);
|
|
2159
|
-
const lang = readAttribute(attributes, SSML_ATTRS.XML_LANG, SSML_ATTRS.LANG);
|
|
2160
|
-
if (version === void 0) {
|
|
2161
|
-
throw new Error(`SSML <${SSML_TAGS.SPEAK}> element is missing the "${SSML_ATTRS.VERSION}" attribute`);
|
|
2162
|
-
}
|
|
2163
|
-
if (lang === void 0) {
|
|
2164
|
-
throw new Error(`SSML <${SSML_TAGS.SPEAK}> element is missing the "${SSML_ATTRS.XML_LANG}" attribute`);
|
|
2165
|
-
}
|
|
2166
|
-
removeStandardNamespaceAttributes(attributes);
|
|
2167
|
-
const document = {
|
|
2168
|
-
children: root.children.map(convertNode),
|
|
2169
|
-
lang,
|
|
2170
|
-
type: SSML_TAGS.SPEAK,
|
|
2171
|
-
version
|
|
2172
|
-
};
|
|
2173
|
-
if (Object.keys(attributes).length > 0) {
|
|
2174
|
-
document.attributes = attributes;
|
|
2175
|
-
}
|
|
2176
|
-
return document;
|
|
2177
|
-
}
|
|
2178
|
-
function escapeAttribute2(value) {
|
|
2179
|
-
return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">").replace(/'/g, "'");
|
|
2180
|
-
}
|
|
2181
|
-
function getPartialTextNodes(text, version, lang) {
|
|
2182
|
-
if (!text.includes("<")) {
|
|
2183
|
-
return [text];
|
|
2184
|
-
}
|
|
2185
|
-
try {
|
|
2186
|
-
const openingTag = `<${SSML_TAGS.SPEAK} ${SSML_ATTRS.VERSION}="${escapeAttribute2(version)}" ${SSML_ATTRS.XMLNS}="${SYNTHESIS_NAMESPACE}" ${SSML_ATTRS.XML_LANG}="${escapeAttribute2(lang)}">`;
|
|
2187
|
-
return parseSsml(`${openingTag}${text}</${SSML_TAGS.SPEAK}>`).children ?? [];
|
|
2188
|
-
} catch {
|
|
2189
|
-
return [{ type: "text", value: text }];
|
|
2190
|
-
}
|
|
2191
|
-
}
|
|
2192
|
-
function getVoiceContext(context) {
|
|
2193
|
-
const voice = context.voice;
|
|
2194
|
-
if (typeof voice === "object" && voice !== null) {
|
|
2195
|
-
return voice;
|
|
2196
|
-
}
|
|
2197
|
-
if (context.voiceName === void 0 && context.voiceEffect === void 0 && typeof voice !== "string") {
|
|
2198
|
-
return void 0;
|
|
2199
|
-
}
|
|
2200
|
-
return {
|
|
2201
|
-
name: context.voiceName ?? voice,
|
|
2202
|
-
effect: context.voiceEffect
|
|
2203
|
-
};
|
|
2204
|
-
}
|
|
2205
|
-
function serializePartialSsml(text, context) {
|
|
2206
|
-
const version = context.version ?? DEFAULT_SSML_VERSION;
|
|
2207
|
-
const lang = context.lang ?? DEFAULT_SSML_LANGUAGE;
|
|
2208
|
-
let children = getPartialTextNodes(text, version, lang);
|
|
2209
|
-
if (context.prosody) {
|
|
2210
|
-
children = [
|
|
2211
|
-
{
|
|
2212
|
-
type: SSML_TAGS.PROSODY,
|
|
2213
|
-
...context.prosody,
|
|
2214
|
-
children
|
|
2215
|
-
}
|
|
2216
|
-
];
|
|
2217
|
-
}
|
|
2218
|
-
const voice = getVoiceContext(context);
|
|
2219
|
-
if (voice) {
|
|
2220
|
-
children = [
|
|
2221
|
-
{
|
|
2222
|
-
type: SSML_TAGS.VOICE,
|
|
2223
|
-
...voice,
|
|
2224
|
-
children
|
|
2225
|
-
}
|
|
2226
|
-
];
|
|
2227
|
-
}
|
|
2228
|
-
const document = {
|
|
2229
|
-
type: SSML_TAGS.SPEAK,
|
|
2230
|
-
version,
|
|
2231
|
-
lang,
|
|
2232
|
-
attributes: context.attributes,
|
|
2233
|
-
children
|
|
2234
|
-
};
|
|
2235
|
-
return buildSsml(document);
|
|
2236
|
-
}
|
|
2237
|
-
function buildPartialSsml(textOrOptions, context) {
|
|
2238
|
-
if (typeof textOrOptions === "string") {
|
|
2239
|
-
return serializePartialSsml(textOrOptions, context ?? {});
|
|
2240
|
-
}
|
|
2241
|
-
return serializePartialSsml(textOrOptions.text, textOrOptions);
|
|
2242
|
-
}
|
|
2243
|
-
var PARSER_POSITION_SUFFIX = / at position (\d+)$/;
|
|
2244
|
-
function validateSsml(xmlString) {
|
|
2245
|
-
try {
|
|
2246
|
-
parseSsml(xmlString);
|
|
2247
|
-
return null;
|
|
2248
|
-
} catch (error) {
|
|
2249
|
-
const rawMessage = error instanceof Error ? error.message : String(error);
|
|
2250
|
-
const positionMatch = PARSER_POSITION_SUFFIX.exec(rawMessage);
|
|
2251
|
-
return {
|
|
2252
|
-
message: positionMatch ? rawMessage.slice(0, positionMatch.index) : rawMessage,
|
|
2253
|
-
position: positionMatch ? Number.parseInt(positionMatch[1], 10) : 0
|
|
2254
|
-
};
|
|
2255
|
-
}
|
|
2256
|
-
}
|
|
2257
|
-
var AZURE_VOICE_DEFINITIONS = [
|
|
2258
|
-
{ name: "de-DE-ConradNeural", locale: "de-DE", styles: ["cheerful", "sad"] },
|
|
2259
|
-
{ name: "de-DE-KatjaNeural", locale: "de-DE", styles: ["cheerful", "sad"] },
|
|
2260
|
-
{ name: "en-US-AndrewNeural", locale: "en-US", styles: ["empathetic", "relieved"] },
|
|
2261
|
-
{
|
|
2262
|
-
name: "en-US-GuyNeural",
|
|
2263
|
-
locale: "en-US",
|
|
2264
|
-
styles: [
|
|
2265
|
-
"angry",
|
|
2266
|
-
"cheerful",
|
|
2267
|
-
"excited",
|
|
2268
|
-
"friendly",
|
|
2269
|
-
"hopeful",
|
|
2270
|
-
"newscast",
|
|
2271
|
-
"sad",
|
|
2272
|
-
"shouting",
|
|
2273
|
-
"terrified",
|
|
2274
|
-
"unfriendly",
|
|
2275
|
-
"whispering"
|
|
2276
|
-
]
|
|
2277
|
-
},
|
|
2278
|
-
{
|
|
2279
|
-
name: "en-US-JennyMultilingualNeural",
|
|
2280
|
-
locale: "en-US",
|
|
2281
|
-
styles: [
|
|
2282
|
-
"cheerful",
|
|
2283
|
-
"empathetic",
|
|
2284
|
-
"excited",
|
|
2285
|
-
"friendly",
|
|
2286
|
-
"hopeful",
|
|
2287
|
-
"sad",
|
|
2288
|
-
"shouting",
|
|
2289
|
-
"terrified",
|
|
2290
|
-
"unfriendly",
|
|
2291
|
-
"whispering"
|
|
2292
|
-
]
|
|
2293
|
-
},
|
|
2294
|
-
{
|
|
2295
|
-
name: "en-US-JennyNeural",
|
|
2296
|
-
locale: "en-US",
|
|
2297
|
-
styles: [
|
|
2298
|
-
"assistant",
|
|
2299
|
-
"chat",
|
|
2300
|
-
"customerservice",
|
|
2301
|
-
"newscast",
|
|
2302
|
-
"cheerful",
|
|
2303
|
-
"empathetic",
|
|
2304
|
-
"excited",
|
|
2305
|
-
"friendly",
|
|
2306
|
-
"hopeful",
|
|
2307
|
-
"sad",
|
|
2308
|
-
"shouting",
|
|
2309
|
-
"terrified",
|
|
2310
|
-
"unfriendly",
|
|
2311
|
-
"whispering"
|
|
2312
|
-
]
|
|
2313
|
-
},
|
|
2314
|
-
{ name: "es-ES-ElviraNeural", locale: "es-ES" },
|
|
2315
|
-
{ name: "fil-PH-AngeloNeural", locale: "fil-PH" },
|
|
2316
|
-
{ name: "fil-PH-Angelo:DragonHDLatestNeural", locale: "fil-PH" },
|
|
2317
|
-
{ name: "fil-PH-BlessicaNeural", locale: "fil-PH" },
|
|
2318
|
-
{ name: "fil-PH-Blessica:DragonHDLatestNeural", locale: "fil-PH" },
|
|
2319
|
-
{ name: "fr-FR-DeniseNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
|
|
2320
|
-
{ name: "fr-FR-HenriNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
|
|
2321
|
-
{ name: "id-ID-GadisNeural", locale: "id-ID" },
|
|
2322
|
-
{ name: "it-IT-ElsaNeural", locale: "it-IT", styles: ["cheerful", "sad"] },
|
|
2323
|
-
{ name: "ja-JP-KeitaNeural", locale: "ja-JP", styles: ["chat"] },
|
|
2324
|
-
{ name: "ja-JP-MayuNeural", locale: "ja-JP", styles: ["calm", "cheerful", "sad"] },
|
|
2325
|
-
{ name: "ja-JP-NanamiNeural", locale: "ja-JP", styles: ["chat", "customerservice", "cheerful", "whispering", "sad"] },
|
|
2326
|
-
{ name: "ko-KR-SunHiNeural", locale: "ko-KR", styles: ["cheerful", "sad"] },
|
|
2327
|
-
{ name: "ms-MY-YasminNeural", locale: "ms-MY" },
|
|
2328
|
-
{ name: "pt-BR-FranciscaNeural", locale: "pt-BR", styles: ["calm"] },
|
|
2329
|
-
{
|
|
2330
|
-
name: "ru-RU-SvetlanaNeural",
|
|
2331
|
-
locale: "ru-RU",
|
|
2332
|
-
styles: ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"]
|
|
2333
|
-
},
|
|
2334
|
-
{ name: "th-TH-PremwadeeNeural", locale: "th-TH" },
|
|
2335
|
-
{ name: "vi-VN-HoaiMyNeural", locale: "vi-VN" },
|
|
2336
|
-
{
|
|
2337
|
-
name: "zh-CN-XiaoxiaoNeural",
|
|
2338
|
-
locale: "zh-CN",
|
|
2339
|
-
styles: [
|
|
2340
|
-
"assistant",
|
|
2341
|
-
"chat",
|
|
2342
|
-
"customerservice",
|
|
2343
|
-
"newscast",
|
|
2344
|
-
"cheerful",
|
|
2345
|
-
"empathetic",
|
|
2346
|
-
"excited",
|
|
2347
|
-
"friendly",
|
|
2348
|
-
"hopeful",
|
|
2349
|
-
"sad",
|
|
2350
|
-
"terrified",
|
|
2351
|
-
"whispering",
|
|
2352
|
-
"poetry-reading",
|
|
2353
|
-
"sports_commentary",
|
|
2354
|
-
"sports_commentary_excited",
|
|
2355
|
-
"story"
|
|
2356
|
-
]
|
|
2357
|
-
},
|
|
2358
|
-
{
|
|
2359
|
-
name: "zh-CN-YunxiNeural",
|
|
2360
|
-
locale: "zh-CN",
|
|
2361
|
-
styles: [
|
|
2362
|
-
"narration-relaxed",
|
|
2363
|
-
"embarrassed",
|
|
2364
|
-
"fearful",
|
|
2365
|
-
"sad",
|
|
2366
|
-
"disgruntled",
|
|
2367
|
-
"serious",
|
|
2368
|
-
"angry",
|
|
2369
|
-
"depressed",
|
|
2370
|
-
"chat",
|
|
2371
|
-
"cheerful",
|
|
2372
|
-
"assistant"
|
|
2373
|
-
]
|
|
2374
|
-
},
|
|
2375
|
-
{ name: "zh-TW-HsiaoChenNeural", locale: "zh-TW" }
|
|
2376
|
-
];
|
|
2377
|
-
var ALLOWED_BREAK_STRENGTHS = /* @__PURE__ */ new Set(["none", "x-weak", "weak", "medium", "strong", "x-strong"]);
|
|
2378
|
-
var ALLOWED_SAY_AS = /* @__PURE__ */ new Set([
|
|
2379
|
-
"characters",
|
|
2380
|
-
"spell-out",
|
|
2381
|
-
"cardinal",
|
|
2382
|
-
"ordinal",
|
|
2383
|
-
"number",
|
|
2384
|
-
"date",
|
|
2385
|
-
"time",
|
|
2386
|
-
"telephone",
|
|
2387
|
-
"fraction",
|
|
2388
|
-
"address",
|
|
2389
|
-
"name",
|
|
2390
|
-
"currency",
|
|
2391
|
-
"number_digit"
|
|
2392
|
-
]);
|
|
2393
|
-
var ALLOWED_ROLES = /* @__PURE__ */ new Set([
|
|
2394
|
-
"Girl",
|
|
2395
|
-
"Boy",
|
|
2396
|
-
"YoungAdultFemale",
|
|
2397
|
-
"YoungAdultMale",
|
|
2398
|
-
"OlderAdultFemale",
|
|
2399
|
-
"OlderAdultMale",
|
|
2400
|
-
"SeniorFemale",
|
|
2401
|
-
"SeniorMale"
|
|
2402
|
-
]);
|
|
2403
|
-
var ALLOWED_EMPHASIS_LEVELS = /* @__PURE__ */ new Set(["strong", "moderate", "reduced", "none"]);
|
|
2404
|
-
var ALLOWED_SILENCE_TYPES = /* @__PURE__ */ new Set([
|
|
2405
|
-
"Leading",
|
|
2406
|
-
"Tailing",
|
|
2407
|
-
"Sentenceboundary",
|
|
2408
|
-
"Comma",
|
|
2409
|
-
"Semicolon",
|
|
2410
|
-
"Enumerationcomma"
|
|
2411
|
-
]);
|
|
2412
|
-
var ALLOWED_VISEME_TYPES = /* @__PURE__ */ new Set(["redlips_front", "FacialExpression"]);
|
|
2413
|
-
var DEFAULT_PREVIEW_TAGS = /* @__PURE__ */ new Set(["mstts:voiceconversion"]);
|
|
2414
|
-
function featureStatusForTag(name, options) {
|
|
2415
|
-
const tagName = canonicalTagName(name);
|
|
2416
|
-
const configured = Object.entries(options.tagStatuses ?? {}).find(
|
|
2417
|
-
([candidate]) => canonicalTagName(candidate) === tagName
|
|
2418
|
-
)?.[1];
|
|
2419
|
-
if (configured) return configured;
|
|
2420
|
-
if ((options.previewTags ?? [...DEFAULT_PREVIEW_TAGS]).some((candidate) => canonicalTagName(candidate) === tagName))
|
|
2421
|
-
return "preview";
|
|
2422
|
-
if ((options.deprecatedTags ?? []).some((candidate) => canonicalTagName(candidate) === tagName)) return "deprecated";
|
|
2423
|
-
return void 0;
|
|
2424
|
-
}
|
|
2425
|
-
function decodeAttribute(value) {
|
|
2426
|
-
return value.replace(
|
|
2427
|
-
/&(?:amp|apos|gt|lt|quot);/gi,
|
|
2428
|
-
(entity) => ({ "&": "&", "'": "'", ">": ">", "<": "<", """: '"' })[entity.toLowerCase()] ?? entity
|
|
2429
|
-
);
|
|
2430
|
-
}
|
|
2431
|
-
function findTagEnd2(source, start) {
|
|
2432
|
-
let quote = "";
|
|
2433
|
-
for (let index = start; index < source.length; index += 1) {
|
|
2434
|
-
const character = source[index];
|
|
2435
|
-
if (quote) {
|
|
2436
|
-
if (character === quote) quote = "";
|
|
2437
|
-
} else if (character === '"' || character === "'") quote = character;
|
|
2438
|
-
else if (character === ">") return index;
|
|
2439
|
-
}
|
|
2440
|
-
return source.length - 1;
|
|
2441
|
-
}
|
|
2442
|
-
function tokenizeElements(source) {
|
|
2443
|
-
const tokens = [];
|
|
2444
|
-
const openElements = [];
|
|
2445
|
-
let index = 0;
|
|
2446
|
-
while (index < source.length) {
|
|
2447
|
-
const start = source.indexOf("<", index);
|
|
2448
|
-
if (start === -1) break;
|
|
2449
|
-
if (source.startsWith("<!--", start)) {
|
|
2450
|
-
const end2 = source.indexOf("-->", start + 4);
|
|
2451
|
-
index = end2 === -1 ? source.length : end2 + 3;
|
|
2452
|
-
continue;
|
|
2453
|
-
}
|
|
2454
|
-
if (source.startsWith("<![CDATA[", start)) {
|
|
2455
|
-
const end2 = source.indexOf("]]>", start + 9);
|
|
2456
|
-
index = end2 === -1 ? source.length : end2 + 3;
|
|
2457
|
-
continue;
|
|
2458
|
-
}
|
|
2459
|
-
if (source.startsWith("<?", start)) {
|
|
2460
|
-
const end2 = source.indexOf("?>", start + 2);
|
|
2461
|
-
index = end2 === -1 ? source.length : end2 + 2;
|
|
2462
|
-
continue;
|
|
2463
|
-
}
|
|
2464
|
-
const end = findTagEnd2(source, start + 1);
|
|
2465
|
-
const raw = source.slice(start, end + 1);
|
|
2466
|
-
if (raw.startsWith("</")) {
|
|
2467
|
-
openElements.pop();
|
|
2468
|
-
index = end + 1;
|
|
2469
|
-
continue;
|
|
2470
|
-
}
|
|
2471
|
-
const nameMatch = /^<\s*([A-Za-z_][A-Za-z0-9_.:-]*)/.exec(raw);
|
|
2472
|
-
if (!nameMatch?.[1]) {
|
|
2473
|
-
index = end + 1;
|
|
2474
|
-
continue;
|
|
2475
|
-
}
|
|
2476
|
-
const attributes = /* @__PURE__ */ new Map();
|
|
2477
|
-
const attributeSource = raw.slice(nameMatch[0].length, raw.length - 1).replace(/\/\s*$/, "");
|
|
2478
|
-
const attributePattern = /([A-Za-z_][A-Za-z0-9_.:-]*)\s*=\s*(["'])([\s\S]*?)\2/g;
|
|
2479
|
-
for (const match of attributeSource.matchAll(attributePattern)) {
|
|
2480
|
-
attributes.set(match[1].toLowerCase(), decodeAttribute(match[3]));
|
|
2481
|
-
}
|
|
2482
|
-
const selfClosing = /\/\s*>$/.test(raw);
|
|
2483
|
-
const parent = openElements[openElements.length - 1];
|
|
2484
|
-
const childElementIndex = parent?.childElementCount;
|
|
2485
|
-
if (parent) parent.childElementCount += 1;
|
|
2486
|
-
const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
|
|
2487
|
-
const tokenName = nameMatch[1];
|
|
2488
|
-
const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
|
|
2489
|
-
tokens.push({
|
|
2490
|
-
attributes,
|
|
2491
|
-
childElementIndex,
|
|
2492
|
-
end,
|
|
2493
|
-
depth: openElements.length + 1,
|
|
2494
|
-
name: tokenName,
|
|
2495
|
-
parentName: parent?.name,
|
|
2496
|
-
parentVoiceName,
|
|
2497
|
-
selfClosing,
|
|
2498
|
-
start
|
|
2499
|
-
});
|
|
2500
|
-
if (!selfClosing) {
|
|
2501
|
-
openElements.push({
|
|
2502
|
-
childElementCount: 0,
|
|
2503
|
-
name: tokenName,
|
|
2504
|
-
voiceName: tokenVoiceName
|
|
2505
|
-
});
|
|
2506
|
-
}
|
|
2507
|
-
index = end + 1;
|
|
2508
|
-
}
|
|
2509
|
-
return tokens;
|
|
2510
|
-
}
|
|
2511
|
-
function location(source, offset) {
|
|
2512
|
-
const before = source.slice(0, Math.max(0, offset));
|
|
2513
|
-
const line = before.split("\n").length;
|
|
2514
|
-
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
2515
|
-
}
|
|
2516
|
-
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code2) {
|
|
2517
|
-
diagnostics.push({
|
|
2518
|
-
...location(source, offset),
|
|
2519
|
-
message,
|
|
2520
|
-
severity,
|
|
2521
|
-
source: "ssml-static-validator",
|
|
2522
|
-
...code2 ? { code: code2 } : {}
|
|
2523
|
-
});
|
|
2524
|
-
}
|
|
2525
|
-
function isSupportedProsodyRate(value) {
|
|
2526
|
-
const trimmed = value.trim();
|
|
2527
|
-
if (/^(x-slow|slow|medium|fast|x-fast|[+-]?\d+(?:\.\d+)?%)$/.test(trimmed)) return true;
|
|
2528
|
-
const multiplier = /^(\d+(?:\.\d+)?)(x)?$/i.exec(trimmed);
|
|
2529
|
-
if (!multiplier) return false;
|
|
2530
|
-
const numericValue = Number(multiplier[1]);
|
|
2531
|
-
return numericValue >= 0.5 && numericValue <= 2;
|
|
2532
|
-
}
|
|
2533
|
-
function isValidAzureAudioDuration(value) {
|
|
2534
|
-
const trimmed = value.trim();
|
|
2535
|
-
const numeric = /^(\d+(?:\.\d+)?)(ms|s)$/.exec(trimmed);
|
|
2536
|
-
if (numeric) return Number(numeric[1]) > 0;
|
|
2537
|
-
const clock = /^(\d{2,}):([0-5]\d):([0-5]\d)(?:\.(\d{1,3}))?$/.exec(trimmed);
|
|
2538
|
-
if (!clock) return false;
|
|
2539
|
-
return Number(clock[1]) > 0 || Number(clock[2]) > 0 || Number(clock[3]) > 0 || Number(clock[4] ?? 0) > 0;
|
|
2540
|
-
}
|
|
2541
|
-
function isValidAzureBackgroundAudioDuration(value) {
|
|
2542
|
-
const match = /^(\d+)$/.exec(value.trim());
|
|
2543
|
-
if (!match) return false;
|
|
2544
|
-
const milliseconds = Number(match[1]);
|
|
2545
|
-
return Number.isFinite(milliseconds) && milliseconds >= 0 && milliseconds <= 1e4;
|
|
2546
|
-
}
|
|
2547
|
-
function attr(token, name) {
|
|
2548
|
-
return token.attributes.get(name.toLowerCase());
|
|
2549
|
-
}
|
|
2550
|
-
var DEFAULT_LANGUAGE_ALIASES = {
|
|
2551
|
-
"zh-CN": ["zh-Hans"],
|
|
2552
|
-
"zh-TW": ["zh-Hant"]
|
|
2553
|
-
};
|
|
2554
|
-
function canonicalLanguageTag(language) {
|
|
2555
|
-
const trimmed = language.trim();
|
|
2556
|
-
if (!trimmed) return "";
|
|
2557
|
-
try {
|
|
2558
|
-
return new Intl.Locale(trimmed).toString().toLowerCase();
|
|
2559
|
-
} catch {
|
|
2560
|
-
return trimmed.toLowerCase();
|
|
2561
|
-
}
|
|
2562
|
-
}
|
|
2563
|
-
function createLanguageNormalizer(options) {
|
|
2564
|
-
const aliases = /* @__PURE__ */ new Map();
|
|
2565
|
-
const addAliasGroup = (canonical, values) => {
|
|
2566
|
-
const normalizedCanonical = canonicalLanguageTag(canonical);
|
|
2567
|
-
if (!normalizedCanonical) return;
|
|
2568
|
-
aliases.set(normalizedCanonical, normalizedCanonical);
|
|
2569
|
-
for (const value of values) {
|
|
2570
|
-
const normalizedValue = canonicalLanguageTag(value);
|
|
2571
|
-
if (normalizedValue) aliases.set(normalizedValue, normalizedCanonical);
|
|
2572
|
-
}
|
|
2573
|
-
};
|
|
2574
|
-
for (const [canonical, values] of Object.entries(DEFAULT_LANGUAGE_ALIASES)) addAliasGroup(canonical, values);
|
|
2575
|
-
for (const [canonical, valueOrValues] of Object.entries(options.languageAliases ?? {}))
|
|
2576
|
-
addAliasGroup(canonical, typeof valueOrValues === "string" ? [valueOrValues] : valueOrValues);
|
|
2577
|
-
return (language) => {
|
|
2578
|
-
const customValue = options.normalizeLanguage ? options.normalizeLanguage(language) : language;
|
|
2579
|
-
const normalized = canonicalLanguageTag(customValue);
|
|
2580
|
-
return aliases.get(normalized) ?? normalized;
|
|
2581
|
-
};
|
|
2582
|
-
}
|
|
2583
|
-
function voiceLocalePrefix(voiceName) {
|
|
2584
|
-
const match = /^(?<language>[A-Za-z]{2,3})-(?<region>[A-Za-z]{2}|\d{3})(?:-|$)/.exec(voiceName.trim());
|
|
2585
|
-
if (!match?.groups) return void 0;
|
|
2586
|
-
const tag = `${match.groups.language}-${match.groups.region}`;
|
|
2587
|
-
return {
|
|
2588
|
-
language: match.groups.language.toLowerCase(),
|
|
2589
|
-
region: match.groups.region.toLowerCase(),
|
|
2590
|
-
tag
|
|
2591
|
-
};
|
|
2592
|
-
}
|
|
2593
|
-
function definitionFromStyleMap(voiceName, styles) {
|
|
2594
|
-
return {
|
|
2595
|
-
name: voiceName,
|
|
2596
|
-
locale: voiceLocalePrefix(voiceName)?.tag ?? "",
|
|
2597
|
-
styles
|
|
2598
|
-
};
|
|
2599
|
-
}
|
|
2600
|
-
function normalizeVoiceCatalog(options) {
|
|
2601
|
-
const definitions = /* @__PURE__ */ new Map();
|
|
2602
|
-
for (const definition of AZURE_VOICE_DEFINITIONS) definitions.set(definition.name.toLowerCase(), definition);
|
|
2603
|
-
for (const definition of options.voiceCatalog ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
2604
|
-
for (const definition of options.voiceDefinitions ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
2605
|
-
for (const definition of options.customVoiceDefinitions ?? [])
|
|
2606
|
-
definitions.set(definition.name.toLowerCase(), definition);
|
|
2607
|
-
for (const [voiceName, styles] of Object.entries(options.customVoiceStyleMap ?? {})) {
|
|
2608
|
-
const key = voiceName.toLowerCase();
|
|
2609
|
-
const current = definitions.get(key);
|
|
2610
|
-
definitions.set(key, {
|
|
2611
|
-
...current ?? definitionFromStyleMap(voiceName, styles),
|
|
2612
|
-
name: current?.name ?? voiceName,
|
|
2613
|
-
styles: styles.map((style) => style.toLowerCase())
|
|
2614
|
-
});
|
|
2615
|
-
}
|
|
2616
|
-
return definitions;
|
|
2617
|
-
}
|
|
2618
|
-
function diagnosticSeverity(policy) {
|
|
2619
|
-
if (policy === "ignore") return void 0;
|
|
2620
|
-
return policy === "error" ? "error" : "warning";
|
|
2621
|
-
}
|
|
2622
|
-
function languagePart(language) {
|
|
2623
|
-
try {
|
|
2624
|
-
return new Intl.Locale(language).language.toLowerCase();
|
|
2625
|
-
} catch {
|
|
2626
|
-
return language.split("-")[0]?.toLowerCase() ?? "";
|
|
2627
|
-
}
|
|
2628
|
-
}
|
|
2629
|
-
function definitionMatchesLanguage(definition, voiceName, language, normalizeLanguage) {
|
|
2630
|
-
const candidateLanguages = definition ? [definition.locale, ...definition.secondaryLocales ?? []].filter(Boolean) : [voiceLocalePrefix(voiceName)?.tag ?? ""];
|
|
2631
|
-
if (candidateLanguages.length === 0 || !language.trim()) return void 0;
|
|
2632
|
-
const normalizedLanguage = normalizeLanguage(language);
|
|
2633
|
-
const normalizedCandidates = candidateLanguages.map(normalizeLanguage);
|
|
2634
|
-
if (normalizedCandidates.includes(normalizedLanguage)) return true;
|
|
2635
|
-
if (!normalizedLanguage || !normalizedCandidates.some(Boolean)) return void 0;
|
|
2636
|
-
return normalizedLanguage === languagePart(normalizedLanguage) ? normalizedCandidates.some((candidate) => languagePart(candidate) === normalizedLanguage) : false;
|
|
2637
|
-
}
|
|
2638
|
-
function canonicalTagName(name) {
|
|
2639
|
-
const normalized = name.toLowerCase();
|
|
2640
|
-
if (normalized === "express-as" || normalized === "expressas") return "mstts:express-as";
|
|
2641
|
-
if (normalized === "sayas") return "say-as";
|
|
2642
|
-
return normalized;
|
|
2643
|
-
}
|
|
2644
|
-
function validateVoiceFeatureMatrix(token, source, diagnostics, voiceName, definition) {
|
|
2645
|
-
if (!voiceName || !definition || token.name.toLowerCase() === "voice" || token.name.toLowerCase() === "mstts:turn")
|
|
2646
|
-
return;
|
|
2647
|
-
const tagName = canonicalTagName(token.name);
|
|
2648
|
-
const unsupportedTags = new Set((definition.unsupportedTags ?? []).map(canonicalTagName));
|
|
2649
|
-
const supportedTags = definition.supportedTags?.map(canonicalTagName);
|
|
2650
|
-
if (unsupportedTags.has(tagName) || supportedTags !== void 0 && !supportedTags.includes(tagName)) {
|
|
2651
|
-
addDiagnostic(
|
|
2652
|
-
diagnostics,
|
|
2653
|
-
source,
|
|
2654
|
-
token.start,
|
|
2655
|
-
`Tag <${token.name}> is not supported by voice "${voiceName}" according to the configured feature matrix.`,
|
|
2656
|
-
"error",
|
|
2657
|
-
"azure-unsupported-tag-for-voice"
|
|
2658
|
-
);
|
|
2659
|
-
}
|
|
2660
|
-
}
|
|
2661
|
-
function validateAudioSource(token, source, diagnostics, options, elementName2) {
|
|
2662
|
-
const src = attr(token, "src");
|
|
2663
|
-
if (!src) {
|
|
2664
|
-
addDiagnostic(diagnostics, source, token.start, `<${elementName2}> requires a "src" attribute.`);
|
|
2665
|
-
return;
|
|
2666
|
-
}
|
|
2667
|
-
let parsed;
|
|
2668
|
-
try {
|
|
2669
|
-
parsed = new URL(src);
|
|
2670
|
-
} catch {
|
|
2671
|
-
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must be an absolute HTTP(S) URL.`);
|
|
2672
|
-
return;
|
|
2673
|
-
}
|
|
2674
|
-
if (parsed.username || parsed.password)
|
|
2675
|
-
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must not contain URL credentials.`);
|
|
2676
|
-
if (parsed.protocol !== "https:" && !(options.allowHttpAudio && parsed.protocol === "http:"))
|
|
2677
|
-
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must use HTTPS.`);
|
|
2678
|
-
const isAllowedOrigin = options.allowedAudioOrigins?.some((allowedOrigin) => {
|
|
2679
|
-
try {
|
|
2680
|
-
const configured = new URL(allowedOrigin);
|
|
2681
|
-
if (configured.protocol !== "https:" && configured.protocol !== "http:" || configured.username || configured.password || configured.pathname !== "/" || configured.search || configured.hash)
|
|
2682
|
-
return false;
|
|
2683
|
-
return configured.origin === parsed.origin;
|
|
2684
|
-
} catch {
|
|
2685
|
-
return false;
|
|
2686
|
-
}
|
|
2687
|
-
}) ?? false;
|
|
2688
|
-
if (options.allowedAudioOrigins && !isAllowedOrigin)
|
|
2689
|
-
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> origin "${parsed.origin}" is not allowed.`);
|
|
2690
|
-
else if (!isAllowedOrigin && !options.allowExternalAudio)
|
|
2691
|
-
addDiagnostic(
|
|
2692
|
-
diagnostics,
|
|
2693
|
-
source,
|
|
2694
|
-
token.start,
|
|
2695
|
-
`<${elementName2} src> external origin "${parsed.origin}" is blocked by default; set allowExternalAudio to true or provide allowedAudioOrigins.`
|
|
2696
|
-
);
|
|
2697
|
-
}
|
|
2698
|
-
function validateElement(token, source, diagnostics, voiceName, options, voiceCatalog) {
|
|
2699
|
-
const name = token.name.toLowerCase();
|
|
2700
|
-
const tagStatus = featureStatusForTag(token.name, options);
|
|
2701
|
-
if (tagStatus === "preview")
|
|
2702
|
-
addDiagnostic(
|
|
2703
|
-
diagnostics,
|
|
2704
|
-
source,
|
|
2705
|
-
token.start,
|
|
2706
|
-
`<${token.name}> is an Azure Speech preview feature and may change or require preview access.`,
|
|
2707
|
-
"warning",
|
|
2708
|
-
"azure-preview-tag"
|
|
2709
|
-
);
|
|
2710
|
-
if (tagStatus === "deprecated")
|
|
2711
|
-
addDiagnostic(
|
|
2712
|
-
diagnostics,
|
|
2713
|
-
source,
|
|
2714
|
-
token.start,
|
|
2715
|
-
`<${token.name}> is deprecated by Azure Speech; migrate to a supported alternative.`,
|
|
2716
|
-
"info",
|
|
2717
|
-
"azure-deprecated-tag"
|
|
2718
|
-
);
|
|
2719
|
-
if (name === "voice" && !attr(token, "name")?.trim())
|
|
2720
|
-
addDiagnostic(diagnostics, source, token.start, '<voice> requires a non-empty "name" attribute.');
|
|
2721
|
-
if (name === "break") {
|
|
2722
|
-
const time = attr(token, "time");
|
|
2723
|
-
const strength = attr(token, "strength");
|
|
2724
|
-
if (!time && !strength)
|
|
2725
|
-
addDiagnostic(diagnostics, source, token.start, '<break> requires either "time" or "strength".');
|
|
2726
|
-
if (time && strength)
|
|
2727
|
-
addDiagnostic(diagnostics, source, token.start, '<break> must not specify both "time" and "strength".');
|
|
2728
|
-
if (time && !/^\d+(?:\.\d+)?(?:ms|s)$/.test(time.trim()))
|
|
2729
|
-
addDiagnostic(diagnostics, source, token.start, '<break time> must use a numeric value followed by "ms" or "s".');
|
|
2730
|
-
if (strength && !ALLOWED_BREAK_STRENGTHS.has(strength))
|
|
2731
|
-
addDiagnostic(diagnostics, source, token.start, `Unsupported <break strength> value "${strength}".`);
|
|
2732
|
-
}
|
|
2733
|
-
if (name === "prosody") {
|
|
2734
|
-
const rate = attr(token, "rate");
|
|
2735
|
-
const pitch = attr(token, "pitch");
|
|
2736
|
-
const volume = attr(token, "volume");
|
|
2737
|
-
if (rate && !isSupportedProsodyRate(rate))
|
|
2738
|
-
addDiagnostic(diagnostics, source, token.start, `Unsupported <prosody rate> value "${rate}".`);
|
|
2739
|
-
if (pitch && !/^(x-low|low|medium|high|x-high|[+-]?\d+(?:\.\d+)?(?:st|Hz|%)?)$/.test(pitch.trim()))
|
|
2740
|
-
addDiagnostic(diagnostics, source, token.start, `Unsupported <prosody pitch> value "${pitch}".`);
|
|
2741
|
-
if (volume && !/^(silent|x-soft|soft|medium|loud|x-loud|[+-]?\d+(?:\.\d+)?(?:dB|%)?)$/.test(volume.trim()))
|
|
2742
|
-
addDiagnostic(diagnostics, source, token.start, `Unsupported <prosody volume> value "${volume}".`);
|
|
2743
|
-
}
|
|
2744
|
-
if (name === "mstts:express-as" || name === "express-as" || name === "expressas") {
|
|
2745
|
-
const style = attr(token, "style");
|
|
2746
|
-
if (!style?.trim())
|
|
2747
|
-
addDiagnostic(diagnostics, source, token.start, '<mstts:express-as> requires a non-empty "style" attribute.');
|
|
2748
|
-
const degree = attr(token, "styledegree") ?? attr(token, "style-degree");
|
|
2749
|
-
if (degree && (!/^\d+(?:\.\d+)?$/.test(degree) || Number(degree) < 0.01 || Number(degree) > 2))
|
|
2750
|
-
addDiagnostic(
|
|
2751
|
-
diagnostics,
|
|
2752
|
-
source,
|
|
2753
|
-
token.start,
|
|
2754
|
-
"<mstts:express-as styledegree> must be a number between 0.01 and 2."
|
|
2755
|
-
);
|
|
2756
|
-
const role = attr(token, "role");
|
|
2757
|
-
if (role && !ALLOWED_ROLES.has(role))
|
|
2758
|
-
addDiagnostic(diagnostics, source, token.start, `Unsupported <mstts:express-as role> value "${role}".`);
|
|
2759
|
-
const definition = voiceName ? voiceCatalog.get(voiceName.toLowerCase()) : void 0;
|
|
2760
|
-
const supportedStyles = definition?.styles;
|
|
2761
|
-
const severity = diagnosticSeverity(options.unsupportedStylePolicy ?? options.unknownVoicePolicy ?? "warn");
|
|
2762
|
-
if (style && definition && !supportedStyles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase()) && severity)
|
|
2763
|
-
addDiagnostic(
|
|
2764
|
-
diagnostics,
|
|
2765
|
-
source,
|
|
2766
|
-
token.start,
|
|
2767
|
-
`Unknown style "${style}" is not supported by voice "${voiceName}" according to the configured voice style map.`,
|
|
2768
|
-
severity,
|
|
2769
|
-
"azure-unsupported-style"
|
|
2770
|
-
);
|
|
2771
|
-
if (style && voiceName && !definition && severity)
|
|
2772
|
-
addDiagnostic(
|
|
2773
|
-
diagnostics,
|
|
2774
|
-
source,
|
|
2775
|
-
token.start,
|
|
2776
|
-
`Unknown style "${style}" cannot be verified because voice "${voiceName}" is not registered in the voice style map.`,
|
|
2777
|
-
severity
|
|
2778
|
-
);
|
|
2779
|
-
}
|
|
2780
|
-
if (name === "say-as" || name === "sayas") {
|
|
2781
|
-
const interpretAs = attr(token, "interpret-as");
|
|
2782
|
-
if (!interpretAs || !ALLOWED_SAY_AS.has(interpretAs))
|
|
2783
|
-
addDiagnostic(diagnostics, source, token.start, `<say-as> requires a supported "interpret-as" value.`);
|
|
2784
|
-
}
|
|
2785
|
-
if (name === "phoneme" && (!attr(token, "alphabet") || !attr(token, "ph")))
|
|
2786
|
-
addDiagnostic(diagnostics, source, token.start, '<phoneme> requires both "alphabet" and "ph" attributes.');
|
|
2787
|
-
if (name === "emphasis" && attr(token, "level") && !ALLOWED_EMPHASIS_LEVELS.has(attr(token, "level") ?? ""))
|
|
2788
|
-
addDiagnostic(diagnostics, source, token.start, `Unsupported <emphasis level> value "${attr(token, "level")}".`);
|
|
2789
|
-
if (name === "sub" && !attr(token, "alias")?.trim())
|
|
2790
|
-
addDiagnostic(diagnostics, source, token.start, '<sub> requires a non-empty "alias" attribute.');
|
|
2791
|
-
if (name === "lang" && !attr(token, "xml:lang")?.trim() && !attr(token, "lang")?.trim())
|
|
2792
|
-
addDiagnostic(diagnostics, source, token.start, '<lang> requires an "xml:lang" attribute.');
|
|
2793
|
-
if (name === "mark" && !attr(token, "name")?.trim())
|
|
2794
|
-
addDiagnostic(diagnostics, source, token.start, '<mark> requires a non-empty "name" attribute.');
|
|
2795
|
-
if (name === "bookmark" && !attr(token, "mark")?.trim())
|
|
2796
|
-
addDiagnostic(diagnostics, source, token.start, '<bookmark> requires a non-empty "mark" attribute.');
|
|
2797
|
-
if (name === "lexicon") {
|
|
2798
|
-
const uri = attr(token, "uri");
|
|
2799
|
-
if (!uri) addDiagnostic(diagnostics, source, token.start, '<lexicon> requires a "uri" attribute.');
|
|
2800
|
-
else {
|
|
2801
|
-
try {
|
|
2802
|
-
const parsed = new URL(uri);
|
|
2803
|
-
if (parsed.protocol !== "https:")
|
|
2804
|
-
addDiagnostic(diagnostics, source, token.start, "<lexicon uri> must use HTTPS.");
|
|
2805
|
-
} catch {
|
|
2806
|
-
addDiagnostic(diagnostics, source, token.start, "<lexicon uri> must be an absolute HTTPS URL.");
|
|
2807
|
-
}
|
|
2808
|
-
}
|
|
2809
|
-
}
|
|
2810
|
-
if (name === "mstts:silence") {
|
|
2811
|
-
const type = attr(token, "type");
|
|
2812
|
-
const value = attr(token, "value");
|
|
2813
|
-
if (!type || !ALLOWED_SILENCE_TYPES.has(type))
|
|
2814
|
-
addDiagnostic(diagnostics, source, token.start, '<mstts:silence> requires a supported "type" attribute.');
|
|
2815
|
-
if (!value || !/^\d+(?:\.\d+)?(?:ms|s)$/.test(value.trim()))
|
|
2816
|
-
addDiagnostic(diagnostics, source, token.start, '<mstts:silence> requires a time-valued "value" attribute.');
|
|
2817
|
-
}
|
|
2818
|
-
if (name === "mstts:audioduration") {
|
|
2819
|
-
const value = attr(token, "value");
|
|
2820
|
-
if (!value || !isValidAzureAudioDuration(value))
|
|
2821
|
-
addDiagnostic(
|
|
2822
|
-
diagnostics,
|
|
2823
|
-
source,
|
|
2824
|
-
token.start,
|
|
2825
|
-
'<mstts:audioduration> requires a positive duration such as "10s", "5000ms", or "00:00:10".'
|
|
2826
|
-
);
|
|
2827
|
-
if (!token.selfClosing)
|
|
2828
|
-
addDiagnostic(diagnostics, source, token.start, "<mstts:audioduration> must be self-closing.");
|
|
2829
|
-
}
|
|
2830
|
-
if (name === "mstts:viseme") {
|
|
2831
|
-
const type = attr(token, "type");
|
|
2832
|
-
if (!type || !ALLOWED_VISEME_TYPES.has(type))
|
|
2833
|
-
addDiagnostic(diagnostics, source, token.start, '<mstts:viseme> requires a supported "type" attribute.');
|
|
2834
|
-
}
|
|
2835
|
-
if (name === "audio") {
|
|
2836
|
-
validateAudioSource(token, source, diagnostics, options, "audio");
|
|
2837
|
-
}
|
|
2838
|
-
if (name === "mstts:turn") {
|
|
2839
|
-
if (!attr(token, "voice")?.trim() && !attr(token, "speaker")?.trim())
|
|
2840
|
-
addDiagnostic(
|
|
2841
|
-
diagnostics,
|
|
2842
|
-
source,
|
|
2843
|
-
token.start,
|
|
2844
|
-
'<mstts:turn> requires a non-empty "voice" or "speaker" attribute.'
|
|
2845
|
-
);
|
|
2846
|
-
if (token.parentName?.toLowerCase() !== "mstts:dialog")
|
|
2847
|
-
addDiagnostic(diagnostics, source, token.start, "<mstts:turn> is only allowed directly inside <mstts:dialog>.");
|
|
2848
|
-
}
|
|
2849
|
-
if (name === "mstts:backgroundaudio") {
|
|
2850
|
-
validateAudioSource(token, source, diagnostics, options, "mstts:backgroundaudio");
|
|
2851
|
-
const volume = attr(token, "volume");
|
|
2852
|
-
if (volume !== void 0 && (!/^\d+(?:\.\d+)?$/.test(volume.trim()) || Number(volume) > 100))
|
|
2853
|
-
addDiagnostic(diagnostics, source, token.start, `Unsupported <mstts:backgroundaudio volume> value "${volume}".`);
|
|
2854
|
-
for (const [attribute, value] of [
|
|
2855
|
-
["fadein", attr(token, "fadein")],
|
|
2856
|
-
["fadeout", attr(token, "fadeout")]
|
|
2857
|
-
]) {
|
|
2858
|
-
if (value !== void 0 && !isValidAzureBackgroundAudioDuration(value))
|
|
2859
|
-
addDiagnostic(
|
|
2860
|
-
diagnostics,
|
|
2861
|
-
source,
|
|
2862
|
-
token.start,
|
|
2863
|
-
`<mstts:backgroundaudio ${attribute}> must be between 0 and 10000 milliseconds, for example "500ms" or "10s".`
|
|
2864
|
-
);
|
|
2865
|
-
}
|
|
2866
|
-
if (token.parentName?.toLowerCase() !== "speak" || token.childElementIndex !== 0)
|
|
2867
|
-
addDiagnostic(
|
|
2868
|
-
diagnostics,
|
|
2869
|
-
source,
|
|
2870
|
-
token.start,
|
|
2871
|
-
"<mstts:backgroundaudio> must be the first element directly under <speak>."
|
|
2872
|
-
);
|
|
2873
|
-
if (!token.selfClosing)
|
|
2874
|
-
addDiagnostic(diagnostics, source, token.start, "<mstts:backgroundaudio> must be self-closing.");
|
|
2875
|
-
}
|
|
2876
|
-
}
|
|
2877
|
-
function validateAzureSsml(ssml, options = {}) {
|
|
2878
|
-
const diagnostics = [];
|
|
2879
|
-
if (typeof ssml !== "string") {
|
|
2880
|
-
return [
|
|
2881
|
-
{
|
|
2882
|
-
line: 1,
|
|
2883
|
-
column: 1,
|
|
2884
|
-
message: "SSML input must be a string",
|
|
2885
|
-
severity: "error",
|
|
2886
|
-
source: "ssml-static-validator"
|
|
2887
|
-
}
|
|
2888
|
-
];
|
|
2889
|
-
}
|
|
2890
|
-
const maxLength = options.maxLength ?? 1e4;
|
|
2891
|
-
if (ssml.length > maxLength)
|
|
2892
|
-
addDiagnostic(diagnostics, ssml, maxLength, `SSML exceeds the maximum length of ${maxLength} characters.`);
|
|
2893
|
-
if (options.maxXmlDepth !== void 0 && (!Number.isInteger(options.maxXmlDepth) || options.maxXmlDepth <= 0)) {
|
|
2894
|
-
addDiagnostic(diagnostics, ssml, 0, "maxXmlDepth must be a positive integer.");
|
|
2895
|
-
}
|
|
2896
|
-
try {
|
|
2897
|
-
parseSsml(ssml);
|
|
2898
|
-
} catch (error) {
|
|
2899
|
-
const message = error instanceof Error ? error.message.replace(/ at position \d+$/, "") : String(error);
|
|
2900
|
-
const match = / at position (\d+)$/.exec(error instanceof Error ? error.message : "");
|
|
2901
|
-
addDiagnostic(diagnostics, ssml, match ? Number(match[1]) : 0, message);
|
|
2902
|
-
return diagnostics;
|
|
2903
|
-
}
|
|
2904
|
-
const tokens = tokenizeElements(ssml);
|
|
2905
|
-
if (options.maxXmlDepth !== void 0) {
|
|
2906
|
-
for (const token of tokens) {
|
|
2907
|
-
if (token.depth > options.maxXmlDepth) {
|
|
2908
|
-
addDiagnostic(
|
|
2909
|
-
diagnostics,
|
|
2910
|
-
ssml,
|
|
2911
|
-
token.start,
|
|
2912
|
-
`XML nesting depth ${token.depth} exceeds the configured maximum of ${options.maxXmlDepth}.`
|
|
2913
|
-
);
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
|
-
}
|
|
2917
|
-
const speak = tokens.find((token) => token.name.toLowerCase() === "speak");
|
|
2918
|
-
const voices = tokens.filter((token) => token.name.toLowerCase() === "voice");
|
|
2919
|
-
const backgroundAudioTokens = tokens.filter((token) => token.name.toLowerCase() === "mstts:backgroundaudio");
|
|
2920
|
-
for (const [index, token] of backgroundAudioTokens.entries()) {
|
|
2921
|
-
if (index > 0)
|
|
2922
|
-
addDiagnostic(
|
|
2923
|
-
diagnostics,
|
|
2924
|
-
ssml,
|
|
2925
|
-
token.start,
|
|
2926
|
-
"An SSML document can contain at most one <mstts:backgroundaudio> element."
|
|
2927
|
-
);
|
|
2928
|
-
}
|
|
2929
|
-
if (!speak || voices.length === 0)
|
|
2930
|
-
addDiagnostic(
|
|
2931
|
-
diagnostics,
|
|
2932
|
-
ssml,
|
|
2933
|
-
speak?.start ?? 0,
|
|
2934
|
-
"Azure SSML requires at least one <voice> element under <speak>."
|
|
2935
|
-
);
|
|
2936
|
-
const voiceName = voices[0] ? attr(voices[0], "name") : void 0;
|
|
2937
|
-
const voiceCatalog = normalizeVoiceCatalog(options);
|
|
2938
|
-
const normalizeLanguage = createLanguageNormalizer(options);
|
|
2939
|
-
const policySeverity = diagnosticSeverity(options.unknownVoicePolicy ?? "warn");
|
|
2940
|
-
const voicesToValidate = options.validateNestedVoices === false ? voices.slice(0, 1) : voices;
|
|
2941
|
-
for (const token of voicesToValidate) {
|
|
2942
|
-
const name = attr(token, "name")?.trim();
|
|
2943
|
-
const language = attr(token, "xml:lang")?.trim() || (speak ? attr(speak, "xml:lang")?.trim() : void 0);
|
|
2944
|
-
const definition = name ? voiceCatalog.get(name.toLowerCase()) : void 0;
|
|
2945
|
-
if (name && definition?.status === "preview")
|
|
2946
|
-
addDiagnostic(
|
|
2947
|
-
diagnostics,
|
|
2948
|
-
ssml,
|
|
2949
|
-
token.start,
|
|
2950
|
-
`Voice "${name}" is an Azure Speech preview voice and may change or require preview access.`,
|
|
2951
|
-
"warning",
|
|
2952
|
-
"azure-preview-voice"
|
|
2953
|
-
);
|
|
2954
|
-
if (name && definition?.status === "deprecated")
|
|
2955
|
-
addDiagnostic(
|
|
2956
|
-
diagnostics,
|
|
2957
|
-
ssml,
|
|
2958
|
-
token.start,
|
|
2959
|
-
`Voice "${name}" is deprecated by Azure Speech; migrate to a supported voice.`,
|
|
2960
|
-
"info",
|
|
2961
|
-
"azure-deprecated-voice"
|
|
2962
|
-
);
|
|
2963
|
-
if (name && !definition && policySeverity)
|
|
2964
|
-
addDiagnostic(
|
|
2965
|
-
diagnostics,
|
|
2966
|
-
ssml,
|
|
2967
|
-
token.start,
|
|
2968
|
-
`Unknown voice "${name}" is not registered in the voice catalog.`,
|
|
2969
|
-
policySeverity,
|
|
2970
|
-
"azure-unknown-voice"
|
|
2971
|
-
);
|
|
2972
|
-
if (name && language && definitionMatchesLanguage(definition, name, language, normalizeLanguage) === false)
|
|
2973
|
-
addDiagnostic(
|
|
2974
|
-
diagnostics,
|
|
2975
|
-
ssml,
|
|
2976
|
-
token.start,
|
|
2977
|
-
`Voice "${name}" does not match language "${language}"; the voice name prefix indicates a different language or region.`,
|
|
2978
|
-
"warning",
|
|
2979
|
-
"azure-locale-mismatch"
|
|
2980
|
-
);
|
|
2981
|
-
}
|
|
2982
|
-
for (const token of tokens) {
|
|
2983
|
-
const tokenName = token.name.toLowerCase();
|
|
2984
|
-
const tokenVoiceName = tokenName === "voice" ? attr(token, "name")?.trim() : tokenName === "mstts:turn" ? attr(token, "voice")?.trim() || token.parentVoiceName : options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
2985
|
-
validateElement(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
2986
|
-
const definition = tokenVoiceName ? voiceCatalog.get(tokenVoiceName.toLowerCase()) : void 0;
|
|
2987
|
-
validateVoiceFeatureMatrix(token, ssml, diagnostics, tokenVoiceName, definition);
|
|
2988
|
-
if (tokenName === "voice" && options.model && definition?.models && !definition.models.some((model) => model.toLowerCase() === options.model?.toLowerCase())) {
|
|
2989
|
-
addDiagnostic(
|
|
2990
|
-
diagnostics,
|
|
2991
|
-
ssml,
|
|
2992
|
-
token.start,
|
|
2993
|
-
`Voice "${tokenVoiceName}" does not support model "${options.model}" according to the configured feature matrix.`,
|
|
2994
|
-
"error",
|
|
2995
|
-
"azure-unsupported-model-for-voice"
|
|
2996
|
-
);
|
|
2997
|
-
}
|
|
2998
|
-
}
|
|
2999
|
-
return diagnostics;
|
|
3000
|
-
}
|
|
3001
|
-
var AZURE_VOICE_CATALOG_METADATA = {
|
|
3002
|
-
apiVersion: "2025-10-01",
|
|
3003
|
-
generatedAt: "2026-08-28T00:00:00.000Z",
|
|
3004
|
-
regions: [],
|
|
3005
|
-
voiceCount: AZURE_VOICE_DEFINITIONS.length
|
|
3006
|
-
};
|
|
3007
|
-
|
|
3008
1538
|
// packages/ssml-editor-react/src/clearSsmlDocument.ts
|
|
3009
1539
|
function getDocumentChildren(document) {
|
|
3010
1540
|
return document.children ?? (document.content === void 0 ? [] : [document.content]);
|
|
@@ -3069,30 +1599,30 @@ var INTRINSICALLY_EMPTY_ELEMENTS = /* @__PURE__ */ new Set([
|
|
|
3069
1599
|
"silence",
|
|
3070
1600
|
"viseme"
|
|
3071
1601
|
]);
|
|
3072
|
-
function
|
|
1602
|
+
function isXmlNameStart(value) {
|
|
3073
1603
|
return value !== void 0 && /[A-Za-z_]/.test(value);
|
|
3074
1604
|
}
|
|
3075
|
-
function
|
|
1605
|
+
function isXmlNameCharacter(value) {
|
|
3076
1606
|
return value !== void 0 && /[A-Za-z0-9_.:-]/.test(value);
|
|
3077
1607
|
}
|
|
3078
|
-
function
|
|
1608
|
+
function isXmlWhitespace(value) {
|
|
3079
1609
|
return value === " " || value === " " || value === "\r" || value === "\n";
|
|
3080
1610
|
}
|
|
3081
1611
|
function fail(message) {
|
|
3082
1612
|
throw new Error(message);
|
|
3083
1613
|
}
|
|
3084
1614
|
function skipWhitespace(source, index) {
|
|
3085
|
-
while (
|
|
1615
|
+
while (isXmlWhitespace(source[index])) {
|
|
3086
1616
|
index += 1;
|
|
3087
1617
|
}
|
|
3088
1618
|
return index;
|
|
3089
1619
|
}
|
|
3090
1620
|
function readName(source, start, end) {
|
|
3091
|
-
if (!
|
|
1621
|
+
if (!isXmlNameStart(source[start])) {
|
|
3092
1622
|
fail("Invalid XML name");
|
|
3093
1623
|
}
|
|
3094
1624
|
let index = start + 1;
|
|
3095
|
-
while (index < end &&
|
|
1625
|
+
while (index < end && isXmlNameCharacter(source[index])) {
|
|
3096
1626
|
index += 1;
|
|
3097
1627
|
}
|
|
3098
1628
|
return { name: source.slice(start, index), index };
|
|
@@ -3128,7 +1658,7 @@ function validateEntityReferences(value) {
|
|
|
3128
1658
|
index = semicolon + 1;
|
|
3129
1659
|
}
|
|
3130
1660
|
}
|
|
3131
|
-
function
|
|
1661
|
+
function findTagEnd2(source, start, hasInternalSubset = false) {
|
|
3132
1662
|
let quote;
|
|
3133
1663
|
let subsetDepth = 0;
|
|
3134
1664
|
for (let index = start + 1; index < source.length; index += 1) {
|
|
@@ -3301,7 +1831,7 @@ function parseXml(source) {
|
|
|
3301
1831
|
continue;
|
|
3302
1832
|
}
|
|
3303
1833
|
if (source.startsWith("</", index)) {
|
|
3304
|
-
const end2 =
|
|
1834
|
+
const end2 = findTagEnd2(source, index);
|
|
3305
1835
|
const name2 = parseClosingTag(source, index, end2);
|
|
3306
1836
|
const element = currentElement(stack);
|
|
3307
1837
|
if (element === void 0 || element.name !== name2) {
|
|
@@ -3313,7 +1843,7 @@ function parseXml(source) {
|
|
|
3313
1843
|
continue;
|
|
3314
1844
|
}
|
|
3315
1845
|
if (source.startsWith("<!", index)) {
|
|
3316
|
-
const end2 =
|
|
1846
|
+
const end2 = findTagEnd2(source, index, true);
|
|
3317
1847
|
parseDeclaration(source, index, end2);
|
|
3318
1848
|
appendNode2(currentElement(stack)?.children ?? children, {
|
|
3319
1849
|
kind: "declaration",
|
|
@@ -3322,7 +1852,7 @@ function parseXml(source) {
|
|
|
3322
1852
|
index = end2 + 1;
|
|
3323
1853
|
continue;
|
|
3324
1854
|
}
|
|
3325
|
-
const end =
|
|
1855
|
+
const end = findTagEnd2(source, index);
|
|
3326
1856
|
const { name, selfClosing } = parseStartTag(source, index, end);
|
|
3327
1857
|
if (stack.length === 0) {
|
|
3328
1858
|
if (root !== void 0) {
|
|
@@ -4243,13 +2773,13 @@ for (const definition of SSML_TAG_DEFINITIONS) {
|
|
|
4243
2773
|
definitionsByName.set(alias, definition);
|
|
4244
2774
|
}
|
|
4245
2775
|
}
|
|
4246
|
-
function
|
|
2776
|
+
function isXmlNameStart2(value) {
|
|
4247
2777
|
return value !== void 0 && /[A-Za-z_]/.test(value);
|
|
4248
2778
|
}
|
|
4249
|
-
function
|
|
2779
|
+
function isXmlNameCharacter2(value) {
|
|
4250
2780
|
return value !== void 0 && /[A-Za-z0-9_.:-]/.test(value);
|
|
4251
2781
|
}
|
|
4252
|
-
function
|
|
2782
|
+
function isXmlWhitespace2(value) {
|
|
4253
2783
|
return value === " " || value === " " || value === "\r" || value === "\n";
|
|
4254
2784
|
}
|
|
4255
2785
|
function positionToOffset(source, lineNumber, column) {
|
|
@@ -4292,7 +2822,7 @@ function toRange(source, token) {
|
|
|
4292
2822
|
function containsOffset(token, offset) {
|
|
4293
2823
|
return offset >= token.start && offset < token.end;
|
|
4294
2824
|
}
|
|
4295
|
-
function
|
|
2825
|
+
function findTagEnd3(source, start) {
|
|
4296
2826
|
let quote;
|
|
4297
2827
|
for (let index = start; index < source.length; index += 1) {
|
|
4298
2828
|
const character = source[index];
|
|
@@ -4315,44 +2845,44 @@ function parseTag(source, start, contentEnd, tokenEnd) {
|
|
|
4315
2845
|
closing = true;
|
|
4316
2846
|
index += 1;
|
|
4317
2847
|
}
|
|
4318
|
-
while (index < contentEnd &&
|
|
2848
|
+
while (index < contentEnd && isXmlWhitespace2(source[index])) {
|
|
4319
2849
|
index += 1;
|
|
4320
2850
|
}
|
|
4321
|
-
if (!
|
|
2851
|
+
if (!isXmlNameStart2(source[index])) {
|
|
4322
2852
|
return void 0;
|
|
4323
2853
|
}
|
|
4324
2854
|
const nameStart = index;
|
|
4325
2855
|
index += 1;
|
|
4326
|
-
while (index < contentEnd &&
|
|
2856
|
+
while (index < contentEnd && isXmlNameCharacter2(source[index])) {
|
|
4327
2857
|
index += 1;
|
|
4328
2858
|
}
|
|
4329
2859
|
const nameEnd = index;
|
|
4330
2860
|
const attributes = [];
|
|
4331
2861
|
if (!closing) {
|
|
4332
2862
|
while (index < contentEnd) {
|
|
4333
|
-
while (index < contentEnd &&
|
|
2863
|
+
while (index < contentEnd && isXmlWhitespace2(source[index])) {
|
|
4334
2864
|
index += 1;
|
|
4335
2865
|
}
|
|
4336
2866
|
if (index >= contentEnd || source[index] === "/") {
|
|
4337
2867
|
break;
|
|
4338
2868
|
}
|
|
4339
|
-
if (!
|
|
2869
|
+
if (!isXmlNameStart2(source[index])) {
|
|
4340
2870
|
index += 1;
|
|
4341
2871
|
continue;
|
|
4342
2872
|
}
|
|
4343
2873
|
const attributeStart = index;
|
|
4344
2874
|
index += 1;
|
|
4345
|
-
while (index < contentEnd &&
|
|
2875
|
+
while (index < contentEnd && isXmlNameCharacter2(source[index])) {
|
|
4346
2876
|
index += 1;
|
|
4347
2877
|
}
|
|
4348
2878
|
const attributeEnd = index;
|
|
4349
|
-
while (index < contentEnd &&
|
|
2879
|
+
while (index < contentEnd && isXmlWhitespace2(source[index])) {
|
|
4350
2880
|
index += 1;
|
|
4351
2881
|
}
|
|
4352
2882
|
let value;
|
|
4353
2883
|
if (source[index] === "=") {
|
|
4354
2884
|
index += 1;
|
|
4355
|
-
while (index < contentEnd &&
|
|
2885
|
+
while (index < contentEnd && isXmlWhitespace2(source[index])) {
|
|
4356
2886
|
index += 1;
|
|
4357
2887
|
}
|
|
4358
2888
|
const quote = source[index];
|
|
@@ -4368,7 +2898,7 @@ function parseTag(source, start, contentEnd, tokenEnd) {
|
|
|
4368
2898
|
}
|
|
4369
2899
|
} else {
|
|
4370
2900
|
const valueStart = index;
|
|
4371
|
-
while (index < contentEnd && !
|
|
2901
|
+
while (index < contentEnd && !isXmlWhitespace2(source[index]) && source[index] !== "/") {
|
|
4372
2902
|
index += 1;
|
|
4373
2903
|
}
|
|
4374
2904
|
value = { start: valueStart, end: index };
|
|
@@ -4423,7 +2953,7 @@ function findTagAtOffset(source, offset) {
|
|
|
4423
2953
|
searchStart = tokenEnd2;
|
|
4424
2954
|
continue;
|
|
4425
2955
|
}
|
|
4426
|
-
const tagEnd =
|
|
2956
|
+
const tagEnd = findTagEnd3(source, start + 1);
|
|
4427
2957
|
const contentEnd = tagEnd ?? source.length;
|
|
4428
2958
|
const tokenEnd = tagEnd === void 0 ? source.length : tagEnd + 1;
|
|
4429
2959
|
if (offset < tokenEnd) {
|
|
@@ -4562,17 +3092,13 @@ export {
|
|
|
4562
3092
|
SAY_AS_DESCRIPTIONS,
|
|
4563
3093
|
LANGUAGE_DESCRIPTIONS,
|
|
4564
3094
|
SILENCE_VALUE_DESCRIPTIONS,
|
|
3095
|
+
SSML_ELEMENT_COPY,
|
|
4565
3096
|
EDITOR_COPY,
|
|
4566
3097
|
INLINE_BADGE_COPY,
|
|
4567
3098
|
SSML_HOVER_COPY,
|
|
4568
3099
|
DEFAULT_LOCALE,
|
|
4569
3100
|
OVERLAY_Z_INDEX,
|
|
4570
3101
|
SELECTION_OVERLAY_ABOVE_THRESHOLD_LINES,
|
|
4571
|
-
buildSsml,
|
|
4572
|
-
parseSsml,
|
|
4573
|
-
buildPartialSsml,
|
|
4574
|
-
validateSsml,
|
|
4575
|
-
validateAzureSsml,
|
|
4576
3102
|
clearSsmlDocument,
|
|
4577
3103
|
formatXmlFragment,
|
|
4578
3104
|
getEditableRegion,
|
|
@@ -4586,4 +3112,4 @@ export {
|
|
|
4586
3112
|
findSsmlHoverTarget,
|
|
4587
3113
|
formatSsmlHover
|
|
4588
3114
|
};
|
|
4589
|
-
//# sourceMappingURL=chunk-
|
|
3115
|
+
//# sourceMappingURL=chunk-LCTE26LS.mjs.map
|