ssml-builder-js 2.8.0 → 2.9.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 +95 -17
- package/dist/{chunk-TKD3PU5Y.mjs → chunk-7LCSH4SZ.mjs} +610 -124
- package/dist/chunk-7LCSH4SZ.mjs.map +1 -0
- package/dist/{chunk-4RQETJUI.mjs → chunk-JNJVTEL6.mjs} +939 -10
- package/dist/chunk-JNJVTEL6.mjs.map +1 -0
- package/dist/core.d.mts +97 -3
- package/dist/core.d.ts +97 -3
- package/dist/core.js +616 -124
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +15 -3
- package/dist/elements.d.mts +10 -1
- package/dist/elements.d.ts +10 -1
- package/dist/elements.js +1245 -15
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +316 -9
- package/dist/elements.mjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +616 -124
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +15 -3
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +47 -6
- package/dist/react.d.ts +47 -6
- package/dist/react.js +1219 -25
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +280 -10
- package/dist/react.mjs.map +1 -1
- package/package.json +4 -3
- package/dist/chunk-4RQETJUI.mjs.map +0 -1
- package/dist/chunk-TKD3PU5Y.mjs.map +0 -1
|
@@ -213,6 +213,13 @@ var SSML_HOVER_COPY = {
|
|
|
213
213
|
parameters: {
|
|
214
214
|
type: { title: "type", description: "\u30D3\u30BC\u30FC\u30E0\u30A4\u30D9\u30F3\u30C8\u306E\u5F62\u5F0F\u3002" }
|
|
215
215
|
}
|
|
216
|
+
},
|
|
217
|
+
"mstts:audioduration": {
|
|
218
|
+
title: "\u97F3\u58F0\u9577",
|
|
219
|
+
description: "\u5408\u6210\u97F3\u58F0\u306E\u76EE\u6A19\u6642\u9593\u3092\u6307\u5B9A\u3057\u307E\u3059\u3002",
|
|
220
|
+
parameters: {
|
|
221
|
+
value: { title: "value", description: "\u76EE\u6A19\u6642\u9593\u3002\u4F8B: `10s`\u3001`5000ms`\u3001`00:00:10`\u3002" }
|
|
222
|
+
}
|
|
216
223
|
}
|
|
217
224
|
}
|
|
218
225
|
},
|
|
@@ -370,6 +377,13 @@ var SSML_HOVER_COPY = {
|
|
|
370
377
|
parameters: {
|
|
371
378
|
type: { title: "type", description: "The viseme event format." }
|
|
372
379
|
}
|
|
380
|
+
},
|
|
381
|
+
"mstts:audioduration": {
|
|
382
|
+
title: "Audio duration",
|
|
383
|
+
description: "Sets the target duration of synthesized audio.",
|
|
384
|
+
parameters: {
|
|
385
|
+
value: { title: "value", description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`." }
|
|
386
|
+
}
|
|
373
387
|
}
|
|
374
388
|
}
|
|
375
389
|
}
|
|
@@ -526,6 +540,8 @@ function findSsmlVoiceContext(source, offset) {
|
|
|
526
540
|
// packages/ssml-editor-react/src/constants/ssmlPresets.ts
|
|
527
541
|
var ssmlPresets_exports = {};
|
|
528
542
|
__export(ssmlPresets_exports, {
|
|
543
|
+
AUDIO_DURATION_DESCRIPTIONS: () => AUDIO_DURATION_DESCRIPTIONS,
|
|
544
|
+
AUDIO_DURATION_PRESETS: () => AUDIO_DURATION_PRESETS,
|
|
529
545
|
BREAK_STRENGTH_PRESETS: () => BREAK_STRENGTH_PRESETS,
|
|
530
546
|
BREAK_TIME_DESCRIPTIONS: () => BREAK_TIME_DESCRIPTIONS,
|
|
531
547
|
BREAK_TIME_PRESETS: () => BREAK_TIME_PRESETS,
|
|
@@ -558,6 +574,103 @@ __export(ssmlPresets_exports, {
|
|
|
558
574
|
getExpressAsStyleCategory: () => getExpressAsStyleCategory,
|
|
559
575
|
resolveExpressAsStyles: () => resolveExpressAsStyles
|
|
560
576
|
});
|
|
577
|
+
|
|
578
|
+
// packages/ssml-editor-react/src/constants/azureVoiceStyleMap.generated.ts
|
|
579
|
+
var AZURE_VOICE_STYLE_MAP = {
|
|
580
|
+
"de-DE-ConradNeural": ["cheerful", "sad"],
|
|
581
|
+
"de-DE-KatjaNeural": ["cheerful", "sad"],
|
|
582
|
+
"en-US-AndrewNeural": ["empathetic", "relieved"],
|
|
583
|
+
"en-US-GuyNeural": [
|
|
584
|
+
"angry",
|
|
585
|
+
"cheerful",
|
|
586
|
+
"excited",
|
|
587
|
+
"friendly",
|
|
588
|
+
"hopeful",
|
|
589
|
+
"newscast",
|
|
590
|
+
"sad",
|
|
591
|
+
"shouting",
|
|
592
|
+
"terrified",
|
|
593
|
+
"unfriendly",
|
|
594
|
+
"whispering"
|
|
595
|
+
],
|
|
596
|
+
"en-US-JennyMultilingualNeural": [
|
|
597
|
+
"cheerful",
|
|
598
|
+
"empathetic",
|
|
599
|
+
"excited",
|
|
600
|
+
"friendly",
|
|
601
|
+
"hopeful",
|
|
602
|
+
"sad",
|
|
603
|
+
"shouting",
|
|
604
|
+
"terrified",
|
|
605
|
+
"unfriendly",
|
|
606
|
+
"whispering"
|
|
607
|
+
],
|
|
608
|
+
"en-US-JennyNeural": [
|
|
609
|
+
"assistant",
|
|
610
|
+
"chat",
|
|
611
|
+
"customerservice",
|
|
612
|
+
"newscast",
|
|
613
|
+
"cheerful",
|
|
614
|
+
"empathetic",
|
|
615
|
+
"excited",
|
|
616
|
+
"friendly",
|
|
617
|
+
"hopeful",
|
|
618
|
+
"sad",
|
|
619
|
+
"shouting",
|
|
620
|
+
"terrified",
|
|
621
|
+
"unfriendly",
|
|
622
|
+
"whispering"
|
|
623
|
+
],
|
|
624
|
+
"es-ES-ElviraNeural": [],
|
|
625
|
+
"fil-PH-AngeloNeural": [],
|
|
626
|
+
"fr-FR-DeniseNeural": ["cheerful", "sad"],
|
|
627
|
+
"fr-FR-HenriNeural": ["cheerful", "sad"],
|
|
628
|
+
"id-ID-GadisNeural": [],
|
|
629
|
+
"it-IT-ElsaNeural": ["cheerful", "sad"],
|
|
630
|
+
"ja-JP-KeitaNeural": ["chat"],
|
|
631
|
+
"ja-JP-MayuNeural": ["calm", "cheerful", "sad"],
|
|
632
|
+
"ja-JP-NanamiNeural": ["chat", "customerservice", "cheerful", "whispering", "sad"],
|
|
633
|
+
"ko-KR-SunHiNeural": ["cheerful", "sad"],
|
|
634
|
+
"ms-MY-YasminNeural": [],
|
|
635
|
+
"pt-BR-FranciscaNeural": ["calm"],
|
|
636
|
+
"ru-RU-SvetlanaNeural": ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"],
|
|
637
|
+
"th-TH-PremwadeeNeural": [],
|
|
638
|
+
"vi-VN-HoaiMyNeural": [],
|
|
639
|
+
"zh-CN-XiaoxiaoNeural": [
|
|
640
|
+
"assistant",
|
|
641
|
+
"chat",
|
|
642
|
+
"customerservice",
|
|
643
|
+
"newscast",
|
|
644
|
+
"cheerful",
|
|
645
|
+
"empathetic",
|
|
646
|
+
"excited",
|
|
647
|
+
"friendly",
|
|
648
|
+
"hopeful",
|
|
649
|
+
"sad",
|
|
650
|
+
"terrified",
|
|
651
|
+
"whispering",
|
|
652
|
+
"poetry-reading",
|
|
653
|
+
"sports_commentary",
|
|
654
|
+
"sports_commentary_excited",
|
|
655
|
+
"story"
|
|
656
|
+
],
|
|
657
|
+
"zh-CN-YunxiNeural": [
|
|
658
|
+
"narration-relaxed",
|
|
659
|
+
"embarrassed",
|
|
660
|
+
"fearful",
|
|
661
|
+
"sad",
|
|
662
|
+
"disgruntled",
|
|
663
|
+
"serious",
|
|
664
|
+
"angry",
|
|
665
|
+
"depressed",
|
|
666
|
+
"chat",
|
|
667
|
+
"cheerful",
|
|
668
|
+
"assistant"
|
|
669
|
+
],
|
|
670
|
+
"zh-TW-HsiaoChenNeural": []
|
|
671
|
+
};
|
|
672
|
+
|
|
673
|
+
// packages/ssml-editor-react/src/constants/ssmlPresets.ts
|
|
561
674
|
var SSML_PRESETS = [
|
|
562
675
|
{
|
|
563
676
|
id: "basic",
|
|
@@ -657,7 +770,7 @@ function getExpressAsStyleCategory(style) {
|
|
|
657
770
|
}
|
|
658
771
|
return "other";
|
|
659
772
|
}
|
|
660
|
-
var
|
|
773
|
+
var LEGACY_VOICE_STYLE_MAP = {
|
|
661
774
|
"ja-JP-MayuNeural": ["calm", "cheerful", "sad"],
|
|
662
775
|
"ja-JP-KeitaNeural": ["chat"],
|
|
663
776
|
"ja-JP-NanamiNeural": ["chat", "customerservice", "cheerful", "whispering", "sad"],
|
|
@@ -743,6 +856,7 @@ var VOICE_STYLE_MAP = {
|
|
|
743
856
|
"de-DE-ConradNeural": ["cheerful", "sad"],
|
|
744
857
|
"ru-RU-SvetlanaNeural": ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"]
|
|
745
858
|
};
|
|
859
|
+
var VOICE_STYLE_MAP = Object.keys(AZURE_VOICE_STYLE_MAP).length > 0 ? AZURE_VOICE_STYLE_MAP : LEGACY_VOICE_STYLE_MAP;
|
|
746
860
|
var VOICE_STYLE_MAP_BY_NORMALIZED_NAME = new Map(
|
|
747
861
|
Object.entries(VOICE_STYLE_MAP).map(([voiceName, styles]) => [voiceName.toLowerCase(), styles])
|
|
748
862
|
);
|
|
@@ -785,6 +899,7 @@ var SAY_AS_PRESETS = [
|
|
|
785
899
|
];
|
|
786
900
|
var LANGUAGE_PRESETS = ["ja-JP", "en-US", "de-DE", "fr-FR"];
|
|
787
901
|
var SILENCE_VALUE_PRESETS = ["300ms", "500ms", "1s"];
|
|
902
|
+
var AUDIO_DURATION_PRESETS = ["5s", "10s", "30s"];
|
|
788
903
|
var SILENCE_TYPE_PRESETS = [
|
|
789
904
|
"Leading",
|
|
790
905
|
"Tailing",
|
|
@@ -793,6 +908,11 @@ var SILENCE_TYPE_PRESETS = [
|
|
|
793
908
|
"Semicolon",
|
|
794
909
|
"Enumerationcomma"
|
|
795
910
|
];
|
|
911
|
+
var AUDIO_DURATION_DESCRIPTIONS = {
|
|
912
|
+
"5s": { ja: "5\u79D2", en: "5 seconds" },
|
|
913
|
+
"10s": { ja: "10\u79D2", en: "10 seconds" },
|
|
914
|
+
"30s": { ja: "30\u79D2", en: "30 seconds" }
|
|
915
|
+
};
|
|
796
916
|
var PHONEME_ALPHABET_PRESETS = ["ipa", "sapi", "ups", "x-sampa"];
|
|
797
917
|
var VISEME_TYPE_PRESETS = ["redlips_front", "FacialExpression"];
|
|
798
918
|
var SSML_ATTRIBUTE_PRESETS = {
|
|
@@ -836,6 +956,14 @@ var SSML_ATTRIBUTE_PRESETS = {
|
|
|
836
956
|
type: SILENCE_TYPE_PRESETS,
|
|
837
957
|
value: SILENCE_VALUE_PRESETS
|
|
838
958
|
},
|
|
959
|
+
"mstts:audioduration": {
|
|
960
|
+
value: AUDIO_DURATION_PRESETS
|
|
961
|
+
},
|
|
962
|
+
"mstts:backgroundaudio": {
|
|
963
|
+
volume: PROSODY_VOLUME_PRESETS,
|
|
964
|
+
fadein: AUDIO_DURATION_PRESETS,
|
|
965
|
+
fadeout: AUDIO_DURATION_PRESETS
|
|
966
|
+
},
|
|
839
967
|
silence: {
|
|
840
968
|
type: SILENCE_TYPE_PRESETS,
|
|
841
969
|
value: SILENCE_VALUE_PRESETS
|
|
@@ -1219,7 +1347,14 @@ var SSML_TAGS = {
|
|
|
1219
1347
|
MSTTS_SILENCE: "mstts:silence",
|
|
1220
1348
|
SILENCE: "silence",
|
|
1221
1349
|
MSTTS_VISEME: "mstts:viseme",
|
|
1222
|
-
VISEME: "viseme"
|
|
1350
|
+
VISEME: "viseme",
|
|
1351
|
+
MSTTS_AUDIO_DURATION: "mstts:audioduration",
|
|
1352
|
+
MSTTS_DIALOG: "mstts:dialog",
|
|
1353
|
+
MSTTS_TURN: "mstts:turn",
|
|
1354
|
+
MSTTS_BACKGROUND_AUDIO: "mstts:backgroundaudio",
|
|
1355
|
+
MSTTS_TTS_EMBEDDING: "mstts:ttsembedding",
|
|
1356
|
+
MSTTS_EMBEDDING: "mstts:embedding",
|
|
1357
|
+
MSTTS_VOICE_CONVERSION: "mstts:voiceconversion"
|
|
1223
1358
|
};
|
|
1224
1359
|
var SSML_ATTRS = {
|
|
1225
1360
|
VERSION: "version",
|
|
@@ -1228,6 +1363,7 @@ var SSML_ATTRS = {
|
|
|
1228
1363
|
LANG: "lang",
|
|
1229
1364
|
MSTTS_XMLNS: "xmlns:mstts",
|
|
1230
1365
|
NAME: "name",
|
|
1366
|
+
VOICE: "voice",
|
|
1231
1367
|
EFFECT: "effect",
|
|
1232
1368
|
RATE: "rate",
|
|
1233
1369
|
PITCH: "pitch",
|
|
@@ -1259,7 +1395,9 @@ var SSML_ATTRS = {
|
|
|
1259
1395
|
MARK: "mark",
|
|
1260
1396
|
URI: "uri",
|
|
1261
1397
|
TYPE: "type",
|
|
1262
|
-
VALUE: "value"
|
|
1398
|
+
VALUE: "value",
|
|
1399
|
+
FADE_IN: "fadein",
|
|
1400
|
+
FADE_OUT: "fadeout"
|
|
1263
1401
|
};
|
|
1264
1402
|
function escapeText(value) {
|
|
1265
1403
|
return value.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
@@ -1346,6 +1484,23 @@ function getAttributes(element) {
|
|
|
1346
1484
|
case SSML_TAGS.VISEME:
|
|
1347
1485
|
addAttribute(attributes, SSML_ATTRS.TYPE, element.typeValue ?? element.visemeType);
|
|
1348
1486
|
break;
|
|
1487
|
+
case SSML_TAGS.MSTTS_AUDIO_DURATION:
|
|
1488
|
+
addAttribute(attributes, SSML_ATTRS.VALUE, element.value);
|
|
1489
|
+
break;
|
|
1490
|
+
case SSML_TAGS.MSTTS_TURN:
|
|
1491
|
+
addAttribute(attributes, SSML_ATTRS.VOICE, element.voice);
|
|
1492
|
+
break;
|
|
1493
|
+
case SSML_TAGS.MSTTS_BACKGROUND_AUDIO:
|
|
1494
|
+
addAttribute(attributes, SSML_ATTRS.SRC, element.src);
|
|
1495
|
+
addAttribute(attributes, SSML_ATTRS.VOLUME, element.volume);
|
|
1496
|
+
addAttribute(attributes, SSML_ATTRS.FADE_IN, element.fadeIn ?? element.fadein);
|
|
1497
|
+
addAttribute(attributes, SSML_ATTRS.FADE_OUT, element.fadeOut ?? element.fadeout);
|
|
1498
|
+
break;
|
|
1499
|
+
case SSML_TAGS.MSTTS_DIALOG:
|
|
1500
|
+
case SSML_TAGS.MSTTS_TTS_EMBEDDING:
|
|
1501
|
+
case SSML_TAGS.MSTTS_EMBEDDING:
|
|
1502
|
+
case SSML_TAGS.MSTTS_VOICE_CONVERSION:
|
|
1503
|
+
break;
|
|
1349
1504
|
case SSML_TAGS.PARAGRAPH:
|
|
1350
1505
|
case SSML_TAGS.SENTENCE:
|
|
1351
1506
|
case SSML_TAGS.WORD:
|
|
@@ -1370,6 +1525,8 @@ function getTagName(element) {
|
|
|
1370
1525
|
case SSML_TAGS.VISEME:
|
|
1371
1526
|
case SSML_TAGS.MSTTS_VISEME:
|
|
1372
1527
|
return SSML_TAGS.MSTTS_VISEME;
|
|
1528
|
+
case SSML_TAGS.MSTTS_AUDIO_DURATION:
|
|
1529
|
+
return SSML_TAGS.MSTTS_AUDIO_DURATION;
|
|
1373
1530
|
case "element":
|
|
1374
1531
|
case "custom":
|
|
1375
1532
|
return element.name;
|
|
@@ -1902,6 +2059,46 @@ function convertElement(node) {
|
|
|
1902
2059
|
if (typeValue !== void 0) element.typeValue = typeValue;
|
|
1903
2060
|
return finishElement(element, node, attributes);
|
|
1904
2061
|
}
|
|
2062
|
+
case SSML_TAGS.MSTTS_AUDIO_DURATION: {
|
|
2063
|
+
const element = { type: SSML_TAGS.MSTTS_AUDIO_DURATION };
|
|
2064
|
+
const value = readAttribute(attributes, SSML_ATTRS.VALUE);
|
|
2065
|
+
if (value !== void 0) element.value = value;
|
|
2066
|
+
return finishElement(element, node, attributes);
|
|
2067
|
+
}
|
|
2068
|
+
case SSML_TAGS.MSTTS_DIALOG: {
|
|
2069
|
+
const element = { type: SSML_TAGS.MSTTS_DIALOG };
|
|
2070
|
+
return finishElement(element, node, attributes);
|
|
2071
|
+
}
|
|
2072
|
+
case SSML_TAGS.MSTTS_TURN: {
|
|
2073
|
+
const element = { type: SSML_TAGS.MSTTS_TURN };
|
|
2074
|
+
const voice = readAttribute(attributes, SSML_ATTRS.VOICE);
|
|
2075
|
+
if (voice !== void 0) element.voice = voice;
|
|
2076
|
+
return finishElement(element, node, attributes);
|
|
2077
|
+
}
|
|
2078
|
+
case SSML_TAGS.MSTTS_BACKGROUND_AUDIO: {
|
|
2079
|
+
const element = { type: SSML_TAGS.MSTTS_BACKGROUND_AUDIO };
|
|
2080
|
+
const src = readAttribute(attributes, SSML_ATTRS.SRC);
|
|
2081
|
+
const volume = readAttribute(attributes, SSML_ATTRS.VOLUME);
|
|
2082
|
+
const fadeIn = readAttribute(attributes, SSML_ATTRS.FADE_IN);
|
|
2083
|
+
const fadeOut = readAttribute(attributes, SSML_ATTRS.FADE_OUT);
|
|
2084
|
+
if (src !== void 0) element.src = src;
|
|
2085
|
+
if (volume !== void 0) element.volume = volume;
|
|
2086
|
+
if (fadeIn !== void 0) element.fadeIn = fadeIn;
|
|
2087
|
+
if (fadeOut !== void 0) element.fadeOut = fadeOut;
|
|
2088
|
+
return finishElement(element, node, attributes);
|
|
2089
|
+
}
|
|
2090
|
+
case SSML_TAGS.MSTTS_TTS_EMBEDDING: {
|
|
2091
|
+
const element = { type: SSML_TAGS.MSTTS_TTS_EMBEDDING };
|
|
2092
|
+
return finishElement(element, node, attributes);
|
|
2093
|
+
}
|
|
2094
|
+
case SSML_TAGS.MSTTS_EMBEDDING: {
|
|
2095
|
+
const element = { type: SSML_TAGS.MSTTS_EMBEDDING };
|
|
2096
|
+
return finishElement(element, node, attributes);
|
|
2097
|
+
}
|
|
2098
|
+
case SSML_TAGS.MSTTS_VOICE_CONVERSION: {
|
|
2099
|
+
const element = { type: SSML_TAGS.MSTTS_VOICE_CONVERSION };
|
|
2100
|
+
return finishElement(element, node, attributes);
|
|
2101
|
+
}
|
|
1905
2102
|
default: {
|
|
1906
2103
|
const element = {
|
|
1907
2104
|
name: node.name,
|
|
@@ -2022,6 +2219,644 @@ function validateSsml(xmlString) {
|
|
|
2022
2219
|
};
|
|
2023
2220
|
}
|
|
2024
2221
|
}
|
|
2222
|
+
var AZURE_VOICE_DEFINITIONS = [
|
|
2223
|
+
{ name: "de-DE-ConradNeural", locale: "de-DE", styles: ["cheerful", "sad"] },
|
|
2224
|
+
{ name: "de-DE-KatjaNeural", locale: "de-DE", styles: ["cheerful", "sad"] },
|
|
2225
|
+
{ name: "en-US-AndrewNeural", locale: "en-US", styles: ["empathetic", "relieved"] },
|
|
2226
|
+
{
|
|
2227
|
+
name: "en-US-GuyNeural",
|
|
2228
|
+
locale: "en-US",
|
|
2229
|
+
styles: [
|
|
2230
|
+
"angry",
|
|
2231
|
+
"cheerful",
|
|
2232
|
+
"excited",
|
|
2233
|
+
"friendly",
|
|
2234
|
+
"hopeful",
|
|
2235
|
+
"newscast",
|
|
2236
|
+
"sad",
|
|
2237
|
+
"shouting",
|
|
2238
|
+
"terrified",
|
|
2239
|
+
"unfriendly",
|
|
2240
|
+
"whispering"
|
|
2241
|
+
]
|
|
2242
|
+
},
|
|
2243
|
+
{
|
|
2244
|
+
name: "en-US-JennyMultilingualNeural",
|
|
2245
|
+
locale: "en-US",
|
|
2246
|
+
styles: [
|
|
2247
|
+
"cheerful",
|
|
2248
|
+
"empathetic",
|
|
2249
|
+
"excited",
|
|
2250
|
+
"friendly",
|
|
2251
|
+
"hopeful",
|
|
2252
|
+
"sad",
|
|
2253
|
+
"shouting",
|
|
2254
|
+
"terrified",
|
|
2255
|
+
"unfriendly",
|
|
2256
|
+
"whispering"
|
|
2257
|
+
]
|
|
2258
|
+
},
|
|
2259
|
+
{
|
|
2260
|
+
name: "en-US-JennyNeural",
|
|
2261
|
+
locale: "en-US",
|
|
2262
|
+
styles: [
|
|
2263
|
+
"assistant",
|
|
2264
|
+
"chat",
|
|
2265
|
+
"customerservice",
|
|
2266
|
+
"newscast",
|
|
2267
|
+
"cheerful",
|
|
2268
|
+
"empathetic",
|
|
2269
|
+
"excited",
|
|
2270
|
+
"friendly",
|
|
2271
|
+
"hopeful",
|
|
2272
|
+
"sad",
|
|
2273
|
+
"shouting",
|
|
2274
|
+
"terrified",
|
|
2275
|
+
"unfriendly",
|
|
2276
|
+
"whispering"
|
|
2277
|
+
]
|
|
2278
|
+
},
|
|
2279
|
+
{ name: "es-ES-ElviraNeural", locale: "es-ES" },
|
|
2280
|
+
{ name: "fil-PH-AngeloNeural", locale: "fil-PH" },
|
|
2281
|
+
{ name: "fr-FR-DeniseNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
|
|
2282
|
+
{ name: "fr-FR-HenriNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
|
|
2283
|
+
{ name: "id-ID-GadisNeural", locale: "id-ID" },
|
|
2284
|
+
{ name: "it-IT-ElsaNeural", locale: "it-IT", styles: ["cheerful", "sad"] },
|
|
2285
|
+
{ name: "ja-JP-KeitaNeural", locale: "ja-JP", styles: ["chat"] },
|
|
2286
|
+
{ name: "ja-JP-MayuNeural", locale: "ja-JP", styles: ["calm", "cheerful", "sad"] },
|
|
2287
|
+
{ name: "ja-JP-NanamiNeural", locale: "ja-JP", styles: ["chat", "customerservice", "cheerful", "whispering", "sad"] },
|
|
2288
|
+
{ name: "ko-KR-SunHiNeural", locale: "ko-KR", styles: ["cheerful", "sad"] },
|
|
2289
|
+
{ name: "ms-MY-YasminNeural", locale: "ms-MY" },
|
|
2290
|
+
{ name: "pt-BR-FranciscaNeural", locale: "pt-BR", styles: ["calm"] },
|
|
2291
|
+
{
|
|
2292
|
+
name: "ru-RU-SvetlanaNeural",
|
|
2293
|
+
locale: "ru-RU",
|
|
2294
|
+
styles: ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"]
|
|
2295
|
+
},
|
|
2296
|
+
{ name: "th-TH-PremwadeeNeural", locale: "th-TH" },
|
|
2297
|
+
{ name: "vi-VN-HoaiMyNeural", locale: "vi-VN" },
|
|
2298
|
+
{
|
|
2299
|
+
name: "zh-CN-XiaoxiaoNeural",
|
|
2300
|
+
locale: "zh-CN",
|
|
2301
|
+
styles: [
|
|
2302
|
+
"assistant",
|
|
2303
|
+
"chat",
|
|
2304
|
+
"customerservice",
|
|
2305
|
+
"newscast",
|
|
2306
|
+
"cheerful",
|
|
2307
|
+
"empathetic",
|
|
2308
|
+
"excited",
|
|
2309
|
+
"friendly",
|
|
2310
|
+
"hopeful",
|
|
2311
|
+
"sad",
|
|
2312
|
+
"terrified",
|
|
2313
|
+
"whispering",
|
|
2314
|
+
"poetry-reading",
|
|
2315
|
+
"sports_commentary",
|
|
2316
|
+
"sports_commentary_excited",
|
|
2317
|
+
"story"
|
|
2318
|
+
]
|
|
2319
|
+
},
|
|
2320
|
+
{
|
|
2321
|
+
name: "zh-CN-YunxiNeural",
|
|
2322
|
+
locale: "zh-CN",
|
|
2323
|
+
styles: [
|
|
2324
|
+
"narration-relaxed",
|
|
2325
|
+
"embarrassed",
|
|
2326
|
+
"fearful",
|
|
2327
|
+
"sad",
|
|
2328
|
+
"disgruntled",
|
|
2329
|
+
"serious",
|
|
2330
|
+
"angry",
|
|
2331
|
+
"depressed",
|
|
2332
|
+
"chat",
|
|
2333
|
+
"cheerful",
|
|
2334
|
+
"assistant"
|
|
2335
|
+
]
|
|
2336
|
+
},
|
|
2337
|
+
{ name: "zh-TW-HsiaoChenNeural", locale: "zh-TW" }
|
|
2338
|
+
];
|
|
2339
|
+
var ALLOWED_BREAK_STRENGTHS = /* @__PURE__ */ new Set(["none", "x-weak", "weak", "medium", "strong", "x-strong"]);
|
|
2340
|
+
var ALLOWED_SAY_AS = /* @__PURE__ */ new Set([
|
|
2341
|
+
"characters",
|
|
2342
|
+
"spell-out",
|
|
2343
|
+
"cardinal",
|
|
2344
|
+
"ordinal",
|
|
2345
|
+
"number",
|
|
2346
|
+
"date",
|
|
2347
|
+
"time",
|
|
2348
|
+
"telephone",
|
|
2349
|
+
"fraction",
|
|
2350
|
+
"address",
|
|
2351
|
+
"name",
|
|
2352
|
+
"currency",
|
|
2353
|
+
"number_digit"
|
|
2354
|
+
]);
|
|
2355
|
+
var ALLOWED_ROLES = /* @__PURE__ */ new Set([
|
|
2356
|
+
"Girl",
|
|
2357
|
+
"Boy",
|
|
2358
|
+
"YoungAdultFemale",
|
|
2359
|
+
"YoungAdultMale",
|
|
2360
|
+
"OlderAdultFemale",
|
|
2361
|
+
"OlderAdultMale",
|
|
2362
|
+
"SeniorFemale",
|
|
2363
|
+
"SeniorMale"
|
|
2364
|
+
]);
|
|
2365
|
+
var ALLOWED_EMPHASIS_LEVELS = /* @__PURE__ */ new Set(["strong", "moderate", "reduced", "none"]);
|
|
2366
|
+
var ALLOWED_SILENCE_TYPES = /* @__PURE__ */ new Set([
|
|
2367
|
+
"Leading",
|
|
2368
|
+
"Tailing",
|
|
2369
|
+
"Sentenceboundary",
|
|
2370
|
+
"Comma",
|
|
2371
|
+
"Semicolon",
|
|
2372
|
+
"Enumerationcomma"
|
|
2373
|
+
]);
|
|
2374
|
+
var ALLOWED_VISEME_TYPES = /* @__PURE__ */ new Set(["redlips_front", "FacialExpression"]);
|
|
2375
|
+
function decodeAttribute(value) {
|
|
2376
|
+
return value.replace(
|
|
2377
|
+
/&(?:amp|apos|gt|lt|quot);/gi,
|
|
2378
|
+
(entity) => ({ "&": "&", "'": "'", ">": ">", "<": "<", """: '"' })[entity.toLowerCase()] ?? entity
|
|
2379
|
+
);
|
|
2380
|
+
}
|
|
2381
|
+
function findTagEnd2(source, start) {
|
|
2382
|
+
let quote = "";
|
|
2383
|
+
for (let index = start; index < source.length; index += 1) {
|
|
2384
|
+
const character = source[index];
|
|
2385
|
+
if (quote) {
|
|
2386
|
+
if (character === quote) quote = "";
|
|
2387
|
+
} else if (character === '"' || character === "'") quote = character;
|
|
2388
|
+
else if (character === ">") return index;
|
|
2389
|
+
}
|
|
2390
|
+
return source.length - 1;
|
|
2391
|
+
}
|
|
2392
|
+
function tokenizeElements(source) {
|
|
2393
|
+
const tokens = [];
|
|
2394
|
+
const openElements = [];
|
|
2395
|
+
let index = 0;
|
|
2396
|
+
while (index < source.length) {
|
|
2397
|
+
const start = source.indexOf("<", index);
|
|
2398
|
+
if (start === -1) break;
|
|
2399
|
+
if (source.startsWith("<!--", start)) {
|
|
2400
|
+
const end2 = source.indexOf("-->", start + 4);
|
|
2401
|
+
index = end2 === -1 ? source.length : end2 + 3;
|
|
2402
|
+
continue;
|
|
2403
|
+
}
|
|
2404
|
+
if (source.startsWith("<![CDATA[", start)) {
|
|
2405
|
+
const end2 = source.indexOf("]]>", start + 9);
|
|
2406
|
+
index = end2 === -1 ? source.length : end2 + 3;
|
|
2407
|
+
continue;
|
|
2408
|
+
}
|
|
2409
|
+
if (source.startsWith("<?", start)) {
|
|
2410
|
+
const end2 = source.indexOf("?>", start + 2);
|
|
2411
|
+
index = end2 === -1 ? source.length : end2 + 2;
|
|
2412
|
+
continue;
|
|
2413
|
+
}
|
|
2414
|
+
const end = findTagEnd2(source, start + 1);
|
|
2415
|
+
const raw = source.slice(start, end + 1);
|
|
2416
|
+
if (raw.startsWith("</")) {
|
|
2417
|
+
openElements.pop();
|
|
2418
|
+
index = end + 1;
|
|
2419
|
+
continue;
|
|
2420
|
+
}
|
|
2421
|
+
const nameMatch = /^<\s*([A-Za-z_][A-Za-z0-9_.:-]*)/.exec(raw);
|
|
2422
|
+
if (!nameMatch?.[1]) {
|
|
2423
|
+
index = end + 1;
|
|
2424
|
+
continue;
|
|
2425
|
+
}
|
|
2426
|
+
const attributes = /* @__PURE__ */ new Map();
|
|
2427
|
+
const attributeSource = raw.slice(nameMatch[0].length, raw.length - 1).replace(/\/\s*$/, "");
|
|
2428
|
+
const attributePattern = /([A-Za-z_][A-Za-z0-9_.:-]*)\s*=\s*(["'])([\s\S]*?)\2/g;
|
|
2429
|
+
for (const match of attributeSource.matchAll(attributePattern)) {
|
|
2430
|
+
attributes.set(match[1].toLowerCase(), decodeAttribute(match[3]));
|
|
2431
|
+
}
|
|
2432
|
+
const selfClosing = /\/\s*>$/.test(raw);
|
|
2433
|
+
const parent = openElements[openElements.length - 1];
|
|
2434
|
+
const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
|
|
2435
|
+
const tokenName = nameMatch[1];
|
|
2436
|
+
const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
|
|
2437
|
+
tokens.push({
|
|
2438
|
+
attributes,
|
|
2439
|
+
end,
|
|
2440
|
+
name: tokenName,
|
|
2441
|
+
parentName: parent?.name,
|
|
2442
|
+
parentVoiceName,
|
|
2443
|
+
selfClosing,
|
|
2444
|
+
start
|
|
2445
|
+
});
|
|
2446
|
+
if (!selfClosing) {
|
|
2447
|
+
openElements.push({
|
|
2448
|
+
name: tokenName,
|
|
2449
|
+
voiceName: tokenVoiceName
|
|
2450
|
+
});
|
|
2451
|
+
}
|
|
2452
|
+
index = end + 1;
|
|
2453
|
+
}
|
|
2454
|
+
return tokens;
|
|
2455
|
+
}
|
|
2456
|
+
function location(source, offset) {
|
|
2457
|
+
const before = source.slice(0, Math.max(0, offset));
|
|
2458
|
+
const line = before.split("\n").length;
|
|
2459
|
+
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
2460
|
+
}
|
|
2461
|
+
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code2) {
|
|
2462
|
+
diagnostics.push({
|
|
2463
|
+
...location(source, offset),
|
|
2464
|
+
message,
|
|
2465
|
+
severity,
|
|
2466
|
+
source: "ssml-static-validator",
|
|
2467
|
+
...code2 ? { code: code2 } : {}
|
|
2468
|
+
});
|
|
2469
|
+
}
|
|
2470
|
+
function isSupportedProsodyRate(value) {
|
|
2471
|
+
const trimmed = value.trim();
|
|
2472
|
+
if (/^(x-slow|slow|medium|fast|x-fast|[+-]?\d+(?:\.\d+)?%)$/.test(trimmed)) return true;
|
|
2473
|
+
const multiplier = /^(\d+(?:\.\d+)?)(x)?$/i.exec(trimmed);
|
|
2474
|
+
if (!multiplier) return false;
|
|
2475
|
+
const numericValue = Number(multiplier[1]);
|
|
2476
|
+
return numericValue >= 0.5 && numericValue <= 2;
|
|
2477
|
+
}
|
|
2478
|
+
function isValidAzureAudioDuration(value) {
|
|
2479
|
+
const trimmed = value.trim();
|
|
2480
|
+
const numeric = /^(\d+(?:\.\d+)?)(ms|s)$/.exec(trimmed);
|
|
2481
|
+
if (numeric) return Number(numeric[1]) > 0;
|
|
2482
|
+
const clock = /^(\d{2,}):([0-5]\d):([0-5]\d)(?:\.(\d{1,3}))?$/.exec(trimmed);
|
|
2483
|
+
if (!clock) return false;
|
|
2484
|
+
return Number(clock[1]) > 0 || Number(clock[2]) > 0 || Number(clock[3]) > 0 || Number(clock[4] ?? 0) > 0;
|
|
2485
|
+
}
|
|
2486
|
+
function attr(token, name) {
|
|
2487
|
+
return token.attributes.get(name.toLowerCase());
|
|
2488
|
+
}
|
|
2489
|
+
var DEFAULT_LANGUAGE_ALIASES = {
|
|
2490
|
+
"zh-CN": ["zh-Hans"],
|
|
2491
|
+
"zh-TW": ["zh-Hant"]
|
|
2492
|
+
};
|
|
2493
|
+
function canonicalLanguageTag(language) {
|
|
2494
|
+
const trimmed = language.trim();
|
|
2495
|
+
if (!trimmed) return "";
|
|
2496
|
+
try {
|
|
2497
|
+
return new Intl.Locale(trimmed).toString().toLowerCase();
|
|
2498
|
+
} catch {
|
|
2499
|
+
return trimmed.toLowerCase();
|
|
2500
|
+
}
|
|
2501
|
+
}
|
|
2502
|
+
function createLanguageNormalizer(options) {
|
|
2503
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
2504
|
+
const addAliasGroup = (canonical, values) => {
|
|
2505
|
+
const normalizedCanonical = canonicalLanguageTag(canonical);
|
|
2506
|
+
if (!normalizedCanonical) return;
|
|
2507
|
+
aliases.set(normalizedCanonical, normalizedCanonical);
|
|
2508
|
+
for (const value of values) {
|
|
2509
|
+
const normalizedValue = canonicalLanguageTag(value);
|
|
2510
|
+
if (normalizedValue) aliases.set(normalizedValue, normalizedCanonical);
|
|
2511
|
+
}
|
|
2512
|
+
};
|
|
2513
|
+
for (const [canonical, values] of Object.entries(DEFAULT_LANGUAGE_ALIASES)) addAliasGroup(canonical, values);
|
|
2514
|
+
for (const [canonical, valueOrValues] of Object.entries(options.languageAliases ?? {}))
|
|
2515
|
+
addAliasGroup(canonical, typeof valueOrValues === "string" ? [valueOrValues] : valueOrValues);
|
|
2516
|
+
return (language) => {
|
|
2517
|
+
const customValue = options.normalizeLanguage ? options.normalizeLanguage(language) : language;
|
|
2518
|
+
const normalized = canonicalLanguageTag(customValue);
|
|
2519
|
+
return aliases.get(normalized) ?? normalized;
|
|
2520
|
+
};
|
|
2521
|
+
}
|
|
2522
|
+
function voiceLocalePrefix(voiceName) {
|
|
2523
|
+
const match = /^(?<language>[A-Za-z]{2,3})-(?<region>[A-Za-z]{2}|\d{3})(?:-|$)/.exec(voiceName.trim());
|
|
2524
|
+
if (!match?.groups) return void 0;
|
|
2525
|
+
const tag = `${match.groups.language}-${match.groups.region}`;
|
|
2526
|
+
return {
|
|
2527
|
+
language: match.groups.language.toLowerCase(),
|
|
2528
|
+
region: match.groups.region.toLowerCase(),
|
|
2529
|
+
tag
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
function definitionFromStyleMap(voiceName, styles) {
|
|
2533
|
+
return {
|
|
2534
|
+
name: voiceName,
|
|
2535
|
+
locale: voiceLocalePrefix(voiceName)?.tag ?? "",
|
|
2536
|
+
styles
|
|
2537
|
+
};
|
|
2538
|
+
}
|
|
2539
|
+
function normalizeVoiceCatalog(options) {
|
|
2540
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
2541
|
+
for (const definition of AZURE_VOICE_DEFINITIONS) definitions.set(definition.name.toLowerCase(), definition);
|
|
2542
|
+
for (const definition of options.voiceCatalog ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
2543
|
+
for (const definition of options.voiceDefinitions ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
2544
|
+
for (const definition of options.customVoiceDefinitions ?? [])
|
|
2545
|
+
definitions.set(definition.name.toLowerCase(), definition);
|
|
2546
|
+
for (const [voiceName, styles] of Object.entries(options.customVoiceStyleMap ?? {})) {
|
|
2547
|
+
const key = voiceName.toLowerCase();
|
|
2548
|
+
const current = definitions.get(key);
|
|
2549
|
+
definitions.set(key, {
|
|
2550
|
+
...current ?? definitionFromStyleMap(voiceName, styles),
|
|
2551
|
+
name: current?.name ?? voiceName,
|
|
2552
|
+
styles: styles.map((style) => style.toLowerCase())
|
|
2553
|
+
});
|
|
2554
|
+
}
|
|
2555
|
+
return definitions;
|
|
2556
|
+
}
|
|
2557
|
+
function diagnosticSeverity(policy) {
|
|
2558
|
+
if (policy === "ignore") return void 0;
|
|
2559
|
+
return policy === "error" ? "error" : "warning";
|
|
2560
|
+
}
|
|
2561
|
+
function languagePart(language) {
|
|
2562
|
+
try {
|
|
2563
|
+
return new Intl.Locale(language).language.toLowerCase();
|
|
2564
|
+
} catch {
|
|
2565
|
+
return language.split("-")[0]?.toLowerCase() ?? "";
|
|
2566
|
+
}
|
|
2567
|
+
}
|
|
2568
|
+
function definitionMatchesLanguage(definition, voiceName, language, normalizeLanguage) {
|
|
2569
|
+
const candidateLanguages = definition ? [definition.locale, ...definition.secondaryLocales ?? []].filter(Boolean) : [voiceLocalePrefix(voiceName)?.tag ?? ""];
|
|
2570
|
+
if (candidateLanguages.length === 0 || !language.trim()) return void 0;
|
|
2571
|
+
const normalizedLanguage = normalizeLanguage(language);
|
|
2572
|
+
const normalizedCandidates = candidateLanguages.map(normalizeLanguage);
|
|
2573
|
+
if (normalizedCandidates.includes(normalizedLanguage)) return true;
|
|
2574
|
+
if (!normalizedLanguage || !normalizedCandidates.some(Boolean)) return void 0;
|
|
2575
|
+
return normalizedLanguage === languagePart(normalizedLanguage) ? normalizedCandidates.some((candidate) => languagePart(candidate) === normalizedLanguage) : false;
|
|
2576
|
+
}
|
|
2577
|
+
function canonicalTagName(name) {
|
|
2578
|
+
const normalized = name.toLowerCase();
|
|
2579
|
+
if (normalized === "express-as" || normalized === "expressas") return "mstts:express-as";
|
|
2580
|
+
if (normalized === "sayas") return "say-as";
|
|
2581
|
+
return normalized;
|
|
2582
|
+
}
|
|
2583
|
+
function validateVoiceFeatureMatrix(token, source, diagnostics, voiceName, definition) {
|
|
2584
|
+
if (!voiceName || !definition || token.name.toLowerCase() === "voice" || token.name.toLowerCase() === "mstts:turn")
|
|
2585
|
+
return;
|
|
2586
|
+
const tagName = canonicalTagName(token.name);
|
|
2587
|
+
const unsupportedTags = new Set((definition.unsupportedTags ?? []).map(canonicalTagName));
|
|
2588
|
+
const supportedTags = definition.supportedTags?.map(canonicalTagName);
|
|
2589
|
+
if (unsupportedTags.has(tagName) || supportedTags !== void 0 && !supportedTags.includes(tagName)) {
|
|
2590
|
+
addDiagnostic(
|
|
2591
|
+
diagnostics,
|
|
2592
|
+
source,
|
|
2593
|
+
token.start,
|
|
2594
|
+
`Tag <${token.name}> is not supported by voice "${voiceName}" according to the configured feature matrix.`,
|
|
2595
|
+
"error",
|
|
2596
|
+
"azure-unsupported-tag-for-voice"
|
|
2597
|
+
);
|
|
2598
|
+
}
|
|
2599
|
+
}
|
|
2600
|
+
function validateAudioSource(token, source, diagnostics, options, elementName2) {
|
|
2601
|
+
const src = attr(token, "src");
|
|
2602
|
+
if (!src) {
|
|
2603
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName2}> requires a "src" attribute.`);
|
|
2604
|
+
return;
|
|
2605
|
+
}
|
|
2606
|
+
let parsed;
|
|
2607
|
+
try {
|
|
2608
|
+
parsed = new URL(src);
|
|
2609
|
+
} catch {
|
|
2610
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must be an absolute HTTP(S) URL.`);
|
|
2611
|
+
return;
|
|
2612
|
+
}
|
|
2613
|
+
if (parsed.protocol !== "https:" && !(options.allowHttpAudio && parsed.protocol === "http:"))
|
|
2614
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must use HTTPS.`);
|
|
2615
|
+
if (options.allowedAudioOrigins && !options.allowedAudioOrigins.includes(parsed.origin))
|
|
2616
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> origin "${parsed.origin}" is not allowed.`);
|
|
2617
|
+
else if (!options.allowExternalAudio)
|
|
2618
|
+
addDiagnostic(
|
|
2619
|
+
diagnostics,
|
|
2620
|
+
source,
|
|
2621
|
+
token.start,
|
|
2622
|
+
`<${elementName2} src> external origin "${parsed.origin}" is blocked by default; set allowExternalAudio to true or provide allowedAudioOrigins.`
|
|
2623
|
+
);
|
|
2624
|
+
}
|
|
2625
|
+
function validateElement(token, source, diagnostics, voiceName, options, voiceCatalog) {
|
|
2626
|
+
const name = token.name.toLowerCase();
|
|
2627
|
+
if (name === "voice" && !attr(token, "name")?.trim())
|
|
2628
|
+
addDiagnostic(diagnostics, source, token.start, '<voice> requires a non-empty "name" attribute.');
|
|
2629
|
+
if (name === "break") {
|
|
2630
|
+
const time = attr(token, "time");
|
|
2631
|
+
const strength = attr(token, "strength");
|
|
2632
|
+
if (!time && !strength)
|
|
2633
|
+
addDiagnostic(diagnostics, source, token.start, '<break> requires either "time" or "strength".');
|
|
2634
|
+
if (time && strength)
|
|
2635
|
+
addDiagnostic(diagnostics, source, token.start, '<break> must not specify both "time" and "strength".');
|
|
2636
|
+
if (time && !/^\d+(?:\.\d+)?(?:ms|s)$/.test(time.trim()))
|
|
2637
|
+
addDiagnostic(diagnostics, source, token.start, '<break time> must use a numeric value followed by "ms" or "s".');
|
|
2638
|
+
if (strength && !ALLOWED_BREAK_STRENGTHS.has(strength))
|
|
2639
|
+
addDiagnostic(diagnostics, source, token.start, `Unsupported <break strength> value "${strength}".`);
|
|
2640
|
+
}
|
|
2641
|
+
if (name === "prosody") {
|
|
2642
|
+
const rate = attr(token, "rate");
|
|
2643
|
+
const pitch = attr(token, "pitch");
|
|
2644
|
+
const volume = attr(token, "volume");
|
|
2645
|
+
if (rate && !isSupportedProsodyRate(rate))
|
|
2646
|
+
addDiagnostic(diagnostics, source, token.start, `Unsupported <prosody rate> value "${rate}".`);
|
|
2647
|
+
if (pitch && !/^(x-low|low|medium|high|x-high|[+-]?\d+(?:\.\d+)?(?:st|Hz|%)?)$/.test(pitch.trim()))
|
|
2648
|
+
addDiagnostic(diagnostics, source, token.start, `Unsupported <prosody pitch> value "${pitch}".`);
|
|
2649
|
+
if (volume && !/^(silent|x-soft|soft|medium|loud|x-loud|[+-]?\d+(?:\.\d+)?(?:dB|%)?)$/.test(volume.trim()))
|
|
2650
|
+
addDiagnostic(diagnostics, source, token.start, `Unsupported <prosody volume> value "${volume}".`);
|
|
2651
|
+
}
|
|
2652
|
+
if (name === "mstts:express-as" || name === "express-as" || name === "expressas") {
|
|
2653
|
+
const style = attr(token, "style");
|
|
2654
|
+
if (!style?.trim())
|
|
2655
|
+
addDiagnostic(diagnostics, source, token.start, '<mstts:express-as> requires a non-empty "style" attribute.');
|
|
2656
|
+
const degree = attr(token, "styledegree") ?? attr(token, "style-degree");
|
|
2657
|
+
if (degree && (!/^\d+(?:\.\d+)?$/.test(degree) || Number(degree) < 0.01 || Number(degree) > 2))
|
|
2658
|
+
addDiagnostic(
|
|
2659
|
+
diagnostics,
|
|
2660
|
+
source,
|
|
2661
|
+
token.start,
|
|
2662
|
+
"<mstts:express-as styledegree> must be a number between 0.01 and 2."
|
|
2663
|
+
);
|
|
2664
|
+
const role = attr(token, "role");
|
|
2665
|
+
if (role && !ALLOWED_ROLES.has(role))
|
|
2666
|
+
addDiagnostic(diagnostics, source, token.start, `Unsupported <mstts:express-as role> value "${role}".`);
|
|
2667
|
+
const definition = voiceName ? voiceCatalog.get(voiceName.toLowerCase()) : void 0;
|
|
2668
|
+
const supportedStyles = definition?.styles;
|
|
2669
|
+
const severity = diagnosticSeverity(options.unsupportedStylePolicy ?? options.unknownVoicePolicy ?? "warn");
|
|
2670
|
+
if (style && definition && !supportedStyles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase()) && severity)
|
|
2671
|
+
addDiagnostic(
|
|
2672
|
+
diagnostics,
|
|
2673
|
+
source,
|
|
2674
|
+
token.start,
|
|
2675
|
+
`Unknown style "${style}" is not supported by voice "${voiceName}" according to the configured voice style map.`,
|
|
2676
|
+
severity,
|
|
2677
|
+
"azure-unsupported-style"
|
|
2678
|
+
);
|
|
2679
|
+
if (style && voiceName && !definition && severity)
|
|
2680
|
+
addDiagnostic(
|
|
2681
|
+
diagnostics,
|
|
2682
|
+
source,
|
|
2683
|
+
token.start,
|
|
2684
|
+
`Unknown style "${style}" cannot be verified because voice "${voiceName}" is not registered in the voice style map.`,
|
|
2685
|
+
severity
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
if (name === "say-as" || name === "sayas") {
|
|
2689
|
+
const interpretAs = attr(token, "interpret-as");
|
|
2690
|
+
if (!interpretAs || !ALLOWED_SAY_AS.has(interpretAs))
|
|
2691
|
+
addDiagnostic(diagnostics, source, token.start, `<say-as> requires a supported "interpret-as" value.`);
|
|
2692
|
+
}
|
|
2693
|
+
if (name === "phoneme" && (!attr(token, "alphabet") || !attr(token, "ph")))
|
|
2694
|
+
addDiagnostic(diagnostics, source, token.start, '<phoneme> requires both "alphabet" and "ph" attributes.');
|
|
2695
|
+
if (name === "emphasis" && attr(token, "level") && !ALLOWED_EMPHASIS_LEVELS.has(attr(token, "level") ?? ""))
|
|
2696
|
+
addDiagnostic(diagnostics, source, token.start, `Unsupported <emphasis level> value "${attr(token, "level")}".`);
|
|
2697
|
+
if (name === "sub" && !attr(token, "alias")?.trim())
|
|
2698
|
+
addDiagnostic(diagnostics, source, token.start, '<sub> requires a non-empty "alias" attribute.');
|
|
2699
|
+
if (name === "lang" && !attr(token, "xml:lang")?.trim() && !attr(token, "lang")?.trim())
|
|
2700
|
+
addDiagnostic(diagnostics, source, token.start, '<lang> requires an "xml:lang" attribute.');
|
|
2701
|
+
if (name === "mark" && !attr(token, "name")?.trim())
|
|
2702
|
+
addDiagnostic(diagnostics, source, token.start, '<mark> requires a non-empty "name" attribute.');
|
|
2703
|
+
if (name === "bookmark" && !attr(token, "mark")?.trim())
|
|
2704
|
+
addDiagnostic(diagnostics, source, token.start, '<bookmark> requires a non-empty "mark" attribute.');
|
|
2705
|
+
if (name === "lexicon") {
|
|
2706
|
+
const uri = attr(token, "uri");
|
|
2707
|
+
if (!uri) addDiagnostic(diagnostics, source, token.start, '<lexicon> requires a "uri" attribute.');
|
|
2708
|
+
else {
|
|
2709
|
+
try {
|
|
2710
|
+
const parsed = new URL(uri);
|
|
2711
|
+
if (parsed.protocol !== "https:")
|
|
2712
|
+
addDiagnostic(diagnostics, source, token.start, "<lexicon uri> must use HTTPS.");
|
|
2713
|
+
} catch {
|
|
2714
|
+
addDiagnostic(diagnostics, source, token.start, "<lexicon uri> must be an absolute HTTPS URL.");
|
|
2715
|
+
}
|
|
2716
|
+
}
|
|
2717
|
+
}
|
|
2718
|
+
if (name === "mstts:silence") {
|
|
2719
|
+
const type = attr(token, "type");
|
|
2720
|
+
const value = attr(token, "value");
|
|
2721
|
+
if (!type || !ALLOWED_SILENCE_TYPES.has(type))
|
|
2722
|
+
addDiagnostic(diagnostics, source, token.start, '<mstts:silence> requires a supported "type" attribute.');
|
|
2723
|
+
if (!value || !/^\d+(?:\.\d+)?(?:ms|s)$/.test(value.trim()))
|
|
2724
|
+
addDiagnostic(diagnostics, source, token.start, '<mstts:silence> requires a time-valued "value" attribute.');
|
|
2725
|
+
}
|
|
2726
|
+
if (name === "mstts:audioduration") {
|
|
2727
|
+
const value = attr(token, "value");
|
|
2728
|
+
if (!value || !isValidAzureAudioDuration(value))
|
|
2729
|
+
addDiagnostic(
|
|
2730
|
+
diagnostics,
|
|
2731
|
+
source,
|
|
2732
|
+
token.start,
|
|
2733
|
+
'<mstts:audioduration> requires a positive duration such as "10s", "5000ms", or "00:00:10".'
|
|
2734
|
+
);
|
|
2735
|
+
if (!token.selfClosing)
|
|
2736
|
+
addDiagnostic(diagnostics, source, token.start, "<mstts:audioduration> must be self-closing.");
|
|
2737
|
+
}
|
|
2738
|
+
if (name === "mstts:viseme") {
|
|
2739
|
+
const type = attr(token, "type");
|
|
2740
|
+
if (!type || !ALLOWED_VISEME_TYPES.has(type))
|
|
2741
|
+
addDiagnostic(diagnostics, source, token.start, '<mstts:viseme> requires a supported "type" attribute.');
|
|
2742
|
+
}
|
|
2743
|
+
if (name === "audio") {
|
|
2744
|
+
validateAudioSource(token, source, diagnostics, options, "audio");
|
|
2745
|
+
}
|
|
2746
|
+
if (name === "mstts:turn") {
|
|
2747
|
+
if (!attr(token, "voice")?.trim())
|
|
2748
|
+
addDiagnostic(diagnostics, source, token.start, '<mstts:turn> requires a non-empty "voice" attribute.');
|
|
2749
|
+
if (token.parentName?.toLowerCase() !== "mstts:dialog")
|
|
2750
|
+
addDiagnostic(diagnostics, source, token.start, "<mstts:turn> is only allowed directly inside <mstts:dialog>.");
|
|
2751
|
+
}
|
|
2752
|
+
if (name === "mstts:backgroundaudio") {
|
|
2753
|
+
validateAudioSource(token, source, diagnostics, options, "mstts:backgroundaudio");
|
|
2754
|
+
const volume = attr(token, "volume");
|
|
2755
|
+
if (volume && !/^(silent|x-soft|soft|medium|loud|x-loud|[+-]?\d+(?:\.\d+)?(?:dB|%))$/i.test(volume.trim()))
|
|
2756
|
+
addDiagnostic(diagnostics, source, token.start, `Unsupported <mstts:backgroundaudio volume> value "${volume}".`);
|
|
2757
|
+
for (const [attribute, value] of [
|
|
2758
|
+
["fadein", attr(token, "fadein")],
|
|
2759
|
+
["fadeout", attr(token, "fadeout")]
|
|
2760
|
+
]) {
|
|
2761
|
+
if (value && !isValidAzureAudioDuration(value))
|
|
2762
|
+
addDiagnostic(
|
|
2763
|
+
diagnostics,
|
|
2764
|
+
source,
|
|
2765
|
+
token.start,
|
|
2766
|
+
`<mstts:backgroundaudio ${attribute}> must be a positive duration such as "500ms" or "10s".`
|
|
2767
|
+
);
|
|
2768
|
+
}
|
|
2769
|
+
if (!token.selfClosing)
|
|
2770
|
+
addDiagnostic(diagnostics, source, token.start, "<mstts:backgroundaudio> must be self-closing.");
|
|
2771
|
+
}
|
|
2772
|
+
}
|
|
2773
|
+
function validateAzureSsml(ssml, options = {}) {
|
|
2774
|
+
const diagnostics = [];
|
|
2775
|
+
if (typeof ssml !== "string") {
|
|
2776
|
+
return [
|
|
2777
|
+
{
|
|
2778
|
+
line: 1,
|
|
2779
|
+
column: 1,
|
|
2780
|
+
message: "SSML input must be a string",
|
|
2781
|
+
severity: "error",
|
|
2782
|
+
source: "ssml-static-validator"
|
|
2783
|
+
}
|
|
2784
|
+
];
|
|
2785
|
+
}
|
|
2786
|
+
const maxLength = options.maxLength ?? 1e4;
|
|
2787
|
+
if (ssml.length > maxLength)
|
|
2788
|
+
addDiagnostic(diagnostics, ssml, maxLength, `SSML exceeds the maximum length of ${maxLength} characters.`);
|
|
2789
|
+
try {
|
|
2790
|
+
parseSsml(ssml);
|
|
2791
|
+
} catch (error) {
|
|
2792
|
+
const message = error instanceof Error ? error.message.replace(/ at position \d+$/, "") : String(error);
|
|
2793
|
+
const match = / at position (\d+)$/.exec(error instanceof Error ? error.message : "");
|
|
2794
|
+
addDiagnostic(diagnostics, ssml, match ? Number(match[1]) : 0, message);
|
|
2795
|
+
return diagnostics;
|
|
2796
|
+
}
|
|
2797
|
+
const tokens = tokenizeElements(ssml);
|
|
2798
|
+
const speak = tokens.find((token) => token.name.toLowerCase() === "speak");
|
|
2799
|
+
const voices = tokens.filter((token) => token.name.toLowerCase() === "voice");
|
|
2800
|
+
if (!speak || voices.length === 0)
|
|
2801
|
+
addDiagnostic(
|
|
2802
|
+
diagnostics,
|
|
2803
|
+
ssml,
|
|
2804
|
+
speak?.start ?? 0,
|
|
2805
|
+
"Azure SSML requires at least one <voice> element under <speak>."
|
|
2806
|
+
);
|
|
2807
|
+
const voiceName = voices[0] ? attr(voices[0], "name") : void 0;
|
|
2808
|
+
const voiceCatalog = normalizeVoiceCatalog(options);
|
|
2809
|
+
const normalizeLanguage = createLanguageNormalizer(options);
|
|
2810
|
+
const policySeverity = diagnosticSeverity(options.unknownVoicePolicy ?? "warn");
|
|
2811
|
+
const voicesToValidate = options.validateNestedVoices === false ? voices.slice(0, 1) : voices;
|
|
2812
|
+
for (const token of voicesToValidate) {
|
|
2813
|
+
const name = attr(token, "name")?.trim();
|
|
2814
|
+
const language = attr(token, "xml:lang")?.trim() || (speak ? attr(speak, "xml:lang")?.trim() : void 0);
|
|
2815
|
+
const definition = name ? voiceCatalog.get(name.toLowerCase()) : void 0;
|
|
2816
|
+
if (name && !definition && policySeverity)
|
|
2817
|
+
addDiagnostic(
|
|
2818
|
+
diagnostics,
|
|
2819
|
+
ssml,
|
|
2820
|
+
token.start,
|
|
2821
|
+
`Unknown voice "${name}" is not registered in the voice catalog.`,
|
|
2822
|
+
policySeverity,
|
|
2823
|
+
"azure-unknown-voice"
|
|
2824
|
+
);
|
|
2825
|
+
if (name && language && definitionMatchesLanguage(definition, name, language, normalizeLanguage) === false)
|
|
2826
|
+
addDiagnostic(
|
|
2827
|
+
diagnostics,
|
|
2828
|
+
ssml,
|
|
2829
|
+
token.start,
|
|
2830
|
+
`Voice "${name}" does not match language "${language}"; the voice name prefix indicates a different language or region.`,
|
|
2831
|
+
"warning",
|
|
2832
|
+
"azure-locale-mismatch"
|
|
2833
|
+
);
|
|
2834
|
+
}
|
|
2835
|
+
for (const token of tokens) {
|
|
2836
|
+
const tokenName = token.name.toLowerCase();
|
|
2837
|
+
const tokenVoiceName = tokenName === "voice" ? attr(token, "name")?.trim() : tokenName === "mstts:turn" ? attr(token, "voice")?.trim() || token.parentVoiceName : options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
2838
|
+
validateElement(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
2839
|
+
const definition = tokenVoiceName ? voiceCatalog.get(tokenVoiceName.toLowerCase()) : void 0;
|
|
2840
|
+
validateVoiceFeatureMatrix(token, ssml, diagnostics, tokenVoiceName, definition);
|
|
2841
|
+
if (tokenName === "voice" && options.model && definition?.models && !definition.models.some((model) => model.toLowerCase() === options.model?.toLowerCase())) {
|
|
2842
|
+
addDiagnostic(
|
|
2843
|
+
diagnostics,
|
|
2844
|
+
ssml,
|
|
2845
|
+
token.start,
|
|
2846
|
+
`Voice "${tokenVoiceName}" does not support model "${options.model}" according to the configured feature matrix.`,
|
|
2847
|
+
"error",
|
|
2848
|
+
"azure-unsupported-model-for-voice"
|
|
2849
|
+
);
|
|
2850
|
+
}
|
|
2851
|
+
}
|
|
2852
|
+
return diagnostics;
|
|
2853
|
+
}
|
|
2854
|
+
var AZURE_VOICE_CATALOG_METADATA = {
|
|
2855
|
+
apiVersion: "2025-10-01",
|
|
2856
|
+
generatedAt: "2026-08-28T00:00:00.000Z",
|
|
2857
|
+
regions: [],
|
|
2858
|
+
voiceCount: AZURE_VOICE_DEFINITIONS.length
|
|
2859
|
+
};
|
|
2025
2860
|
|
|
2026
2861
|
// packages/ssml-editor-react/src/clearSsmlDocument.ts
|
|
2027
2862
|
function getDocumentChildren(document) {
|
|
@@ -2082,6 +2917,7 @@ var INTRINSICALLY_EMPTY_ELEMENTS = /* @__PURE__ */ new Set([
|
|
|
2082
2917
|
"lexicon",
|
|
2083
2918
|
"mark",
|
|
2084
2919
|
"mstts:silence",
|
|
2920
|
+
"mstts:audioduration",
|
|
2085
2921
|
"mstts:viseme",
|
|
2086
2922
|
"silence",
|
|
2087
2923
|
"viseme"
|
|
@@ -2145,7 +2981,7 @@ function validateEntityReferences(value) {
|
|
|
2145
2981
|
index = semicolon + 1;
|
|
2146
2982
|
}
|
|
2147
2983
|
}
|
|
2148
|
-
function
|
|
2984
|
+
function findTagEnd3(source, start, hasInternalSubset = false) {
|
|
2149
2985
|
let quote;
|
|
2150
2986
|
let subsetDepth = 0;
|
|
2151
2987
|
for (let index = start + 1; index < source.length; index += 1) {
|
|
@@ -2318,7 +3154,7 @@ function parseXml(source) {
|
|
|
2318
3154
|
continue;
|
|
2319
3155
|
}
|
|
2320
3156
|
if (source.startsWith("</", index)) {
|
|
2321
|
-
const end2 =
|
|
3157
|
+
const end2 = findTagEnd3(source, index);
|
|
2322
3158
|
const name2 = parseClosingTag(source, index, end2);
|
|
2323
3159
|
const element = currentElement(stack);
|
|
2324
3160
|
if (element === void 0 || element.name !== name2) {
|
|
@@ -2330,7 +3166,7 @@ function parseXml(source) {
|
|
|
2330
3166
|
continue;
|
|
2331
3167
|
}
|
|
2332
3168
|
if (source.startsWith("<!", index)) {
|
|
2333
|
-
const end2 =
|
|
3169
|
+
const end2 = findTagEnd3(source, index, true);
|
|
2334
3170
|
parseDeclaration(source, index, end2);
|
|
2335
3171
|
appendNode2(currentElement(stack)?.children ?? children, {
|
|
2336
3172
|
kind: "declaration",
|
|
@@ -2339,7 +3175,7 @@ function parseXml(source) {
|
|
|
2339
3175
|
index = end2 + 1;
|
|
2340
3176
|
continue;
|
|
2341
3177
|
}
|
|
2342
|
-
const end =
|
|
3178
|
+
const end = findTagEnd3(source, index);
|
|
2343
3179
|
const { name, selfClosing } = parseStartTag(source, index, end);
|
|
2344
3180
|
if (stack.length === 0) {
|
|
2345
3181
|
if (root !== void 0) {
|
|
@@ -2802,6 +3638,28 @@ var SSML_COMPLETION_SNIPPETS = [
|
|
|
2802
3638
|
label: "mstts:express-as",
|
|
2803
3639
|
insertText: `<mstts:express-as style="cheerful">\${1:text}</mstts:express-as>`
|
|
2804
3640
|
},
|
|
3641
|
+
{
|
|
3642
|
+
label: "mstts:audioduration",
|
|
3643
|
+
insertText: '<mstts:audioduration value="10s" />'
|
|
3644
|
+
},
|
|
3645
|
+
{
|
|
3646
|
+
label: "mstts:dialog",
|
|
3647
|
+
insertText: `<mstts:dialog>
|
|
3648
|
+
<mstts:turn voice="\${1:en-US-JennyNeural}">\${2:text}</mstts:turn>
|
|
3649
|
+
</mstts:dialog>`
|
|
3650
|
+
},
|
|
3651
|
+
{
|
|
3652
|
+
label: "mstts:turn",
|
|
3653
|
+
insertText: `<mstts:turn voice="\${1:en-US-JennyNeural}">\${2:text}</mstts:turn>`
|
|
3654
|
+
},
|
|
3655
|
+
{
|
|
3656
|
+
label: "mstts:backgroundaudio",
|
|
3657
|
+
insertText: `<mstts:backgroundaudio src="\${1:https://example.com/audio.mp3}" volume="\${2:-3dB}" />`
|
|
3658
|
+
},
|
|
3659
|
+
{
|
|
3660
|
+
label: "mstts:ttsembedding",
|
|
3661
|
+
insertText: `<mstts:ttsembedding>\${1:text}</mstts:ttsembedding>`
|
|
3662
|
+
},
|
|
2805
3663
|
{
|
|
2806
3664
|
label: "sub",
|
|
2807
3665
|
insertText: `<sub alias="\${1:\u8AAD\u307F}">\${2:\u6F22\u5B57}</sub>`
|
|
@@ -3161,6 +4019,74 @@ var SSML_TAG_DEFINITIONS = [
|
|
|
3161
4019
|
values: VISEME_TYPE_PRESETS2
|
|
3162
4020
|
}
|
|
3163
4021
|
]
|
|
4022
|
+
},
|
|
4023
|
+
{
|
|
4024
|
+
name: "mstts:audioduration",
|
|
4025
|
+
description: "Sets the target duration of synthesized audio.",
|
|
4026
|
+
parameters: [
|
|
4027
|
+
{
|
|
4028
|
+
name: "value",
|
|
4029
|
+
description: "The target duration, such as `10s`, `5000ms`, or `00:00:10`.",
|
|
4030
|
+
example: "10s"
|
|
4031
|
+
}
|
|
4032
|
+
]
|
|
4033
|
+
},
|
|
4034
|
+
{
|
|
4035
|
+
name: "mstts:dialog",
|
|
4036
|
+
description: "Groups multiple Azure dialog turns that can use different voices.",
|
|
4037
|
+
parameters: []
|
|
4038
|
+
},
|
|
4039
|
+
{
|
|
4040
|
+
name: "mstts:turn",
|
|
4041
|
+
description: "Adds one dialog turn using the required Azure voice name.",
|
|
4042
|
+
parameters: [
|
|
4043
|
+
{
|
|
4044
|
+
name: "voice",
|
|
4045
|
+
description: "The Azure voice used for this turn, such as `en-US-JennyNeural`.",
|
|
4046
|
+
example: "en-US-JennyNeural"
|
|
4047
|
+
}
|
|
4048
|
+
]
|
|
4049
|
+
},
|
|
4050
|
+
{
|
|
4051
|
+
name: "mstts:backgroundaudio",
|
|
4052
|
+
description: "Plays background audio while speech is synthesized.",
|
|
4053
|
+
parameters: [
|
|
4054
|
+
{
|
|
4055
|
+
name: "src",
|
|
4056
|
+
description: "An absolute HTTP(S) URL for the background audio file.",
|
|
4057
|
+
example: "https://example.com/music.mp3"
|
|
4058
|
+
},
|
|
4059
|
+
{
|
|
4060
|
+
name: "volume",
|
|
4061
|
+
description: "The background audio volume, for example `-3dB` or `medium`.",
|
|
4062
|
+
example: "-3dB"
|
|
4063
|
+
},
|
|
4064
|
+
{
|
|
4065
|
+
name: "fadein",
|
|
4066
|
+
description: "The fade-in duration, for example `1s`.",
|
|
4067
|
+
example: "1s"
|
|
4068
|
+
},
|
|
4069
|
+
{
|
|
4070
|
+
name: "fadeout",
|
|
4071
|
+
description: "The fade-out duration, for example `500ms`.",
|
|
4072
|
+
example: "500ms"
|
|
4073
|
+
}
|
|
4074
|
+
]
|
|
4075
|
+
},
|
|
4076
|
+
{
|
|
4077
|
+
name: "mstts:ttsembedding",
|
|
4078
|
+
description: "Embeds custom voice or speaker profile metadata for Azure Speech.",
|
|
4079
|
+
parameters: []
|
|
4080
|
+
},
|
|
4081
|
+
{
|
|
4082
|
+
name: "mstts:embedding",
|
|
4083
|
+
description: "Specifies embedding metadata for custom voice scenarios.",
|
|
4084
|
+
parameters: []
|
|
4085
|
+
},
|
|
4086
|
+
{
|
|
4087
|
+
name: "mstts:voiceconversion",
|
|
4088
|
+
description: "Specifies voice conversion metadata for custom voice scenarios.",
|
|
4089
|
+
parameters: []
|
|
3164
4090
|
}
|
|
3165
4091
|
];
|
|
3166
4092
|
var definitionsByName = /* @__PURE__ */ new Map();
|
|
@@ -3219,7 +4145,7 @@ function toRange(source, token) {
|
|
|
3219
4145
|
function containsOffset(token, offset) {
|
|
3220
4146
|
return offset >= token.start && offset < token.end;
|
|
3221
4147
|
}
|
|
3222
|
-
function
|
|
4148
|
+
function findTagEnd4(source, start) {
|
|
3223
4149
|
let quote;
|
|
3224
4150
|
for (let index = start; index < source.length; index += 1) {
|
|
3225
4151
|
const character = source[index];
|
|
@@ -3350,7 +4276,7 @@ function findTagAtOffset(source, offset) {
|
|
|
3350
4276
|
searchStart = tokenEnd2;
|
|
3351
4277
|
continue;
|
|
3352
4278
|
}
|
|
3353
|
-
const tagEnd =
|
|
4279
|
+
const tagEnd = findTagEnd4(source, start + 1);
|
|
3354
4280
|
const contentEnd = tagEnd ?? source.length;
|
|
3355
4281
|
const tokenEnd = tagEnd === void 0 ? source.length : tagEnd + 1;
|
|
3356
4282
|
if (offset < tokenEnd) {
|
|
@@ -3477,6 +4403,8 @@ export {
|
|
|
3477
4403
|
SAY_AS_PRESETS,
|
|
3478
4404
|
LANGUAGE_PRESETS,
|
|
3479
4405
|
SILENCE_VALUE_PRESETS,
|
|
4406
|
+
AUDIO_DURATION_PRESETS,
|
|
4407
|
+
AUDIO_DURATION_DESCRIPTIONS,
|
|
3480
4408
|
SSML_ATTRIBUTE_PRESETS,
|
|
3481
4409
|
BREAK_TIME_DESCRIPTIONS,
|
|
3482
4410
|
EMPHASIS_LEVEL_DESCRIPTIONS,
|
|
@@ -3497,6 +4425,7 @@ export {
|
|
|
3497
4425
|
parseSsml,
|
|
3498
4426
|
buildPartialSsml,
|
|
3499
4427
|
validateSsml,
|
|
4428
|
+
validateAzureSsml,
|
|
3500
4429
|
clearSsmlDocument,
|
|
3501
4430
|
formatXmlFragment,
|
|
3502
4431
|
getEditableRegion,
|
|
@@ -3510,4 +4439,4 @@ export {
|
|
|
3510
4439
|
findSsmlHoverTarget,
|
|
3511
4440
|
formatSsmlHover
|
|
3512
4441
|
};
|
|
3513
|
-
//# sourceMappingURL=chunk-
|
|
4442
|
+
//# sourceMappingURL=chunk-JNJVTEL6.mjs.map
|