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
package/dist/index.js
CHANGED
|
@@ -51,11 +51,14 @@ __export(src_exports, {
|
|
|
51
51
|
getBuiltInVoiceCatalogMetadata: () => getBuiltInVoiceCatalogMetadata,
|
|
52
52
|
isValidAzureAudioDuration: () => isValidAzureAudioDuration,
|
|
53
53
|
mapSsmlTextNodes: () => mapSsmlTextNodes,
|
|
54
|
+
mergeSynthesisResults: () => mergeSynthesisResults,
|
|
54
55
|
normalizeAzureLanguage: () => normalizeAzureLanguage,
|
|
55
56
|
parseSsml: () => parseSsml,
|
|
56
57
|
splitSsmlDocument: () => splitSsmlDocument,
|
|
57
58
|
synthesizeSpeech: () => synthesizeSpeech,
|
|
58
59
|
synthesizeSsml: () => synthesizeSsml,
|
|
60
|
+
synthesizeSsmlChunks: () => synthesizeSsmlChunks,
|
|
61
|
+
synthesizeSsmlSafe: () => synthesizeSsmlSafe,
|
|
59
62
|
validateAzureSsml: () => validateAzureSsml,
|
|
60
63
|
validateSsml: () => validateSsml,
|
|
61
64
|
validateSsmlStructureIntegrity: () => validateSsmlStructureIntegrity
|
|
@@ -1061,30 +1064,109 @@ function splitNode(document, node, maxLength, context = []) {
|
|
|
1061
1064
|
flush();
|
|
1062
1065
|
return parts;
|
|
1063
1066
|
}
|
|
1064
|
-
function
|
|
1065
|
-
if (
|
|
1067
|
+
function textFromNode(node) {
|
|
1068
|
+
if (typeof node === "string") return node;
|
|
1069
|
+
if (node.type === "text") return node.value;
|
|
1070
|
+
return (node.children ?? []).map(textFromNode).join("");
|
|
1071
|
+
}
|
|
1072
|
+
function collectMarks(node, marks) {
|
|
1073
|
+
if (typeof node === "string" || node.type === "text") return;
|
|
1074
|
+
if (node.type === "mark" && node.name) marks.push(node.name);
|
|
1075
|
+
if (node.type === "bookmark" && node.mark) marks.push(node.mark);
|
|
1076
|
+
for (const child of node.children ?? []) collectMarks(child, marks);
|
|
1077
|
+
}
|
|
1078
|
+
function collectInheritedContext(nodes) {
|
|
1079
|
+
const context = {};
|
|
1080
|
+
const visit = (node) => {
|
|
1081
|
+
if (typeof node === "string" || node.type === "text") return;
|
|
1082
|
+
if (context.voice === void 0 && node.type === "voice" && node.name) context.voice = node.name;
|
|
1083
|
+
if (context.lang === void 0 && node.type === "lang" && node.lang) context.lang = node.lang;
|
|
1084
|
+
if (context.prosody === void 0 && node.type === "prosody") {
|
|
1085
|
+
const prosody = {};
|
|
1086
|
+
for (const [key, value] of Object.entries(node.attributes ?? {})) prosody[key] = String(value);
|
|
1087
|
+
for (const key of ["rate", "pitch", "volume", "contour", "range"]) {
|
|
1088
|
+
const value = node[key];
|
|
1089
|
+
if (value !== void 0) prosody[key] = String(value);
|
|
1090
|
+
}
|
|
1091
|
+
if (Object.keys(prosody).length > 0) context.prosody = prosody;
|
|
1092
|
+
}
|
|
1093
|
+
for (const child of node.children ?? []) visit(child);
|
|
1094
|
+
};
|
|
1095
|
+
nodes.forEach(visit);
|
|
1096
|
+
return context;
|
|
1097
|
+
}
|
|
1098
|
+
function createChunk(document, nodes, chunkIndex, textStart, backgroundAudio, replicateBackgroundAudio) {
|
|
1099
|
+
const chunkNodes = backgroundAudio && (replicateBackgroundAudio || chunkIndex === 0) ? [backgroundAudio, ...nodes] : nodes;
|
|
1100
|
+
const text = nodes.map(textFromNode).join("");
|
|
1101
|
+
const marks = [];
|
|
1102
|
+
for (const node of nodes) collectMarks(node, marks);
|
|
1103
|
+
const inheritedContext = collectInheritedContext(nodes);
|
|
1104
|
+
if (inheritedContext.lang === void 0 && document.lang) inheritedContext.lang = document.lang;
|
|
1105
|
+
return {
|
|
1106
|
+
chunkIndex,
|
|
1107
|
+
ssml: documentWithChildren(document, chunkNodes),
|
|
1108
|
+
originalTextRange: { start: textStart, end: textStart + text.length },
|
|
1109
|
+
inheritedContext,
|
|
1110
|
+
containedMarks: marks,
|
|
1111
|
+
hasBackgroundAudio: chunkNodes.some(
|
|
1112
|
+
(node) => typeof node !== "string" && node.type !== "text" && node.type === "mstts:backgroundaudio"
|
|
1113
|
+
)
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
function splitSsmlDocument(ssml, maxLength = DEFAULT_MAX_LENGTH, options = {}) {
|
|
1117
|
+
const resolvedMaxLength = typeof maxLength === "number" ? maxLength : maxLength.maxLength ?? DEFAULT_MAX_LENGTH;
|
|
1118
|
+
const resolvedOptions = typeof maxLength === "number" ? options : maxLength;
|
|
1119
|
+
if (!Number.isInteger(resolvedMaxLength) || resolvedMaxLength <= 0) {
|
|
1066
1120
|
throw new RangeError("maxLength must be a positive integer");
|
|
1067
1121
|
}
|
|
1068
1122
|
const document = parseSsml(ssml);
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1123
|
+
const backgroundAudio = (document.children ?? []).find(
|
|
1124
|
+
(node) => typeof node !== "string" && node.type !== "text" && node.type === "mstts:backgroundaudio"
|
|
1125
|
+
);
|
|
1126
|
+
if (ssml.length <= resolvedMaxLength) {
|
|
1127
|
+
return [createChunk(document, document.children ?? [], 0, 0, backgroundAudio, true)];
|
|
1128
|
+
}
|
|
1129
|
+
const contentChildren = (document.children ?? []).filter((node) => node !== backgroundAudio);
|
|
1130
|
+
const plainDocumentLength = documentWithChildren(document, []).length;
|
|
1131
|
+
const backgroundDocumentLength = backgroundAudio ? documentWithChildren(document, [backgroundAudio]).length : plainDocumentLength;
|
|
1132
|
+
const backgroundOverhead = Math.max(0, backgroundDocumentLength - plainDocumentLength);
|
|
1133
|
+
const contentMaxLength = Math.max(1, resolvedMaxLength - backgroundOverhead);
|
|
1134
|
+
const splitChildren = contentChildren.flatMap((child) => splitNode(document, child, contentMaxLength));
|
|
1072
1135
|
const chunks = [];
|
|
1073
1136
|
let group = [];
|
|
1074
1137
|
for (const child of splitChildren) {
|
|
1075
1138
|
const candidate = [...group, child];
|
|
1076
|
-
if (documentWithChildren(document, candidate).length <=
|
|
1139
|
+
if (documentWithChildren(document, candidate).length <= contentMaxLength) {
|
|
1077
1140
|
group = candidate;
|
|
1078
1141
|
continue;
|
|
1079
1142
|
}
|
|
1080
1143
|
if (group.length > 0) chunks.push(group);
|
|
1081
1144
|
group = [child];
|
|
1082
|
-
if (documentWithChildren(document, group).length >
|
|
1145
|
+
if (documentWithChildren(document, group).length > contentMaxLength) {
|
|
1083
1146
|
throw new RangeError("maxLength is too small to contain the SSML document wrapper");
|
|
1084
1147
|
}
|
|
1085
1148
|
}
|
|
1086
1149
|
if (group.length > 0) chunks.push(group);
|
|
1087
|
-
|
|
1150
|
+
if (chunks.length === 0) {
|
|
1151
|
+
const result = createChunk(document, [], 0, 0, backgroundAudio, resolvedOptions.replicateBackgroundAudio ?? false);
|
|
1152
|
+
if (result.ssml.length > resolvedMaxLength) {
|
|
1153
|
+
throw new RangeError("maxLength is too small to contain the SSML document wrapper");
|
|
1154
|
+
}
|
|
1155
|
+
return [result];
|
|
1156
|
+
}
|
|
1157
|
+
let textStart = 0;
|
|
1158
|
+
return chunks.map((chunk, chunkIndex) => {
|
|
1159
|
+
const result = createChunk(
|
|
1160
|
+
document,
|
|
1161
|
+
chunk,
|
|
1162
|
+
chunkIndex,
|
|
1163
|
+
textStart,
|
|
1164
|
+
backgroundAudio,
|
|
1165
|
+
resolvedOptions.replicateBackgroundAudio ?? false
|
|
1166
|
+
);
|
|
1167
|
+
textStart = result.originalTextRange.end;
|
|
1168
|
+
return result;
|
|
1169
|
+
});
|
|
1088
1170
|
}
|
|
1089
1171
|
|
|
1090
1172
|
// packages/ssml-core/src/validation.ts
|
|
@@ -2175,7 +2257,7 @@ function validateElement(token, source, diagnostics, voiceName, options, voiceCa
|
|
|
2175
2257
|
addDiagnostic(diagnostics, source, token.start, "<mstts:backgroundaudio> must be self-closing.");
|
|
2176
2258
|
}
|
|
2177
2259
|
}
|
|
2178
|
-
function
|
|
2260
|
+
function validateAzureSsmlStatic(ssml, options = {}) {
|
|
2179
2261
|
const diagnostics = [];
|
|
2180
2262
|
if (typeof ssml !== "string") {
|
|
2181
2263
|
return [
|
|
@@ -2299,6 +2381,51 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
2299
2381
|
}
|
|
2300
2382
|
return diagnostics;
|
|
2301
2383
|
}
|
|
2384
|
+
function urlAttributes(token) {
|
|
2385
|
+
const tag = canonicalTagName(token.name);
|
|
2386
|
+
const attributes = tag === "audio" || tag === "mstts:backgroundaudio" ? ["src"] : tag === "lexicon" ? ["uri"] : tag === "mstts:voiceconversion" ? ["url"] : [];
|
|
2387
|
+
return attributes.flatMap((attribute) => {
|
|
2388
|
+
const value = attr(token, attribute);
|
|
2389
|
+
return value === void 0 ? [] : [{ attribute, value }];
|
|
2390
|
+
});
|
|
2391
|
+
}
|
|
2392
|
+
function validateAzureSsml(ssml, options = {}) {
|
|
2393
|
+
const diagnostics = validateAzureSsmlStatic(ssml, options);
|
|
2394
|
+
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
2395
|
+
if (!validator || typeof ssml !== "string") return diagnostics;
|
|
2396
|
+
let tokens;
|
|
2397
|
+
try {
|
|
2398
|
+
tokens = tokenizeElements(ssml);
|
|
2399
|
+
} catch {
|
|
2400
|
+
return diagnostics;
|
|
2401
|
+
}
|
|
2402
|
+
const checks = tokens.flatMap(
|
|
2403
|
+
(token) => urlAttributes(token).map(async ({ attribute, value }) => {
|
|
2404
|
+
try {
|
|
2405
|
+
const result = await validator(value, { tag: token.name, attribute });
|
|
2406
|
+
const valid = typeof result === "boolean" ? result : result.valid;
|
|
2407
|
+
if (!valid) {
|
|
2408
|
+
const reason = typeof result === "boolean" ? void 0 : result.reason;
|
|
2409
|
+
addDiagnostic(
|
|
2410
|
+
diagnostics,
|
|
2411
|
+
ssml,
|
|
2412
|
+
token.start,
|
|
2413
|
+
`<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
|
|
2414
|
+
);
|
|
2415
|
+
}
|
|
2416
|
+
} catch (error) {
|
|
2417
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
2418
|
+
addDiagnostic(
|
|
2419
|
+
diagnostics,
|
|
2420
|
+
ssml,
|
|
2421
|
+
token.start,
|
|
2422
|
+
`<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
|
|
2423
|
+
);
|
|
2424
|
+
}
|
|
2425
|
+
})
|
|
2426
|
+
);
|
|
2427
|
+
return Promise.all(checks).then(() => diagnostics);
|
|
2428
|
+
}
|
|
2302
2429
|
|
|
2303
2430
|
// packages/ssml-core/src/generated/azureVoiceCatalog.ts
|
|
2304
2431
|
var AZURE_VOICE_CATALOG_METADATA = {
|
|
@@ -2485,12 +2612,23 @@ async function synthesizeSsml(ssml, config) {
|
|
|
2485
2612
|
...(bookmarks ?? []).map((bookmark) => bookmark.audioOffsetMs)
|
|
2486
2613
|
);
|
|
2487
2614
|
const durationMs = result.audioDuration ? ticksToMilliseconds(result.audioDuration) : eventDurationMs;
|
|
2615
|
+
const requestId = result.resultId;
|
|
2616
|
+
const addSourceMetadata = (event) => ({
|
|
2617
|
+
...event,
|
|
2618
|
+
...config.sourceTextRange ? { textRange: { ...config.sourceTextRange } } : {},
|
|
2619
|
+
...requestId ? { requestId } : {}
|
|
2620
|
+
});
|
|
2621
|
+
const sourceBoundaries = boundaries.map((boundary) => addSourceMetadata(boundary));
|
|
2622
|
+
const sourceVisemes = visemes.map((viseme) => addSourceMetadata(viseme));
|
|
2623
|
+
const sourceBookmarks = bookmarks.map((bookmark) => addSourceMetadata(bookmark));
|
|
2488
2624
|
resolve({
|
|
2489
2625
|
audioData: result.audioData,
|
|
2490
2626
|
durationMs,
|
|
2491
|
-
...
|
|
2492
|
-
...
|
|
2493
|
-
...
|
|
2627
|
+
...config.sourceTextRange ? { textRange: { ...config.sourceTextRange } } : {},
|
|
2628
|
+
...requestId ? { requestId } : {},
|
|
2629
|
+
...sourceBoundaries.length > 0 ? { boundaries: sourceBoundaries, wordBoundary: sourceBoundaries, wordBoundaries: sourceBoundaries } : {},
|
|
2630
|
+
...sourceVisemes.length > 0 ? { visemes: sourceVisemes } : {},
|
|
2631
|
+
...sourceBookmarks.length > 0 ? { bookmarks: sourceBookmarks } : {}
|
|
2494
2632
|
});
|
|
2495
2633
|
};
|
|
2496
2634
|
try {
|
|
@@ -2510,30 +2648,1591 @@ async function synthesizeSsml(ssml, config) {
|
|
|
2510
2648
|
}
|
|
2511
2649
|
});
|
|
2512
2650
|
}
|
|
2651
|
+
async function synthesizeSsmlChunks(chunks, config) {
|
|
2652
|
+
const results = [];
|
|
2653
|
+
const totalChunks = chunks.length;
|
|
2654
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
2655
|
+
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
2656
|
+
const result = await synthesizeSsml(input.ssml, {
|
|
2657
|
+
...config,
|
|
2658
|
+
...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
|
|
2659
|
+
onProgress: void 0
|
|
2660
|
+
});
|
|
2661
|
+
results.push(result);
|
|
2662
|
+
config.onProgress?.({
|
|
2663
|
+
currentChunk: index + 1,
|
|
2664
|
+
totalChunks,
|
|
2665
|
+
percent: totalChunks === 0 ? 100 : Math.round((index + 1) / totalChunks * 100)
|
|
2666
|
+
});
|
|
2667
|
+
}
|
|
2668
|
+
return mergeSynthesisResults(results);
|
|
2669
|
+
}
|
|
2670
|
+
function mergeSynthesisResults(results) {
|
|
2671
|
+
const audioLength = results.reduce((total, result) => total + result.audioData.byteLength, 0);
|
|
2672
|
+
const audioData = new Uint8Array(audioLength);
|
|
2673
|
+
const boundaries = [];
|
|
2674
|
+
const visemes = [];
|
|
2675
|
+
const bookmarks = [];
|
|
2676
|
+
let byteOffset = 0;
|
|
2677
|
+
let durationOffset = 0;
|
|
2678
|
+
for (const result of results) {
|
|
2679
|
+
audioData.set(new Uint8Array(result.audioData), byteOffset);
|
|
2680
|
+
byteOffset += result.audioData.byteLength;
|
|
2681
|
+
const chunkBoundaries = result.boundaries && result.boundaries.length > 0 ? result.boundaries : result.wordBoundary ?? result.wordBoundaries ?? [];
|
|
2682
|
+
for (const boundary of chunkBoundaries) {
|
|
2683
|
+
const textRange = boundary.textRange ?? result.textRange;
|
|
2684
|
+
const requestId = boundary.requestId ?? result.requestId;
|
|
2685
|
+
boundaries.push({
|
|
2686
|
+
...boundary,
|
|
2687
|
+
audioOffsetMs: boundary.audioOffsetMs + durationOffset,
|
|
2688
|
+
...textRange ? { textRange: { ...textRange } } : {},
|
|
2689
|
+
...requestId ? { requestId } : {}
|
|
2690
|
+
});
|
|
2691
|
+
}
|
|
2692
|
+
for (const viseme of result.visemes ?? []) {
|
|
2693
|
+
const textRange = viseme.textRange ?? result.textRange;
|
|
2694
|
+
const requestId = viseme.requestId ?? result.requestId;
|
|
2695
|
+
visemes.push({
|
|
2696
|
+
...viseme,
|
|
2697
|
+
audioOffsetMs: viseme.audioOffsetMs + durationOffset,
|
|
2698
|
+
...textRange ? { textRange: { ...textRange } } : {},
|
|
2699
|
+
...requestId ? { requestId } : {}
|
|
2700
|
+
});
|
|
2701
|
+
}
|
|
2702
|
+
for (const bookmark of result.bookmarks ?? []) {
|
|
2703
|
+
const textRange = bookmark.textRange ?? result.textRange;
|
|
2704
|
+
const requestId = bookmark.requestId ?? result.requestId;
|
|
2705
|
+
bookmarks.push({
|
|
2706
|
+
...bookmark,
|
|
2707
|
+
audioOffsetMs: bookmark.audioOffsetMs + durationOffset,
|
|
2708
|
+
...textRange ? { textRange: { ...textRange } } : {},
|
|
2709
|
+
...requestId ? { requestId } : {}
|
|
2710
|
+
});
|
|
2711
|
+
}
|
|
2712
|
+
durationOffset += Math.max(0, result.durationMs);
|
|
2713
|
+
}
|
|
2714
|
+
return {
|
|
2715
|
+
audioData: audioData.buffer,
|
|
2716
|
+
durationMs: durationOffset,
|
|
2717
|
+
...boundaries.length > 0 ? { boundaries, wordBoundary: boundaries, wordBoundaries: boundaries } : {},
|
|
2718
|
+
...visemes.length > 0 ? { visemes } : {},
|
|
2719
|
+
...bookmarks.length > 0 ? { bookmarks } : {},
|
|
2720
|
+
...results.length === 1 && results[0]?.requestId ? { requestId: results[0].requestId } : {},
|
|
2721
|
+
...results.length === 1 && results[0]?.textRange ? { textRange: { ...results[0].textRange } } : {}
|
|
2722
|
+
};
|
|
2723
|
+
}
|
|
2513
2724
|
async function synthesizeSpeech(ssml, config) {
|
|
2514
2725
|
return (await synthesizeSsml(ssml, config)).audioData;
|
|
2515
2726
|
}
|
|
2516
2727
|
|
|
2517
|
-
// packages/
|
|
2518
|
-
var
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
|
|
2728
|
+
// packages/ssml-core/dist/index.mjs
|
|
2729
|
+
var __typeError2 = (msg) => {
|
|
2730
|
+
throw TypeError(msg);
|
|
2731
|
+
};
|
|
2732
|
+
var __accessCheck2 = (obj, member, msg) => member.has(obj) || __typeError2("Cannot " + msg);
|
|
2733
|
+
var __privateGet2 = (obj, member, getter) => (__accessCheck2(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
|
|
2734
|
+
var __privateAdd2 = (obj, member, value) => member.has(obj) ? __typeError2("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
|
|
2735
|
+
var __privateSet2 = (obj, member, value, setter) => (__accessCheck2(obj, member, "write to private field"), setter ? setter.call(obj, value) : member.set(obj, value), value);
|
|
2736
|
+
var SYNTHESIS_NAMESPACE2 = "http://www.w3.org/2001/10/synthesis";
|
|
2737
|
+
var MSTTS_NAMESPACE2 = "http://www.w3.org/2001/mstts";
|
|
2738
|
+
var MAX_NESTING_DEPTH2 = 1e3;
|
|
2739
|
+
var SSML_TAGS2 = {
|
|
2740
|
+
SPEAK: "speak",
|
|
2741
|
+
VOICE: "voice",
|
|
2742
|
+
PROSODY: "prosody",
|
|
2743
|
+
BREAK: "break",
|
|
2744
|
+
EXPRESS_AS: "express-as",
|
|
2745
|
+
EXPRESS_AS_CAMEL: "expressAs",
|
|
2746
|
+
MSTTS_EXPRESS_AS: "mstts:express-as",
|
|
2747
|
+
SAY_AS: "say-as",
|
|
2748
|
+
SAY_AS_CAMEL: "sayAs",
|
|
2749
|
+
PHONEME: "phoneme",
|
|
2750
|
+
EMPHASIS: "emphasis",
|
|
2751
|
+
AUDIO: "audio",
|
|
2752
|
+
SUB: "sub",
|
|
2753
|
+
LANG: "lang",
|
|
2754
|
+
MARK: "mark",
|
|
2755
|
+
BOOKMARK: "bookmark",
|
|
2756
|
+
LEXICON: "lexicon",
|
|
2757
|
+
PARAGRAPH: "p",
|
|
2758
|
+
SENTENCE: "s",
|
|
2759
|
+
WORD: "w",
|
|
2760
|
+
MSTTS_SILENCE: "mstts:silence",
|
|
2761
|
+
SILENCE: "silence",
|
|
2762
|
+
MSTTS_VISEME: "mstts:viseme",
|
|
2763
|
+
VISEME: "viseme",
|
|
2764
|
+
MSTTS_AUDIO_DURATION: "mstts:audioduration",
|
|
2765
|
+
MSTTS_DIALOG: "mstts:dialog",
|
|
2766
|
+
MSTTS_TURN: "mstts:turn",
|
|
2767
|
+
MSTTS_BACKGROUND_AUDIO: "mstts:backgroundaudio",
|
|
2768
|
+
MSTTS_TTS_EMBEDDING: "mstts:ttsembedding",
|
|
2769
|
+
MSTTS_EMBEDDING: "mstts:embedding",
|
|
2770
|
+
MSTTS_VOICE_CONVERSION: "mstts:voiceconversion"
|
|
2771
|
+
};
|
|
2772
|
+
var SSML_ATTRS2 = {
|
|
2773
|
+
VERSION: "version",
|
|
2774
|
+
XMLNS: "xmlns",
|
|
2775
|
+
XML_LANG: "xml:lang",
|
|
2776
|
+
LANG: "lang",
|
|
2777
|
+
MSTTS_XMLNS: "xmlns:mstts",
|
|
2778
|
+
NAME: "name",
|
|
2779
|
+
VOICE: "voice",
|
|
2780
|
+
SPEAKER: "speaker",
|
|
2781
|
+
EFFECT: "effect",
|
|
2782
|
+
RATE: "rate",
|
|
2783
|
+
PITCH: "pitch",
|
|
2784
|
+
VOLUME: "volume",
|
|
2785
|
+
CONTOUR: "contour",
|
|
2786
|
+
RANGE: "range",
|
|
2787
|
+
TIME: "time",
|
|
2788
|
+
STRENGTH: "strength",
|
|
2789
|
+
STYLE: "style",
|
|
2790
|
+
STYLE_DEGREE: "styledegree",
|
|
2791
|
+
STYLE_DEGREE_CAMEL: "styleDegree",
|
|
2792
|
+
STYLE_DEGREE_HYPHEN: "style-degree",
|
|
2793
|
+
ROLE: "role",
|
|
2794
|
+
INTERPRET_AS: "interpret-as",
|
|
2795
|
+
FORMAT: "format",
|
|
2796
|
+
DETAIL: "detail",
|
|
2797
|
+
ALPHABET: "alphabet",
|
|
2798
|
+
PH: "ph",
|
|
2799
|
+
LEVEL: "level",
|
|
2800
|
+
SRC: "src",
|
|
2801
|
+
DESC: "desc",
|
|
2802
|
+
CLIP_BEGIN: "clipBegin",
|
|
2803
|
+
CLIP_END: "clipEnd",
|
|
2804
|
+
SPEED: "speed",
|
|
2805
|
+
REPEAT_COUNT: "repeatCount",
|
|
2806
|
+
REPEAT_DURATION: "repeatDuration",
|
|
2807
|
+
SOUND_LEVEL: "soundLevel",
|
|
2808
|
+
ALIAS: "alias",
|
|
2809
|
+
MARK: "mark",
|
|
2810
|
+
URI: "uri",
|
|
2811
|
+
ID: "id",
|
|
2812
|
+
MODEL: "model",
|
|
2813
|
+
PROFILE: "profile",
|
|
2814
|
+
URL: "url",
|
|
2815
|
+
SPEAKER_PROFILE_ID: "speakerProfileId",
|
|
2816
|
+
TYPE: "type",
|
|
2817
|
+
VALUE: "value",
|
|
2818
|
+
FADE_IN: "fadein",
|
|
2819
|
+
FADE_OUT: "fadeout"
|
|
2820
|
+
};
|
|
2821
|
+
var XML_ENTITIES2 = {
|
|
2822
|
+
amp: "&",
|
|
2823
|
+
apos: "'",
|
|
2824
|
+
gt: ">",
|
|
2825
|
+
lt: "<",
|
|
2826
|
+
quot: '"'
|
|
2827
|
+
};
|
|
2828
|
+
function hasOwn2(object, property) {
|
|
2829
|
+
return Object.getOwnPropertyDescriptor(object, property) !== void 0;
|
|
2830
|
+
}
|
|
2831
|
+
function setAttribute2(attributes, name, value) {
|
|
2832
|
+
Object.defineProperty(attributes, name, {
|
|
2833
|
+
configurable: true,
|
|
2834
|
+
enumerable: true,
|
|
2835
|
+
value,
|
|
2836
|
+
writable: true
|
|
2837
|
+
});
|
|
2838
|
+
}
|
|
2839
|
+
function decodeEntity2(entity) {
|
|
2840
|
+
const namedValue = hasOwn2(XML_ENTITIES2, entity) ? XML_ENTITIES2[entity] : void 0;
|
|
2841
|
+
if (namedValue !== void 0) {
|
|
2842
|
+
return namedValue;
|
|
2524
2843
|
}
|
|
2525
|
-
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };
|
|
2530
|
-
return synthesizeSpeech(ssml, config);
|
|
2844
|
+
const isHexadecimal = entity.startsWith("#x") || entity.startsWith("#X");
|
|
2845
|
+
const isDecimal = entity.startsWith("#");
|
|
2846
|
+
if (!isHexadecimal && !isDecimal) {
|
|
2847
|
+
throw new Error(`Unknown XML entity: &${entity};`);
|
|
2531
2848
|
}
|
|
2532
|
-
|
|
2533
|
-
|
|
2534
|
-
|
|
2535
|
-
|
|
2536
|
-
|
|
2849
|
+
const digits = entity.slice(isHexadecimal ? 2 : 1);
|
|
2850
|
+
const codePoint = Number.parseInt(digits, isHexadecimal ? 16 : 10);
|
|
2851
|
+
if (!digits || !Number.isInteger(codePoint) || codePoint < 0 || codePoint > 1114111 || codePoint >= 55296 && codePoint <= 57343 || codePoint < 32 && ![9, 10, 13].includes(codePoint)) {
|
|
2852
|
+
throw new Error(`Invalid XML character reference: &${entity};`);
|
|
2853
|
+
}
|
|
2854
|
+
return String.fromCodePoint(codePoint);
|
|
2855
|
+
}
|
|
2856
|
+
function decodeXmlEntities2(value) {
|
|
2857
|
+
let result = "";
|
|
2858
|
+
let start = 0;
|
|
2859
|
+
while (true) {
|
|
2860
|
+
const ampersand = value.indexOf("&", start);
|
|
2861
|
+
if (ampersand === -1) {
|
|
2862
|
+
return result + value.slice(start);
|
|
2863
|
+
}
|
|
2864
|
+
result += value.slice(start, ampersand);
|
|
2865
|
+
const semicolon = value.indexOf(";", ampersand + 1);
|
|
2866
|
+
if (semicolon === -1) {
|
|
2867
|
+
throw new Error("Unterminated XML entity reference");
|
|
2868
|
+
}
|
|
2869
|
+
result += decodeEntity2(value.slice(ampersand + 1, semicolon));
|
|
2870
|
+
start = semicolon + 1;
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
function isXmlNameStart2(value) {
|
|
2874
|
+
return value !== void 0 && /[A-Za-z_]/.test(value);
|
|
2875
|
+
}
|
|
2876
|
+
function isXmlNameCharacter2(value) {
|
|
2877
|
+
return value !== void 0 && /[A-Za-z0-9_.:-]/.test(value);
|
|
2878
|
+
}
|
|
2879
|
+
function isXmlWhitespace2(value) {
|
|
2880
|
+
return value === " " || value === " " || value === "\r" || value === "\n";
|
|
2881
|
+
}
|
|
2882
|
+
function removeStandardNamespaceAttributes2(attributes) {
|
|
2883
|
+
if (attributes[SSML_ATTRS2.XMLNS] === SYNTHESIS_NAMESPACE2) {
|
|
2884
|
+
delete attributes[SSML_ATTRS2.XMLNS];
|
|
2885
|
+
}
|
|
2886
|
+
if (attributes[SSML_ATTRS2.MSTTS_XMLNS] === MSTTS_NAMESPACE2) {
|
|
2887
|
+
delete attributes[SSML_ATTRS2.MSTTS_XMLNS];
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
var _index2;
|
|
2891
|
+
var XmlParser2 = class {
|
|
2892
|
+
constructor(source) {
|
|
2893
|
+
__privateAdd2(this, _index2, 0);
|
|
2894
|
+
this.source = source;
|
|
2895
|
+
}
|
|
2896
|
+
parse() {
|
|
2897
|
+
if (this.source.charCodeAt(0) === 65279) {
|
|
2898
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 1);
|
|
2899
|
+
}
|
|
2900
|
+
this.skipMisc();
|
|
2901
|
+
if (__privateGet2(this, _index2) >= this.source.length) {
|
|
2902
|
+
this.fail("SSML input is empty");
|
|
2903
|
+
}
|
|
2904
|
+
if (this.source[__privateGet2(this, _index2)] !== "<") {
|
|
2905
|
+
this.fail("SSML input must start with an XML element");
|
|
2906
|
+
}
|
|
2907
|
+
const root = this.parseElement(0);
|
|
2908
|
+
this.skipMisc();
|
|
2909
|
+
if (__privateGet2(this, _index2) !== this.source.length) {
|
|
2910
|
+
this.fail("Unexpected content after the root XML element");
|
|
2911
|
+
}
|
|
2912
|
+
return root;
|
|
2913
|
+
}
|
|
2914
|
+
parseElement(depth) {
|
|
2915
|
+
if (depth > MAX_NESTING_DEPTH2) {
|
|
2916
|
+
this.fail("XML nesting depth exceeds the supported limit");
|
|
2917
|
+
}
|
|
2918
|
+
this.expect("<");
|
|
2919
|
+
if (this.source[__privateGet2(this, _index2)] === "/") {
|
|
2920
|
+
this.fail("Unexpected closing XML element");
|
|
2921
|
+
}
|
|
2922
|
+
const name = this.parseName();
|
|
2923
|
+
const { attributes, selfClosing } = this.parseStartTag();
|
|
2924
|
+
if (selfClosing) {
|
|
2925
|
+
return { name, attributes, children: [] };
|
|
2926
|
+
}
|
|
2927
|
+
const children = [];
|
|
2928
|
+
while (__privateGet2(this, _index2) < this.source.length) {
|
|
2929
|
+
if (this.source.startsWith("</", __privateGet2(this, _index2))) {
|
|
2930
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 2);
|
|
2931
|
+
const closingName = this.parseName();
|
|
2932
|
+
this.skipWhitespace();
|
|
2933
|
+
this.expect(">");
|
|
2934
|
+
if (closingName !== name) {
|
|
2935
|
+
this.fail(`Mismatched closing element: expected </${name}> but found </${closingName}>`);
|
|
2936
|
+
}
|
|
2937
|
+
return { name, attributes, children };
|
|
2938
|
+
}
|
|
2939
|
+
if (this.source.startsWith("<!--", __privateGet2(this, _index2))) {
|
|
2940
|
+
this.skipComment();
|
|
2941
|
+
continue;
|
|
2942
|
+
}
|
|
2943
|
+
if (this.source.startsWith("<![CDATA[", __privateGet2(this, _index2))) {
|
|
2944
|
+
this.appendText(children, this.parseCdata());
|
|
2945
|
+
continue;
|
|
2946
|
+
}
|
|
2947
|
+
if (this.source.startsWith("<?", __privateGet2(this, _index2))) {
|
|
2948
|
+
this.skipProcessingInstruction();
|
|
2949
|
+
continue;
|
|
2950
|
+
}
|
|
2951
|
+
if (this.source.startsWith("<!", __privateGet2(this, _index2))) {
|
|
2952
|
+
this.fail("Unsupported XML declaration inside an element");
|
|
2953
|
+
}
|
|
2954
|
+
if (this.source[__privateGet2(this, _index2)] === "<") {
|
|
2955
|
+
children.push(this.parseElement(depth + 1));
|
|
2956
|
+
} else {
|
|
2957
|
+
this.appendText(children, this.parseText());
|
|
2958
|
+
}
|
|
2959
|
+
}
|
|
2960
|
+
this.fail(`Unclosed XML element: <${name}>`);
|
|
2961
|
+
}
|
|
2962
|
+
parseStartTag() {
|
|
2963
|
+
const attributes = {};
|
|
2964
|
+
while (__privateGet2(this, _index2) < this.source.length) {
|
|
2965
|
+
this.skipWhitespace();
|
|
2966
|
+
if (this.source.startsWith("/>", __privateGet2(this, _index2))) {
|
|
2967
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 2);
|
|
2968
|
+
return { attributes, selfClosing: true };
|
|
2969
|
+
}
|
|
2970
|
+
if (this.source[__privateGet2(this, _index2)] === ">") {
|
|
2971
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 1);
|
|
2972
|
+
return { attributes, selfClosing: false };
|
|
2973
|
+
}
|
|
2974
|
+
const name = this.parseName();
|
|
2975
|
+
this.skipWhitespace();
|
|
2976
|
+
this.expect("=");
|
|
2977
|
+
this.skipWhitespace();
|
|
2978
|
+
const quote = this.source[__privateGet2(this, _index2)];
|
|
2979
|
+
if (quote !== '"' && quote !== "'") {
|
|
2980
|
+
this.fail(`XML attribute ${name} must use a quoted value`);
|
|
2981
|
+
}
|
|
2982
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 1);
|
|
2983
|
+
const valueStart = __privateGet2(this, _index2);
|
|
2984
|
+
while (__privateGet2(this, _index2) < this.source.length && this.source[__privateGet2(this, _index2)] !== quote) {
|
|
2985
|
+
if (this.source[__privateGet2(this, _index2)] === "<") {
|
|
2986
|
+
this.fail(`Invalid "<" in XML attribute ${name}`);
|
|
2987
|
+
}
|
|
2988
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 1);
|
|
2989
|
+
}
|
|
2990
|
+
if (__privateGet2(this, _index2) >= this.source.length) {
|
|
2991
|
+
this.fail(`Unclosed XML attribute ${name}`);
|
|
2992
|
+
}
|
|
2993
|
+
const value = decodeXmlEntities2(this.source.slice(valueStart, __privateGet2(this, _index2)));
|
|
2994
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 1);
|
|
2995
|
+
if (hasOwn2(attributes, name)) {
|
|
2996
|
+
this.fail(`Duplicate XML attribute: ${name}`);
|
|
2997
|
+
}
|
|
2998
|
+
setAttribute2(attributes, name, value);
|
|
2999
|
+
}
|
|
3000
|
+
this.fail("Unclosed XML start tag");
|
|
3001
|
+
}
|
|
3002
|
+
parseText() {
|
|
3003
|
+
const start = __privateGet2(this, _index2);
|
|
3004
|
+
while (__privateGet2(this, _index2) < this.source.length && this.source[__privateGet2(this, _index2)] !== "<") {
|
|
3005
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 1);
|
|
3006
|
+
}
|
|
3007
|
+
const value = this.source.slice(start, __privateGet2(this, _index2));
|
|
3008
|
+
if (value.includes("]]>")) {
|
|
3009
|
+
this.fail("CDATA termination is not valid in ordinary XML text");
|
|
3010
|
+
}
|
|
3011
|
+
return decodeXmlEntities2(value);
|
|
3012
|
+
}
|
|
3013
|
+
parseCdata() {
|
|
3014
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + "<![CDATA[".length);
|
|
3015
|
+
const end = this.source.indexOf("]]>", __privateGet2(this, _index2));
|
|
3016
|
+
if (end === -1) {
|
|
3017
|
+
this.fail("Unclosed XML CDATA section");
|
|
3018
|
+
}
|
|
3019
|
+
const value = this.source.slice(__privateGet2(this, _index2), end);
|
|
3020
|
+
__privateSet2(this, _index2, end + 3);
|
|
3021
|
+
return value;
|
|
3022
|
+
}
|
|
3023
|
+
skipComment() {
|
|
3024
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + "<!--".length);
|
|
3025
|
+
const end = this.source.indexOf("-->", __privateGet2(this, _index2));
|
|
3026
|
+
if (end === -1) {
|
|
3027
|
+
this.fail("Unclosed XML comment");
|
|
3028
|
+
}
|
|
3029
|
+
if (this.source.slice(__privateGet2(this, _index2), end).includes("--")) {
|
|
3030
|
+
this.fail("XML comments cannot contain consecutive hyphens");
|
|
3031
|
+
}
|
|
3032
|
+
__privateSet2(this, _index2, end + 3);
|
|
3033
|
+
}
|
|
3034
|
+
skipProcessingInstruction() {
|
|
3035
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + "<?".length);
|
|
3036
|
+
this.parseName();
|
|
3037
|
+
const end = this.source.indexOf("?>", __privateGet2(this, _index2));
|
|
3038
|
+
if (end === -1) {
|
|
3039
|
+
this.fail("Unclosed XML processing instruction");
|
|
3040
|
+
}
|
|
3041
|
+
__privateSet2(this, _index2, end + 2);
|
|
3042
|
+
}
|
|
3043
|
+
skipMisc() {
|
|
3044
|
+
while (__privateGet2(this, _index2) < this.source.length) {
|
|
3045
|
+
this.skipWhitespace();
|
|
3046
|
+
if (this.source.startsWith("<!--", __privateGet2(this, _index2))) {
|
|
3047
|
+
this.skipComment();
|
|
3048
|
+
continue;
|
|
3049
|
+
}
|
|
3050
|
+
if (this.source.startsWith("<?", __privateGet2(this, _index2))) {
|
|
3051
|
+
this.skipProcessingInstruction();
|
|
3052
|
+
continue;
|
|
3053
|
+
}
|
|
3054
|
+
if (this.source.startsWith("<!DOCTYPE", __privateGet2(this, _index2))) {
|
|
3055
|
+
this.fail("DOCTYPE declarations are not supported");
|
|
3056
|
+
}
|
|
3057
|
+
break;
|
|
3058
|
+
}
|
|
3059
|
+
}
|
|
3060
|
+
parseName() {
|
|
3061
|
+
const first = this.source[__privateGet2(this, _index2)];
|
|
3062
|
+
if (!isXmlNameStart2(first)) {
|
|
3063
|
+
this.fail("Invalid XML name");
|
|
3064
|
+
}
|
|
3065
|
+
const start = __privateGet2(this, _index2);
|
|
3066
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 1);
|
|
3067
|
+
while (isXmlNameCharacter2(this.source[__privateGet2(this, _index2)])) {
|
|
3068
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 1);
|
|
3069
|
+
}
|
|
3070
|
+
return this.source.slice(start, __privateGet2(this, _index2));
|
|
3071
|
+
}
|
|
3072
|
+
appendText(children, value) {
|
|
3073
|
+
if (!value) {
|
|
3074
|
+
return;
|
|
3075
|
+
}
|
|
3076
|
+
const previous = children[children.length - 1];
|
|
3077
|
+
if (typeof previous === "string") {
|
|
3078
|
+
children[children.length - 1] = previous + value;
|
|
3079
|
+
} else {
|
|
3080
|
+
children.push(value);
|
|
3081
|
+
}
|
|
3082
|
+
}
|
|
3083
|
+
skipWhitespace() {
|
|
3084
|
+
while (isXmlWhitespace2(this.source[__privateGet2(this, _index2)])) {
|
|
3085
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + 1);
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
expect(value) {
|
|
3089
|
+
if (!this.source.startsWith(value, __privateGet2(this, _index2))) {
|
|
3090
|
+
this.fail(`Expected "${value}"`);
|
|
3091
|
+
}
|
|
3092
|
+
__privateSet2(this, _index2, __privateGet2(this, _index2) + value.length);
|
|
3093
|
+
}
|
|
3094
|
+
fail(message) {
|
|
3095
|
+
throw new Error(`${message} at position ${__privateGet2(this, _index2)}`);
|
|
3096
|
+
}
|
|
3097
|
+
};
|
|
3098
|
+
_index2 = /* @__PURE__ */ new WeakMap();
|
|
3099
|
+
function readAttribute2(attributes, ...names) {
|
|
3100
|
+
let found = false;
|
|
3101
|
+
let value;
|
|
3102
|
+
for (const name of names) {
|
|
3103
|
+
if (hasOwn2(attributes, name)) {
|
|
3104
|
+
if (!found) {
|
|
3105
|
+
value = String(attributes[name]);
|
|
3106
|
+
found = true;
|
|
3107
|
+
}
|
|
3108
|
+
delete attributes[name];
|
|
3109
|
+
}
|
|
3110
|
+
}
|
|
3111
|
+
return value;
|
|
3112
|
+
}
|
|
3113
|
+
function getElementAttributes2(node) {
|
|
3114
|
+
const attributes = { ...node.attributes };
|
|
3115
|
+
removeStandardNamespaceAttributes2(attributes);
|
|
3116
|
+
return attributes;
|
|
3117
|
+
}
|
|
3118
|
+
function finishElement2(element, node, attributes) {
|
|
3119
|
+
if (node.children.length > 0) {
|
|
3120
|
+
element.children = node.children.map(convertNode2);
|
|
3121
|
+
}
|
|
3122
|
+
if (Object.keys(attributes).length > 0) {
|
|
3123
|
+
element.attributes = attributes;
|
|
3124
|
+
}
|
|
3125
|
+
return element;
|
|
3126
|
+
}
|
|
3127
|
+
function convertElement2(node) {
|
|
3128
|
+
const attributes = getElementAttributes2(node);
|
|
3129
|
+
switch (node.name) {
|
|
3130
|
+
case SSML_TAGS2.VOICE: {
|
|
3131
|
+
const element = { type: SSML_TAGS2.VOICE };
|
|
3132
|
+
const name = readAttribute2(attributes, SSML_ATTRS2.NAME);
|
|
3133
|
+
const effect = readAttribute2(attributes, SSML_ATTRS2.EFFECT);
|
|
3134
|
+
if (name !== void 0) element.name = name;
|
|
3135
|
+
if (effect !== void 0) element.effect = effect;
|
|
3136
|
+
return finishElement2(element, node, attributes);
|
|
3137
|
+
}
|
|
3138
|
+
case SSML_TAGS2.PROSODY: {
|
|
3139
|
+
const element = { type: SSML_TAGS2.PROSODY };
|
|
3140
|
+
const rate = readAttribute2(attributes, SSML_ATTRS2.RATE);
|
|
3141
|
+
const pitch = readAttribute2(attributes, SSML_ATTRS2.PITCH);
|
|
3142
|
+
const volume = readAttribute2(attributes, SSML_ATTRS2.VOLUME);
|
|
3143
|
+
const contour = readAttribute2(attributes, SSML_ATTRS2.CONTOUR);
|
|
3144
|
+
const range = readAttribute2(attributes, SSML_ATTRS2.RANGE);
|
|
3145
|
+
if (rate !== void 0) element.rate = rate;
|
|
3146
|
+
if (pitch !== void 0) element.pitch = pitch;
|
|
3147
|
+
if (volume !== void 0) element.volume = volume;
|
|
3148
|
+
if (contour !== void 0) element.contour = contour;
|
|
3149
|
+
if (range !== void 0) element.range = range;
|
|
3150
|
+
return finishElement2(element, node, attributes);
|
|
3151
|
+
}
|
|
3152
|
+
case SSML_TAGS2.BREAK: {
|
|
3153
|
+
const element = { type: SSML_TAGS2.BREAK };
|
|
3154
|
+
const time = readAttribute2(attributes, SSML_ATTRS2.TIME);
|
|
3155
|
+
const strength = readAttribute2(attributes, SSML_ATTRS2.STRENGTH);
|
|
3156
|
+
if (time !== void 0) element.time = time;
|
|
3157
|
+
if (strength !== void 0) element.strength = strength;
|
|
3158
|
+
return finishElement2(element, node, attributes);
|
|
3159
|
+
}
|
|
3160
|
+
case SSML_TAGS2.EXPRESS_AS:
|
|
3161
|
+
case SSML_TAGS2.EXPRESS_AS_CAMEL:
|
|
3162
|
+
case SSML_TAGS2.MSTTS_EXPRESS_AS: {
|
|
3163
|
+
const element = { type: node.name };
|
|
3164
|
+
const style = readAttribute2(attributes, SSML_ATTRS2.STYLE);
|
|
3165
|
+
const styleDegree = readAttribute2(
|
|
3166
|
+
attributes,
|
|
3167
|
+
SSML_ATTRS2.STYLE_DEGREE,
|
|
3168
|
+
SSML_ATTRS2.STYLE_DEGREE_CAMEL,
|
|
3169
|
+
SSML_ATTRS2.STYLE_DEGREE_HYPHEN
|
|
3170
|
+
);
|
|
3171
|
+
const role = readAttribute2(attributes, SSML_ATTRS2.ROLE);
|
|
3172
|
+
if (style !== void 0) element.style = style;
|
|
3173
|
+
if (styleDegree !== void 0) element.styleDegree = styleDegree;
|
|
3174
|
+
if (role !== void 0) element.role = role;
|
|
3175
|
+
return finishElement2(element, node, attributes);
|
|
3176
|
+
}
|
|
3177
|
+
case SSML_TAGS2.SAY_AS:
|
|
3178
|
+
case SSML_TAGS2.SAY_AS_CAMEL: {
|
|
3179
|
+
const element = { type: node.name };
|
|
3180
|
+
const interpretAs = readAttribute2(attributes, SSML_ATTRS2.INTERPRET_AS);
|
|
3181
|
+
const format = readAttribute2(attributes, SSML_ATTRS2.FORMAT);
|
|
3182
|
+
const detail = readAttribute2(attributes, SSML_ATTRS2.DETAIL);
|
|
3183
|
+
if (interpretAs !== void 0) element.interpretAs = interpretAs;
|
|
3184
|
+
if (format !== void 0) element.format = format;
|
|
3185
|
+
if (detail !== void 0) element.detail = detail;
|
|
3186
|
+
return finishElement2(element, node, attributes);
|
|
3187
|
+
}
|
|
3188
|
+
case SSML_TAGS2.PHONEME: {
|
|
3189
|
+
const element = { type: SSML_TAGS2.PHONEME };
|
|
3190
|
+
const alphabet = readAttribute2(attributes, SSML_ATTRS2.ALPHABET);
|
|
3191
|
+
const ph = readAttribute2(attributes, SSML_ATTRS2.PH);
|
|
3192
|
+
if (alphabet !== void 0) element.alphabet = alphabet;
|
|
3193
|
+
if (ph !== void 0) element.ph = ph;
|
|
3194
|
+
return finishElement2(element, node, attributes);
|
|
3195
|
+
}
|
|
3196
|
+
case SSML_TAGS2.EMPHASIS: {
|
|
3197
|
+
const element = { type: SSML_TAGS2.EMPHASIS };
|
|
3198
|
+
const level = readAttribute2(attributes, SSML_ATTRS2.LEVEL);
|
|
3199
|
+
if (level !== void 0) element.level = level;
|
|
3200
|
+
return finishElement2(element, node, attributes);
|
|
3201
|
+
}
|
|
3202
|
+
case SSML_TAGS2.AUDIO: {
|
|
3203
|
+
const element = { type: SSML_TAGS2.AUDIO };
|
|
3204
|
+
const src = readAttribute2(attributes, SSML_ATTRS2.SRC);
|
|
3205
|
+
const desc = readAttribute2(attributes, SSML_ATTRS2.DESC);
|
|
3206
|
+
const clipBegin = readAttribute2(attributes, SSML_ATTRS2.CLIP_BEGIN);
|
|
3207
|
+
const clipEnd = readAttribute2(attributes, SSML_ATTRS2.CLIP_END);
|
|
3208
|
+
const speed = readAttribute2(attributes, SSML_ATTRS2.SPEED);
|
|
3209
|
+
const repeatCount = readAttribute2(attributes, SSML_ATTRS2.REPEAT_COUNT);
|
|
3210
|
+
const repeatDuration = readAttribute2(attributes, SSML_ATTRS2.REPEAT_DURATION);
|
|
3211
|
+
const soundLevel = readAttribute2(attributes, SSML_ATTRS2.SOUND_LEVEL);
|
|
3212
|
+
if (src !== void 0) element.src = src;
|
|
3213
|
+
if (desc !== void 0) element.desc = desc;
|
|
3214
|
+
if (clipBegin !== void 0) element.clipBegin = clipBegin;
|
|
3215
|
+
if (clipEnd !== void 0) element.clipEnd = clipEnd;
|
|
3216
|
+
if (speed !== void 0) element.speed = speed;
|
|
3217
|
+
if (repeatCount !== void 0) element.repeatCount = repeatCount;
|
|
3218
|
+
if (repeatDuration !== void 0) element.repeatDuration = repeatDuration;
|
|
3219
|
+
if (soundLevel !== void 0) element.soundLevel = soundLevel;
|
|
3220
|
+
return finishElement2(element, node, attributes);
|
|
3221
|
+
}
|
|
3222
|
+
case SSML_TAGS2.SUB: {
|
|
3223
|
+
const element = { type: SSML_TAGS2.SUB };
|
|
3224
|
+
const alias = readAttribute2(attributes, SSML_ATTRS2.ALIAS);
|
|
3225
|
+
if (alias !== void 0) element.alias = alias;
|
|
3226
|
+
return finishElement2(element, node, attributes);
|
|
3227
|
+
}
|
|
3228
|
+
case SSML_TAGS2.LANG: {
|
|
3229
|
+
const element = { type: SSML_TAGS2.LANG };
|
|
3230
|
+
const lang = readAttribute2(attributes, SSML_ATTRS2.XML_LANG, SSML_ATTRS2.LANG);
|
|
3231
|
+
if (lang !== void 0) element.lang = lang;
|
|
3232
|
+
return finishElement2(element, node, attributes);
|
|
3233
|
+
}
|
|
3234
|
+
case SSML_TAGS2.MARK: {
|
|
3235
|
+
const element = { type: SSML_TAGS2.MARK };
|
|
3236
|
+
const name = readAttribute2(attributes, SSML_ATTRS2.NAME);
|
|
3237
|
+
if (name !== void 0) element.name = name;
|
|
3238
|
+
return finishElement2(element, node, attributes);
|
|
3239
|
+
}
|
|
3240
|
+
case SSML_TAGS2.BOOKMARK: {
|
|
3241
|
+
const element = { type: SSML_TAGS2.BOOKMARK };
|
|
3242
|
+
const mark = readAttribute2(attributes, SSML_ATTRS2.MARK);
|
|
3243
|
+
if (mark !== void 0) element.mark = mark;
|
|
3244
|
+
return finishElement2(element, node, attributes);
|
|
3245
|
+
}
|
|
3246
|
+
case SSML_TAGS2.LEXICON: {
|
|
3247
|
+
const element = { type: SSML_TAGS2.LEXICON };
|
|
3248
|
+
const uri = readAttribute2(attributes, SSML_ATTRS2.URI);
|
|
3249
|
+
if (uri !== void 0) element.uri = uri;
|
|
3250
|
+
return finishElement2(element, node, attributes);
|
|
3251
|
+
}
|
|
3252
|
+
case SSML_TAGS2.PARAGRAPH: {
|
|
3253
|
+
const element = { type: SSML_TAGS2.PARAGRAPH };
|
|
3254
|
+
return finishElement2(element, node, attributes);
|
|
3255
|
+
}
|
|
3256
|
+
case SSML_TAGS2.SENTENCE: {
|
|
3257
|
+
const element = { type: SSML_TAGS2.SENTENCE };
|
|
3258
|
+
return finishElement2(element, node, attributes);
|
|
3259
|
+
}
|
|
3260
|
+
case SSML_TAGS2.WORD: {
|
|
3261
|
+
const element = { type: SSML_TAGS2.WORD };
|
|
3262
|
+
return finishElement2(element, node, attributes);
|
|
3263
|
+
}
|
|
3264
|
+
case SSML_TAGS2.MSTTS_SILENCE:
|
|
3265
|
+
case SSML_TAGS2.SILENCE: {
|
|
3266
|
+
const element = {
|
|
3267
|
+
type: node.name === SSML_TAGS2.MSTTS_SILENCE ? SSML_TAGS2.MSTTS_SILENCE : SSML_TAGS2.SILENCE
|
|
3268
|
+
};
|
|
3269
|
+
const typeValue = readAttribute2(attributes, SSML_ATTRS2.TYPE);
|
|
3270
|
+
const value = readAttribute2(attributes, SSML_ATTRS2.VALUE);
|
|
3271
|
+
if (typeValue !== void 0) element.typeValue = typeValue;
|
|
3272
|
+
if (value !== void 0) element.value = value;
|
|
3273
|
+
return finishElement2(element, node, attributes);
|
|
3274
|
+
}
|
|
3275
|
+
case SSML_TAGS2.MSTTS_VISEME:
|
|
3276
|
+
case SSML_TAGS2.VISEME: {
|
|
3277
|
+
const element = {
|
|
3278
|
+
type: node.name === SSML_TAGS2.MSTTS_VISEME ? SSML_TAGS2.MSTTS_VISEME : SSML_TAGS2.VISEME
|
|
3279
|
+
};
|
|
3280
|
+
const typeValue = readAttribute2(attributes, SSML_ATTRS2.TYPE);
|
|
3281
|
+
if (typeValue !== void 0) element.typeValue = typeValue;
|
|
3282
|
+
return finishElement2(element, node, attributes);
|
|
3283
|
+
}
|
|
3284
|
+
case SSML_TAGS2.MSTTS_AUDIO_DURATION: {
|
|
3285
|
+
const element = { type: SSML_TAGS2.MSTTS_AUDIO_DURATION };
|
|
3286
|
+
const value = readAttribute2(attributes, SSML_ATTRS2.VALUE);
|
|
3287
|
+
if (value !== void 0) element.value = value;
|
|
3288
|
+
return finishElement2(element, node, attributes);
|
|
3289
|
+
}
|
|
3290
|
+
case SSML_TAGS2.MSTTS_DIALOG: {
|
|
3291
|
+
const element = { type: SSML_TAGS2.MSTTS_DIALOG };
|
|
3292
|
+
return finishElement2(element, node, attributes);
|
|
3293
|
+
}
|
|
3294
|
+
case SSML_TAGS2.MSTTS_TURN: {
|
|
3295
|
+
const element = { type: SSML_TAGS2.MSTTS_TURN };
|
|
3296
|
+
const voice = readAttribute2(attributes, SSML_ATTRS2.VOICE);
|
|
3297
|
+
const speaker = readAttribute2(attributes, SSML_ATTRS2.SPEAKER);
|
|
3298
|
+
if (voice !== void 0) element.voice = voice;
|
|
3299
|
+
if (speaker !== void 0) element.speaker = speaker;
|
|
3300
|
+
return finishElement2(element, node, attributes);
|
|
3301
|
+
}
|
|
3302
|
+
case SSML_TAGS2.MSTTS_BACKGROUND_AUDIO: {
|
|
3303
|
+
const element = { type: SSML_TAGS2.MSTTS_BACKGROUND_AUDIO };
|
|
3304
|
+
const src = readAttribute2(attributes, SSML_ATTRS2.SRC);
|
|
3305
|
+
const volume = readAttribute2(attributes, SSML_ATTRS2.VOLUME);
|
|
3306
|
+
const fadeIn = readAttribute2(attributes, SSML_ATTRS2.FADE_IN);
|
|
3307
|
+
const fadeOut = readAttribute2(attributes, SSML_ATTRS2.FADE_OUT);
|
|
3308
|
+
if (src !== void 0) element.src = src;
|
|
3309
|
+
if (volume !== void 0) element.volume = volume;
|
|
3310
|
+
if (fadeIn !== void 0) element.fadeIn = fadeIn;
|
|
3311
|
+
if (fadeOut !== void 0) element.fadeOut = fadeOut;
|
|
3312
|
+
return finishElement2(element, node, attributes);
|
|
3313
|
+
}
|
|
3314
|
+
case SSML_TAGS2.MSTTS_TTS_EMBEDDING: {
|
|
3315
|
+
const element = { type: SSML_TAGS2.MSTTS_TTS_EMBEDDING };
|
|
3316
|
+
const speakerProfileId = readAttribute2(attributes, SSML_ATTRS2.SPEAKER_PROFILE_ID);
|
|
3317
|
+
if (speakerProfileId !== void 0) element.speakerProfileId = speakerProfileId;
|
|
3318
|
+
return finishElement2(element, node, attributes);
|
|
3319
|
+
}
|
|
3320
|
+
case SSML_TAGS2.MSTTS_EMBEDDING: {
|
|
3321
|
+
const element = { type: SSML_TAGS2.MSTTS_EMBEDDING };
|
|
3322
|
+
const id = readAttribute2(attributes, SSML_ATTRS2.ID);
|
|
3323
|
+
const speakerProfileId = readAttribute2(attributes, SSML_ATTRS2.SPEAKER_PROFILE_ID);
|
|
3324
|
+
if (id !== void 0) element.id = id;
|
|
3325
|
+
if (speakerProfileId !== void 0) element.speakerProfileId = speakerProfileId;
|
|
3326
|
+
return finishElement2(element, node, attributes);
|
|
3327
|
+
}
|
|
3328
|
+
case SSML_TAGS2.MSTTS_VOICE_CONVERSION: {
|
|
3329
|
+
const element = { type: SSML_TAGS2.MSTTS_VOICE_CONVERSION };
|
|
3330
|
+
const url = readAttribute2(attributes, SSML_ATTRS2.URL);
|
|
3331
|
+
const profile = readAttribute2(attributes, SSML_ATTRS2.PROFILE);
|
|
3332
|
+
const speakerProfileId = readAttribute2(attributes, SSML_ATTRS2.SPEAKER_PROFILE_ID);
|
|
3333
|
+
if (url !== void 0) element.url = url;
|
|
3334
|
+
if (profile !== void 0) element.profile = profile;
|
|
3335
|
+
if (speakerProfileId !== void 0) element.speakerProfileId = speakerProfileId;
|
|
3336
|
+
return finishElement2(element, node, attributes);
|
|
3337
|
+
}
|
|
3338
|
+
default: {
|
|
3339
|
+
const element = {
|
|
3340
|
+
name: node.name,
|
|
3341
|
+
type: "custom"
|
|
3342
|
+
};
|
|
3343
|
+
return finishElement2(element, node, attributes);
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
}
|
|
3347
|
+
function convertNode2(node) {
|
|
3348
|
+
return typeof node === "string" ? node : convertElement2(node);
|
|
3349
|
+
}
|
|
3350
|
+
function parseSsml2(xmlString) {
|
|
3351
|
+
if (typeof xmlString !== "string") {
|
|
3352
|
+
throw new TypeError("SSML input must be a string");
|
|
3353
|
+
}
|
|
3354
|
+
const root = new XmlParser2(xmlString).parse();
|
|
3355
|
+
if (root.name !== SSML_TAGS2.SPEAK) {
|
|
3356
|
+
throw new Error(`SSML root element must be <${SSML_TAGS2.SPEAK}>, found <${root.name}>`);
|
|
3357
|
+
}
|
|
3358
|
+
const attributes = { ...root.attributes };
|
|
3359
|
+
const version = readAttribute2(attributes, SSML_ATTRS2.VERSION);
|
|
3360
|
+
const lang = readAttribute2(attributes, SSML_ATTRS2.XML_LANG, SSML_ATTRS2.LANG);
|
|
3361
|
+
if (version === void 0) {
|
|
3362
|
+
throw new Error(`SSML <${SSML_TAGS2.SPEAK}> element is missing the "${SSML_ATTRS2.VERSION}" attribute`);
|
|
3363
|
+
}
|
|
3364
|
+
if (lang === void 0) {
|
|
3365
|
+
throw new Error(`SSML <${SSML_TAGS2.SPEAK}> element is missing the "${SSML_ATTRS2.XML_LANG}" attribute`);
|
|
3366
|
+
}
|
|
3367
|
+
removeStandardNamespaceAttributes2(attributes);
|
|
3368
|
+
const document = {
|
|
3369
|
+
children: root.children.map(convertNode2),
|
|
3370
|
+
lang,
|
|
3371
|
+
type: SSML_TAGS2.SPEAK,
|
|
3372
|
+
version
|
|
3373
|
+
};
|
|
3374
|
+
if (Object.keys(attributes).length > 0) {
|
|
3375
|
+
document.attributes = attributes;
|
|
3376
|
+
}
|
|
3377
|
+
return document;
|
|
3378
|
+
}
|
|
3379
|
+
var AZURE_VOICE_DEFINITIONS2 = [
|
|
3380
|
+
{ name: "de-DE-ConradNeural", locale: "de-DE", styles: ["cheerful", "sad"] },
|
|
3381
|
+
{ name: "de-DE-KatjaNeural", locale: "de-DE", styles: ["cheerful", "sad"] },
|
|
3382
|
+
{ name: "en-US-AndrewNeural", locale: "en-US", styles: ["empathetic", "relieved"] },
|
|
3383
|
+
{
|
|
3384
|
+
name: "en-US-GuyNeural",
|
|
3385
|
+
locale: "en-US",
|
|
3386
|
+
styles: [
|
|
3387
|
+
"angry",
|
|
3388
|
+
"cheerful",
|
|
3389
|
+
"excited",
|
|
3390
|
+
"friendly",
|
|
3391
|
+
"hopeful",
|
|
3392
|
+
"newscast",
|
|
3393
|
+
"sad",
|
|
3394
|
+
"shouting",
|
|
3395
|
+
"terrified",
|
|
3396
|
+
"unfriendly",
|
|
3397
|
+
"whispering"
|
|
3398
|
+
]
|
|
3399
|
+
},
|
|
3400
|
+
{
|
|
3401
|
+
name: "en-US-JennyMultilingualNeural",
|
|
3402
|
+
locale: "en-US",
|
|
3403
|
+
styles: [
|
|
3404
|
+
"cheerful",
|
|
3405
|
+
"empathetic",
|
|
3406
|
+
"excited",
|
|
3407
|
+
"friendly",
|
|
3408
|
+
"hopeful",
|
|
3409
|
+
"sad",
|
|
3410
|
+
"shouting",
|
|
3411
|
+
"terrified",
|
|
3412
|
+
"unfriendly",
|
|
3413
|
+
"whispering"
|
|
3414
|
+
]
|
|
3415
|
+
},
|
|
3416
|
+
{
|
|
3417
|
+
name: "en-US-JennyNeural",
|
|
3418
|
+
locale: "en-US",
|
|
3419
|
+
styles: [
|
|
3420
|
+
"assistant",
|
|
3421
|
+
"chat",
|
|
3422
|
+
"customerservice",
|
|
3423
|
+
"newscast",
|
|
3424
|
+
"cheerful",
|
|
3425
|
+
"empathetic",
|
|
3426
|
+
"excited",
|
|
3427
|
+
"friendly",
|
|
3428
|
+
"hopeful",
|
|
3429
|
+
"sad",
|
|
3430
|
+
"shouting",
|
|
3431
|
+
"terrified",
|
|
3432
|
+
"unfriendly",
|
|
3433
|
+
"whispering"
|
|
3434
|
+
]
|
|
3435
|
+
},
|
|
3436
|
+
{ name: "es-ES-ElviraNeural", locale: "es-ES" },
|
|
3437
|
+
{ name: "fil-PH-AngeloNeural", locale: "fil-PH" },
|
|
3438
|
+
{ name: "fil-PH-Angelo:DragonHDLatestNeural", locale: "fil-PH" },
|
|
3439
|
+
{ name: "fil-PH-BlessicaNeural", locale: "fil-PH" },
|
|
3440
|
+
{ name: "fil-PH-Blessica:DragonHDLatestNeural", locale: "fil-PH" },
|
|
3441
|
+
{ name: "fr-FR-DeniseNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
|
|
3442
|
+
{ name: "fr-FR-HenriNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
|
|
3443
|
+
{ name: "id-ID-GadisNeural", locale: "id-ID" },
|
|
3444
|
+
{ name: "it-IT-ElsaNeural", locale: "it-IT", styles: ["cheerful", "sad"] },
|
|
3445
|
+
{ name: "ja-JP-KeitaNeural", locale: "ja-JP", styles: ["chat"] },
|
|
3446
|
+
{ name: "ja-JP-MayuNeural", locale: "ja-JP", styles: ["calm", "cheerful", "sad"] },
|
|
3447
|
+
{ name: "ja-JP-NanamiNeural", locale: "ja-JP", styles: ["chat", "customerservice", "cheerful", "whispering", "sad"] },
|
|
3448
|
+
{ name: "ko-KR-SunHiNeural", locale: "ko-KR", styles: ["cheerful", "sad"] },
|
|
3449
|
+
{ name: "ms-MY-YasminNeural", locale: "ms-MY" },
|
|
3450
|
+
{ name: "pt-BR-FranciscaNeural", locale: "pt-BR", styles: ["calm"] },
|
|
3451
|
+
{
|
|
3452
|
+
name: "ru-RU-SvetlanaNeural",
|
|
3453
|
+
locale: "ru-RU",
|
|
3454
|
+
styles: ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"]
|
|
3455
|
+
},
|
|
3456
|
+
{ name: "th-TH-PremwadeeNeural", locale: "th-TH" },
|
|
3457
|
+
{ name: "vi-VN-HoaiMyNeural", locale: "vi-VN" },
|
|
3458
|
+
{
|
|
3459
|
+
name: "zh-CN-XiaoxiaoNeural",
|
|
3460
|
+
locale: "zh-CN",
|
|
3461
|
+
styles: [
|
|
3462
|
+
"assistant",
|
|
3463
|
+
"chat",
|
|
3464
|
+
"customerservice",
|
|
3465
|
+
"newscast",
|
|
3466
|
+
"cheerful",
|
|
3467
|
+
"empathetic",
|
|
3468
|
+
"excited",
|
|
3469
|
+
"friendly",
|
|
3470
|
+
"hopeful",
|
|
3471
|
+
"sad",
|
|
3472
|
+
"terrified",
|
|
3473
|
+
"whispering",
|
|
3474
|
+
"poetry-reading",
|
|
3475
|
+
"sports_commentary",
|
|
3476
|
+
"sports_commentary_excited",
|
|
3477
|
+
"story"
|
|
3478
|
+
]
|
|
3479
|
+
},
|
|
3480
|
+
{
|
|
3481
|
+
name: "zh-CN-YunxiNeural",
|
|
3482
|
+
locale: "zh-CN",
|
|
3483
|
+
styles: [
|
|
3484
|
+
"narration-relaxed",
|
|
3485
|
+
"embarrassed",
|
|
3486
|
+
"fearful",
|
|
3487
|
+
"sad",
|
|
3488
|
+
"disgruntled",
|
|
3489
|
+
"serious",
|
|
3490
|
+
"angry",
|
|
3491
|
+
"depressed",
|
|
3492
|
+
"chat",
|
|
3493
|
+
"cheerful",
|
|
3494
|
+
"assistant"
|
|
3495
|
+
]
|
|
3496
|
+
},
|
|
3497
|
+
{ name: "zh-TW-HsiaoChenNeural", locale: "zh-TW" }
|
|
3498
|
+
];
|
|
3499
|
+
var ALLOWED_BREAK_STRENGTHS2 = /* @__PURE__ */ new Set(["none", "x-weak", "weak", "medium", "strong", "x-strong"]);
|
|
3500
|
+
var ALLOWED_SAY_AS2 = /* @__PURE__ */ new Set([
|
|
3501
|
+
"characters",
|
|
3502
|
+
"spell-out",
|
|
3503
|
+
"cardinal",
|
|
3504
|
+
"ordinal",
|
|
3505
|
+
"number",
|
|
3506
|
+
"date",
|
|
3507
|
+
"time",
|
|
3508
|
+
"telephone",
|
|
3509
|
+
"fraction",
|
|
3510
|
+
"address",
|
|
3511
|
+
"name",
|
|
3512
|
+
"currency",
|
|
3513
|
+
"number_digit"
|
|
3514
|
+
]);
|
|
3515
|
+
var ALLOWED_ROLES2 = /* @__PURE__ */ new Set([
|
|
3516
|
+
"Girl",
|
|
3517
|
+
"Boy",
|
|
3518
|
+
"YoungAdultFemale",
|
|
3519
|
+
"YoungAdultMale",
|
|
3520
|
+
"OlderAdultFemale",
|
|
3521
|
+
"OlderAdultMale",
|
|
3522
|
+
"SeniorFemale",
|
|
3523
|
+
"SeniorMale"
|
|
3524
|
+
]);
|
|
3525
|
+
var ALLOWED_EMPHASIS_LEVELS2 = /* @__PURE__ */ new Set(["strong", "moderate", "reduced", "none"]);
|
|
3526
|
+
var ALLOWED_SILENCE_TYPES2 = /* @__PURE__ */ new Set([
|
|
3527
|
+
"Leading",
|
|
3528
|
+
"Tailing",
|
|
3529
|
+
"Sentenceboundary",
|
|
3530
|
+
"Comma",
|
|
3531
|
+
"Semicolon",
|
|
3532
|
+
"Enumerationcomma"
|
|
3533
|
+
]);
|
|
3534
|
+
var ALLOWED_VISEME_TYPES2 = /* @__PURE__ */ new Set(["redlips_front", "FacialExpression"]);
|
|
3535
|
+
var DEFAULT_PREVIEW_TAGS2 = /* @__PURE__ */ new Set(["mstts:voiceconversion"]);
|
|
3536
|
+
function featureStatusForTag2(name, options) {
|
|
3537
|
+
const tagName = canonicalTagName2(name);
|
|
3538
|
+
const configured = Object.entries(options.tagStatuses ?? {}).find(
|
|
3539
|
+
([candidate]) => canonicalTagName2(candidate) === tagName
|
|
3540
|
+
)?.[1];
|
|
3541
|
+
if (configured) return configured;
|
|
3542
|
+
if ((options.previewTags ?? [...DEFAULT_PREVIEW_TAGS2]).some((candidate) => canonicalTagName2(candidate) === tagName))
|
|
3543
|
+
return "preview";
|
|
3544
|
+
if ((options.deprecatedTags ?? []).some((candidate) => canonicalTagName2(candidate) === tagName)) return "deprecated";
|
|
3545
|
+
return void 0;
|
|
3546
|
+
}
|
|
3547
|
+
function decodeAttribute2(value) {
|
|
3548
|
+
return value.replace(
|
|
3549
|
+
/&(?:amp|apos|gt|lt|quot);/gi,
|
|
3550
|
+
(entity) => ({ "&": "&", "'": "'", ">": ">", "<": "<", """: '"' })[entity.toLowerCase()] ?? entity
|
|
3551
|
+
);
|
|
3552
|
+
}
|
|
3553
|
+
function findTagEnd22(source, start) {
|
|
3554
|
+
let quote = "";
|
|
3555
|
+
for (let index = start; index < source.length; index += 1) {
|
|
3556
|
+
const character = source[index];
|
|
3557
|
+
if (quote) {
|
|
3558
|
+
if (character === quote) quote = "";
|
|
3559
|
+
} else if (character === '"' || character === "'") quote = character;
|
|
3560
|
+
else if (character === ">") return index;
|
|
3561
|
+
}
|
|
3562
|
+
return source.length - 1;
|
|
3563
|
+
}
|
|
3564
|
+
function tokenizeElements2(source) {
|
|
3565
|
+
const tokens = [];
|
|
3566
|
+
const openElements = [];
|
|
3567
|
+
let index = 0;
|
|
3568
|
+
while (index < source.length) {
|
|
3569
|
+
const start = source.indexOf("<", index);
|
|
3570
|
+
if (start === -1) break;
|
|
3571
|
+
if (source.startsWith("<!--", start)) {
|
|
3572
|
+
const end2 = source.indexOf("-->", start + 4);
|
|
3573
|
+
index = end2 === -1 ? source.length : end2 + 3;
|
|
3574
|
+
continue;
|
|
3575
|
+
}
|
|
3576
|
+
if (source.startsWith("<![CDATA[", start)) {
|
|
3577
|
+
const end2 = source.indexOf("]]>", start + 9);
|
|
3578
|
+
index = end2 === -1 ? source.length : end2 + 3;
|
|
3579
|
+
continue;
|
|
3580
|
+
}
|
|
3581
|
+
if (source.startsWith("<?", start)) {
|
|
3582
|
+
const end2 = source.indexOf("?>", start + 2);
|
|
3583
|
+
index = end2 === -1 ? source.length : end2 + 2;
|
|
3584
|
+
continue;
|
|
3585
|
+
}
|
|
3586
|
+
const end = findTagEnd22(source, start + 1);
|
|
3587
|
+
const raw = source.slice(start, end + 1);
|
|
3588
|
+
if (raw.startsWith("</")) {
|
|
3589
|
+
openElements.pop();
|
|
3590
|
+
index = end + 1;
|
|
3591
|
+
continue;
|
|
3592
|
+
}
|
|
3593
|
+
const nameMatch = /^<\s*([A-Za-z_][A-Za-z0-9_.:-]*)/.exec(raw);
|
|
3594
|
+
if (!nameMatch?.[1]) {
|
|
3595
|
+
index = end + 1;
|
|
3596
|
+
continue;
|
|
3597
|
+
}
|
|
3598
|
+
const attributes = /* @__PURE__ */ new Map();
|
|
3599
|
+
const attributeSource = raw.slice(nameMatch[0].length, raw.length - 1).replace(/\/\s*$/, "");
|
|
3600
|
+
const attributePattern = /([A-Za-z_][A-Za-z0-9_.:-]*)\s*=\s*(["'])([\s\S]*?)\2/g;
|
|
3601
|
+
for (const match of attributeSource.matchAll(attributePattern)) {
|
|
3602
|
+
attributes.set(match[1].toLowerCase(), decodeAttribute2(match[3]));
|
|
3603
|
+
}
|
|
3604
|
+
const selfClosing = /\/\s*>$/.test(raw);
|
|
3605
|
+
const parent = openElements[openElements.length - 1];
|
|
3606
|
+
const childElementIndex = parent?.childElementCount;
|
|
3607
|
+
if (parent) parent.childElementCount += 1;
|
|
3608
|
+
const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
|
|
3609
|
+
const tokenName = nameMatch[1];
|
|
3610
|
+
const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
|
|
3611
|
+
tokens.push({
|
|
3612
|
+
attributes,
|
|
3613
|
+
childElementIndex,
|
|
3614
|
+
end,
|
|
3615
|
+
depth: openElements.length + 1,
|
|
3616
|
+
name: tokenName,
|
|
3617
|
+
parentName: parent?.name,
|
|
3618
|
+
parentVoiceName,
|
|
3619
|
+
selfClosing,
|
|
3620
|
+
start
|
|
3621
|
+
});
|
|
3622
|
+
if (!selfClosing) {
|
|
3623
|
+
openElements.push({
|
|
3624
|
+
childElementCount: 0,
|
|
3625
|
+
name: tokenName,
|
|
3626
|
+
voiceName: tokenVoiceName
|
|
3627
|
+
});
|
|
3628
|
+
}
|
|
3629
|
+
index = end + 1;
|
|
3630
|
+
}
|
|
3631
|
+
return tokens;
|
|
3632
|
+
}
|
|
3633
|
+
function location2(source, offset) {
|
|
3634
|
+
const before = source.slice(0, Math.max(0, offset));
|
|
3635
|
+
const line = before.split("\n").length;
|
|
3636
|
+
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
3637
|
+
}
|
|
3638
|
+
function addDiagnostic2(diagnostics, source, offset, message, severity = "error", code) {
|
|
3639
|
+
diagnostics.push({
|
|
3640
|
+
...location2(source, offset),
|
|
3641
|
+
message,
|
|
3642
|
+
severity,
|
|
3643
|
+
source: "ssml-static-validator",
|
|
3644
|
+
...code ? { code } : {}
|
|
3645
|
+
});
|
|
3646
|
+
}
|
|
3647
|
+
function isSupportedProsodyRate2(value) {
|
|
3648
|
+
const trimmed = value.trim();
|
|
3649
|
+
if (/^(x-slow|slow|medium|fast|x-fast|[+-]?\d+(?:\.\d+)?%)$/.test(trimmed)) return true;
|
|
3650
|
+
const multiplier = /^(\d+(?:\.\d+)?)(x)?$/i.exec(trimmed);
|
|
3651
|
+
if (!multiplier) return false;
|
|
3652
|
+
const numericValue = Number(multiplier[1]);
|
|
3653
|
+
return numericValue >= 0.5 && numericValue <= 2;
|
|
3654
|
+
}
|
|
3655
|
+
function isValidAzureAudioDuration2(value) {
|
|
3656
|
+
const trimmed = value.trim();
|
|
3657
|
+
const numeric = /^(\d+(?:\.\d+)?)(ms|s)$/.exec(trimmed);
|
|
3658
|
+
if (numeric) return Number(numeric[1]) > 0;
|
|
3659
|
+
const clock = /^(\d{2,}):([0-5]\d):([0-5]\d)(?:\.(\d{1,3}))?$/.exec(trimmed);
|
|
3660
|
+
if (!clock) return false;
|
|
3661
|
+
return Number(clock[1]) > 0 || Number(clock[2]) > 0 || Number(clock[3]) > 0 || Number(clock[4] ?? 0) > 0;
|
|
3662
|
+
}
|
|
3663
|
+
function isValidAzureBackgroundAudioDuration2(value) {
|
|
3664
|
+
const match = /^(\d+)$/.exec(value.trim());
|
|
3665
|
+
if (!match) return false;
|
|
3666
|
+
const milliseconds = Number(match[1]);
|
|
3667
|
+
return Number.isFinite(milliseconds) && milliseconds >= 0 && milliseconds <= 1e4;
|
|
3668
|
+
}
|
|
3669
|
+
function attr2(token, name) {
|
|
3670
|
+
return token.attributes.get(name.toLowerCase());
|
|
3671
|
+
}
|
|
3672
|
+
var DEFAULT_LANGUAGE_ALIASES2 = {
|
|
3673
|
+
"zh-CN": ["zh-Hans"],
|
|
3674
|
+
"zh-TW": ["zh-Hant"]
|
|
3675
|
+
};
|
|
3676
|
+
function canonicalLanguageTag2(language) {
|
|
3677
|
+
const trimmed = language.trim();
|
|
3678
|
+
if (!trimmed) return "";
|
|
3679
|
+
try {
|
|
3680
|
+
return new Intl.Locale(trimmed).toString().toLowerCase();
|
|
3681
|
+
} catch {
|
|
3682
|
+
return trimmed.toLowerCase();
|
|
3683
|
+
}
|
|
3684
|
+
}
|
|
3685
|
+
function createLanguageNormalizer2(options) {
|
|
3686
|
+
const aliases = /* @__PURE__ */ new Map();
|
|
3687
|
+
const addAliasGroup = (canonical, values) => {
|
|
3688
|
+
const normalizedCanonical = canonicalLanguageTag2(canonical);
|
|
3689
|
+
if (!normalizedCanonical) return;
|
|
3690
|
+
aliases.set(normalizedCanonical, normalizedCanonical);
|
|
3691
|
+
for (const value of values) {
|
|
3692
|
+
const normalizedValue = canonicalLanguageTag2(value);
|
|
3693
|
+
if (normalizedValue) aliases.set(normalizedValue, normalizedCanonical);
|
|
3694
|
+
}
|
|
3695
|
+
};
|
|
3696
|
+
for (const [canonical, values] of Object.entries(DEFAULT_LANGUAGE_ALIASES2)) addAliasGroup(canonical, values);
|
|
3697
|
+
for (const [canonical, valueOrValues] of Object.entries(options.languageAliases ?? {}))
|
|
3698
|
+
addAliasGroup(canonical, typeof valueOrValues === "string" ? [valueOrValues] : valueOrValues);
|
|
3699
|
+
return (language) => {
|
|
3700
|
+
const customValue = options.normalizeLanguage ? options.normalizeLanguage(language) : language;
|
|
3701
|
+
const normalized = canonicalLanguageTag2(customValue);
|
|
3702
|
+
return aliases.get(normalized) ?? normalized;
|
|
3703
|
+
};
|
|
3704
|
+
}
|
|
3705
|
+
function voiceLocalePrefix2(voiceName) {
|
|
3706
|
+
const match = /^(?<language>[A-Za-z]{2,3})-(?<region>[A-Za-z]{2}|\d{3})(?:-|$)/.exec(voiceName.trim());
|
|
3707
|
+
if (!match?.groups) return void 0;
|
|
3708
|
+
const tag = `${match.groups.language}-${match.groups.region}`;
|
|
3709
|
+
return {
|
|
3710
|
+
language: match.groups.language.toLowerCase(),
|
|
3711
|
+
region: match.groups.region.toLowerCase(),
|
|
3712
|
+
tag
|
|
3713
|
+
};
|
|
3714
|
+
}
|
|
3715
|
+
function definitionFromStyleMap2(voiceName, styles) {
|
|
3716
|
+
return {
|
|
3717
|
+
name: voiceName,
|
|
3718
|
+
locale: voiceLocalePrefix2(voiceName)?.tag ?? "",
|
|
3719
|
+
styles
|
|
3720
|
+
};
|
|
3721
|
+
}
|
|
3722
|
+
function normalizeVoiceCatalog2(options) {
|
|
3723
|
+
const definitions = /* @__PURE__ */ new Map();
|
|
3724
|
+
for (const definition of AZURE_VOICE_DEFINITIONS2) definitions.set(definition.name.toLowerCase(), definition);
|
|
3725
|
+
for (const definition of options.voiceCatalog ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
3726
|
+
for (const definition of options.voiceDefinitions ?? []) definitions.set(definition.name.toLowerCase(), definition);
|
|
3727
|
+
for (const definition of options.customVoiceDefinitions ?? [])
|
|
3728
|
+
definitions.set(definition.name.toLowerCase(), definition);
|
|
3729
|
+
for (const [voiceName, styles] of Object.entries(options.customVoiceStyleMap ?? {})) {
|
|
3730
|
+
const key = voiceName.toLowerCase();
|
|
3731
|
+
const current = definitions.get(key);
|
|
3732
|
+
definitions.set(key, {
|
|
3733
|
+
...current ?? definitionFromStyleMap2(voiceName, styles),
|
|
3734
|
+
name: current?.name ?? voiceName,
|
|
3735
|
+
styles: styles.map((style) => style.toLowerCase())
|
|
3736
|
+
});
|
|
3737
|
+
}
|
|
3738
|
+
return definitions;
|
|
3739
|
+
}
|
|
3740
|
+
function diagnosticSeverity2(policy) {
|
|
3741
|
+
if (policy === "ignore") return void 0;
|
|
3742
|
+
return policy === "error" ? "error" : "warning";
|
|
3743
|
+
}
|
|
3744
|
+
function languagePart2(language) {
|
|
3745
|
+
try {
|
|
3746
|
+
return new Intl.Locale(language).language.toLowerCase();
|
|
3747
|
+
} catch {
|
|
3748
|
+
return language.split("-")[0]?.toLowerCase() ?? "";
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
function definitionMatchesLanguage2(definition, voiceName, language, normalizeLanguage) {
|
|
3752
|
+
const candidateLanguages = definition ? [definition.locale, ...definition.secondaryLocales ?? []].filter(Boolean) : [voiceLocalePrefix2(voiceName)?.tag ?? ""];
|
|
3753
|
+
if (candidateLanguages.length === 0 || !language.trim()) return void 0;
|
|
3754
|
+
const normalizedLanguage = normalizeLanguage(language);
|
|
3755
|
+
const normalizedCandidates = candidateLanguages.map(normalizeLanguage);
|
|
3756
|
+
if (normalizedCandidates.includes(normalizedLanguage)) return true;
|
|
3757
|
+
if (!normalizedLanguage || !normalizedCandidates.some(Boolean)) return void 0;
|
|
3758
|
+
return normalizedLanguage === languagePart2(normalizedLanguage) ? normalizedCandidates.some((candidate) => languagePart2(candidate) === normalizedLanguage) : false;
|
|
3759
|
+
}
|
|
3760
|
+
function canonicalTagName2(name) {
|
|
3761
|
+
const normalized = name.toLowerCase();
|
|
3762
|
+
if (normalized === "express-as" || normalized === "expressas") return "mstts:express-as";
|
|
3763
|
+
if (normalized === "sayas") return "say-as";
|
|
3764
|
+
return normalized;
|
|
3765
|
+
}
|
|
3766
|
+
function validateVoiceFeatureMatrix2(token, source, diagnostics, voiceName, definition) {
|
|
3767
|
+
if (!voiceName || !definition || token.name.toLowerCase() === "voice" || token.name.toLowerCase() === "mstts:turn")
|
|
3768
|
+
return;
|
|
3769
|
+
const tagName = canonicalTagName2(token.name);
|
|
3770
|
+
const unsupportedTags = new Set((definition.unsupportedTags ?? []).map(canonicalTagName2));
|
|
3771
|
+
const supportedTags = definition.supportedTags?.map(canonicalTagName2);
|
|
3772
|
+
if (unsupportedTags.has(tagName) || supportedTags !== void 0 && !supportedTags.includes(tagName)) {
|
|
3773
|
+
addDiagnostic2(
|
|
3774
|
+
diagnostics,
|
|
3775
|
+
source,
|
|
3776
|
+
token.start,
|
|
3777
|
+
`Tag <${token.name}> is not supported by voice "${voiceName}" according to the configured feature matrix.`,
|
|
3778
|
+
"error",
|
|
3779
|
+
"azure-unsupported-tag-for-voice"
|
|
3780
|
+
);
|
|
3781
|
+
}
|
|
3782
|
+
}
|
|
3783
|
+
function validateAudioSource2(token, source, diagnostics, options, elementName2) {
|
|
3784
|
+
const src = attr2(token, "src");
|
|
3785
|
+
if (!src) {
|
|
3786
|
+
addDiagnostic2(diagnostics, source, token.start, `<${elementName2}> requires a "src" attribute.`);
|
|
3787
|
+
return;
|
|
3788
|
+
}
|
|
3789
|
+
let parsed;
|
|
3790
|
+
try {
|
|
3791
|
+
parsed = new URL(src);
|
|
3792
|
+
} catch {
|
|
3793
|
+
addDiagnostic2(diagnostics, source, token.start, `<${elementName2} src> must be an absolute HTTP(S) URL.`);
|
|
3794
|
+
return;
|
|
3795
|
+
}
|
|
3796
|
+
if (parsed.username || parsed.password)
|
|
3797
|
+
addDiagnostic2(diagnostics, source, token.start, `<${elementName2} src> must not contain URL credentials.`);
|
|
3798
|
+
if (parsed.protocol !== "https:" && !(options.allowHttpAudio && parsed.protocol === "http:"))
|
|
3799
|
+
addDiagnostic2(diagnostics, source, token.start, `<${elementName2} src> must use HTTPS.`);
|
|
3800
|
+
const isAllowedOrigin = options.allowedAudioOrigins?.some((allowedOrigin) => {
|
|
3801
|
+
try {
|
|
3802
|
+
const configured = new URL(allowedOrigin);
|
|
3803
|
+
if (configured.protocol !== "https:" && configured.protocol !== "http:" || configured.username || configured.password || configured.pathname !== "/" || configured.search || configured.hash)
|
|
3804
|
+
return false;
|
|
3805
|
+
return configured.origin === parsed.origin;
|
|
3806
|
+
} catch {
|
|
3807
|
+
return false;
|
|
3808
|
+
}
|
|
3809
|
+
}) ?? false;
|
|
3810
|
+
if (options.allowedAudioOrigins && !isAllowedOrigin)
|
|
3811
|
+
addDiagnostic2(diagnostics, source, token.start, `<${elementName2} src> origin "${parsed.origin}" is not allowed.`);
|
|
3812
|
+
else if (!isAllowedOrigin && !options.allowExternalAudio)
|
|
3813
|
+
addDiagnostic2(
|
|
3814
|
+
diagnostics,
|
|
3815
|
+
source,
|
|
3816
|
+
token.start,
|
|
3817
|
+
`<${elementName2} src> external origin "${parsed.origin}" is blocked by default; set allowExternalAudio to true or provide allowedAudioOrigins.`
|
|
3818
|
+
);
|
|
3819
|
+
}
|
|
3820
|
+
function validateElement2(token, source, diagnostics, voiceName, options, voiceCatalog) {
|
|
3821
|
+
const name = token.name.toLowerCase();
|
|
3822
|
+
const tagStatus = featureStatusForTag2(token.name, options);
|
|
3823
|
+
if (tagStatus === "preview")
|
|
3824
|
+
addDiagnostic2(
|
|
3825
|
+
diagnostics,
|
|
3826
|
+
source,
|
|
3827
|
+
token.start,
|
|
3828
|
+
`<${token.name}> is an Azure Speech preview feature and may change or require preview access.`,
|
|
3829
|
+
"warning",
|
|
3830
|
+
"azure-preview-tag"
|
|
3831
|
+
);
|
|
3832
|
+
if (tagStatus === "deprecated")
|
|
3833
|
+
addDiagnostic2(
|
|
3834
|
+
diagnostics,
|
|
3835
|
+
source,
|
|
3836
|
+
token.start,
|
|
3837
|
+
`<${token.name}> is deprecated by Azure Speech; migrate to a supported alternative.`,
|
|
3838
|
+
"info",
|
|
3839
|
+
"azure-deprecated-tag"
|
|
3840
|
+
);
|
|
3841
|
+
if (name === "voice" && !attr2(token, "name")?.trim())
|
|
3842
|
+
addDiagnostic2(diagnostics, source, token.start, '<voice> requires a non-empty "name" attribute.');
|
|
3843
|
+
if (name === "break") {
|
|
3844
|
+
const time = attr2(token, "time");
|
|
3845
|
+
const strength = attr2(token, "strength");
|
|
3846
|
+
if (!time && !strength)
|
|
3847
|
+
addDiagnostic2(diagnostics, source, token.start, '<break> requires either "time" or "strength".');
|
|
3848
|
+
if (time && strength)
|
|
3849
|
+
addDiagnostic2(diagnostics, source, token.start, '<break> must not specify both "time" and "strength".');
|
|
3850
|
+
if (time && !/^\d+(?:\.\d+)?(?:ms|s)$/.test(time.trim()))
|
|
3851
|
+
addDiagnostic2(diagnostics, source, token.start, '<break time> must use a numeric value followed by "ms" or "s".');
|
|
3852
|
+
if (strength && !ALLOWED_BREAK_STRENGTHS2.has(strength))
|
|
3853
|
+
addDiagnostic2(diagnostics, source, token.start, `Unsupported <break strength> value "${strength}".`);
|
|
3854
|
+
}
|
|
3855
|
+
if (name === "prosody") {
|
|
3856
|
+
const rate = attr2(token, "rate");
|
|
3857
|
+
const pitch = attr2(token, "pitch");
|
|
3858
|
+
const volume = attr2(token, "volume");
|
|
3859
|
+
if (rate && !isSupportedProsodyRate2(rate))
|
|
3860
|
+
addDiagnostic2(diagnostics, source, token.start, `Unsupported <prosody rate> value "${rate}".`);
|
|
3861
|
+
if (pitch && !/^(x-low|low|medium|high|x-high|[+-]?\d+(?:\.\d+)?(?:st|Hz|%)?)$/.test(pitch.trim()))
|
|
3862
|
+
addDiagnostic2(diagnostics, source, token.start, `Unsupported <prosody pitch> value "${pitch}".`);
|
|
3863
|
+
if (volume && !/^(silent|x-soft|soft|medium|loud|x-loud|[+-]?\d+(?:\.\d+)?(?:dB|%)?)$/.test(volume.trim()))
|
|
3864
|
+
addDiagnostic2(diagnostics, source, token.start, `Unsupported <prosody volume> value "${volume}".`);
|
|
3865
|
+
}
|
|
3866
|
+
if (name === "mstts:express-as" || name === "express-as" || name === "expressas") {
|
|
3867
|
+
const style = attr2(token, "style");
|
|
3868
|
+
if (!style?.trim())
|
|
3869
|
+
addDiagnostic2(diagnostics, source, token.start, '<mstts:express-as> requires a non-empty "style" attribute.');
|
|
3870
|
+
const degree = attr2(token, "styledegree") ?? attr2(token, "style-degree");
|
|
3871
|
+
if (degree && (!/^\d+(?:\.\d+)?$/.test(degree) || Number(degree) < 0.01 || Number(degree) > 2))
|
|
3872
|
+
addDiagnostic2(
|
|
3873
|
+
diagnostics,
|
|
3874
|
+
source,
|
|
3875
|
+
token.start,
|
|
3876
|
+
"<mstts:express-as styledegree> must be a number between 0.01 and 2."
|
|
3877
|
+
);
|
|
3878
|
+
const role = attr2(token, "role");
|
|
3879
|
+
if (role && !ALLOWED_ROLES2.has(role))
|
|
3880
|
+
addDiagnostic2(diagnostics, source, token.start, `Unsupported <mstts:express-as role> value "${role}".`);
|
|
3881
|
+
const definition = voiceName ? voiceCatalog.get(voiceName.toLowerCase()) : void 0;
|
|
3882
|
+
const supportedStyles = definition?.styles;
|
|
3883
|
+
const severity = diagnosticSeverity2(options.unsupportedStylePolicy ?? options.unknownVoicePolicy ?? "warn");
|
|
3884
|
+
if (style && definition && !supportedStyles?.some((candidate) => candidate.toLowerCase() === style.toLowerCase()) && severity)
|
|
3885
|
+
addDiagnostic2(
|
|
3886
|
+
diagnostics,
|
|
3887
|
+
source,
|
|
3888
|
+
token.start,
|
|
3889
|
+
`Unknown style "${style}" is not supported by voice "${voiceName}" according to the configured voice style map.`,
|
|
3890
|
+
severity,
|
|
3891
|
+
"azure-unsupported-style"
|
|
3892
|
+
);
|
|
3893
|
+
if (style && voiceName && !definition && severity)
|
|
3894
|
+
addDiagnostic2(
|
|
3895
|
+
diagnostics,
|
|
3896
|
+
source,
|
|
3897
|
+
token.start,
|
|
3898
|
+
`Unknown style "${style}" cannot be verified because voice "${voiceName}" is not registered in the voice style map.`,
|
|
3899
|
+
severity
|
|
3900
|
+
);
|
|
3901
|
+
}
|
|
3902
|
+
if (name === "say-as" || name === "sayas") {
|
|
3903
|
+
const interpretAs = attr2(token, "interpret-as");
|
|
3904
|
+
if (!interpretAs || !ALLOWED_SAY_AS2.has(interpretAs))
|
|
3905
|
+
addDiagnostic2(diagnostics, source, token.start, `<say-as> requires a supported "interpret-as" value.`);
|
|
3906
|
+
}
|
|
3907
|
+
if (name === "phoneme" && (!attr2(token, "alphabet") || !attr2(token, "ph")))
|
|
3908
|
+
addDiagnostic2(diagnostics, source, token.start, '<phoneme> requires both "alphabet" and "ph" attributes.');
|
|
3909
|
+
if (name === "emphasis" && attr2(token, "level") && !ALLOWED_EMPHASIS_LEVELS2.has(attr2(token, "level") ?? ""))
|
|
3910
|
+
addDiagnostic2(diagnostics, source, token.start, `Unsupported <emphasis level> value "${attr2(token, "level")}".`);
|
|
3911
|
+
if (name === "sub" && !attr2(token, "alias")?.trim())
|
|
3912
|
+
addDiagnostic2(diagnostics, source, token.start, '<sub> requires a non-empty "alias" attribute.');
|
|
3913
|
+
if (name === "lang" && !attr2(token, "xml:lang")?.trim() && !attr2(token, "lang")?.trim())
|
|
3914
|
+
addDiagnostic2(diagnostics, source, token.start, '<lang> requires an "xml:lang" attribute.');
|
|
3915
|
+
if (name === "mark" && !attr2(token, "name")?.trim())
|
|
3916
|
+
addDiagnostic2(diagnostics, source, token.start, '<mark> requires a non-empty "name" attribute.');
|
|
3917
|
+
if (name === "bookmark" && !attr2(token, "mark")?.trim())
|
|
3918
|
+
addDiagnostic2(diagnostics, source, token.start, '<bookmark> requires a non-empty "mark" attribute.');
|
|
3919
|
+
if (name === "lexicon") {
|
|
3920
|
+
const uri = attr2(token, "uri");
|
|
3921
|
+
if (!uri) addDiagnostic2(diagnostics, source, token.start, '<lexicon> requires a "uri" attribute.');
|
|
3922
|
+
else {
|
|
3923
|
+
try {
|
|
3924
|
+
const parsed = new URL(uri);
|
|
3925
|
+
if (parsed.protocol !== "https:")
|
|
3926
|
+
addDiagnostic2(diagnostics, source, token.start, "<lexicon uri> must use HTTPS.");
|
|
3927
|
+
} catch {
|
|
3928
|
+
addDiagnostic2(diagnostics, source, token.start, "<lexicon uri> must be an absolute HTTPS URL.");
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3931
|
+
}
|
|
3932
|
+
if (name === "mstts:silence") {
|
|
3933
|
+
const type = attr2(token, "type");
|
|
3934
|
+
const value = attr2(token, "value");
|
|
3935
|
+
if (!type || !ALLOWED_SILENCE_TYPES2.has(type))
|
|
3936
|
+
addDiagnostic2(diagnostics, source, token.start, '<mstts:silence> requires a supported "type" attribute.');
|
|
3937
|
+
if (!value || !/^\d+(?:\.\d+)?(?:ms|s)$/.test(value.trim()))
|
|
3938
|
+
addDiagnostic2(diagnostics, source, token.start, '<mstts:silence> requires a time-valued "value" attribute.');
|
|
3939
|
+
}
|
|
3940
|
+
if (name === "mstts:audioduration") {
|
|
3941
|
+
const value = attr2(token, "value");
|
|
3942
|
+
if (!value || !isValidAzureAudioDuration2(value))
|
|
3943
|
+
addDiagnostic2(
|
|
3944
|
+
diagnostics,
|
|
3945
|
+
source,
|
|
3946
|
+
token.start,
|
|
3947
|
+
'<mstts:audioduration> requires a positive duration such as "10s", "5000ms", or "00:00:10".'
|
|
3948
|
+
);
|
|
3949
|
+
if (!token.selfClosing)
|
|
3950
|
+
addDiagnostic2(diagnostics, source, token.start, "<mstts:audioduration> must be self-closing.");
|
|
3951
|
+
}
|
|
3952
|
+
if (name === "mstts:viseme") {
|
|
3953
|
+
const type = attr2(token, "type");
|
|
3954
|
+
if (!type || !ALLOWED_VISEME_TYPES2.has(type))
|
|
3955
|
+
addDiagnostic2(diagnostics, source, token.start, '<mstts:viseme> requires a supported "type" attribute.');
|
|
3956
|
+
}
|
|
3957
|
+
if (name === "audio") {
|
|
3958
|
+
validateAudioSource2(token, source, diagnostics, options, "audio");
|
|
3959
|
+
}
|
|
3960
|
+
if (name === "mstts:turn") {
|
|
3961
|
+
if (!attr2(token, "voice")?.trim() && !attr2(token, "speaker")?.trim())
|
|
3962
|
+
addDiagnostic2(
|
|
3963
|
+
diagnostics,
|
|
3964
|
+
source,
|
|
3965
|
+
token.start,
|
|
3966
|
+
'<mstts:turn> requires a non-empty "voice" or "speaker" attribute.'
|
|
3967
|
+
);
|
|
3968
|
+
if (token.parentName?.toLowerCase() !== "mstts:dialog")
|
|
3969
|
+
addDiagnostic2(diagnostics, source, token.start, "<mstts:turn> is only allowed directly inside <mstts:dialog>.");
|
|
3970
|
+
}
|
|
3971
|
+
if (name === "mstts:backgroundaudio") {
|
|
3972
|
+
validateAudioSource2(token, source, diagnostics, options, "mstts:backgroundaudio");
|
|
3973
|
+
const volume = attr2(token, "volume");
|
|
3974
|
+
if (volume !== void 0 && (!/^\d+(?:\.\d+)?$/.test(volume.trim()) || Number(volume) > 100))
|
|
3975
|
+
addDiagnostic2(diagnostics, source, token.start, `Unsupported <mstts:backgroundaudio volume> value "${volume}".`);
|
|
3976
|
+
for (const [attribute, value] of [
|
|
3977
|
+
["fadein", attr2(token, "fadein")],
|
|
3978
|
+
["fadeout", attr2(token, "fadeout")]
|
|
3979
|
+
]) {
|
|
3980
|
+
if (value !== void 0 && !isValidAzureBackgroundAudioDuration2(value))
|
|
3981
|
+
addDiagnostic2(
|
|
3982
|
+
diagnostics,
|
|
3983
|
+
source,
|
|
3984
|
+
token.start,
|
|
3985
|
+
`<mstts:backgroundaudio ${attribute}> must be between 0 and 10000 milliseconds, for example "500ms" or "10s".`
|
|
3986
|
+
);
|
|
3987
|
+
}
|
|
3988
|
+
if (token.parentName?.toLowerCase() !== "speak" || token.childElementIndex !== 0)
|
|
3989
|
+
addDiagnostic2(
|
|
3990
|
+
diagnostics,
|
|
3991
|
+
source,
|
|
3992
|
+
token.start,
|
|
3993
|
+
"<mstts:backgroundaudio> must be the first element directly under <speak>."
|
|
3994
|
+
);
|
|
3995
|
+
if (!token.selfClosing)
|
|
3996
|
+
addDiagnostic2(diagnostics, source, token.start, "<mstts:backgroundaudio> must be self-closing.");
|
|
3997
|
+
}
|
|
3998
|
+
}
|
|
3999
|
+
function validateAzureSsmlStatic2(ssml, options = {}) {
|
|
4000
|
+
const diagnostics = [];
|
|
4001
|
+
if (typeof ssml !== "string") {
|
|
4002
|
+
return [
|
|
4003
|
+
{
|
|
4004
|
+
line: 1,
|
|
4005
|
+
column: 1,
|
|
4006
|
+
message: "SSML input must be a string",
|
|
4007
|
+
severity: "error",
|
|
4008
|
+
source: "ssml-static-validator"
|
|
4009
|
+
}
|
|
4010
|
+
];
|
|
4011
|
+
}
|
|
4012
|
+
const maxLength = options.maxLength ?? 1e4;
|
|
4013
|
+
if (ssml.length > maxLength)
|
|
4014
|
+
addDiagnostic2(diagnostics, ssml, maxLength, `SSML exceeds the maximum length of ${maxLength} characters.`);
|
|
4015
|
+
if (options.maxXmlDepth !== void 0 && (!Number.isInteger(options.maxXmlDepth) || options.maxXmlDepth <= 0)) {
|
|
4016
|
+
addDiagnostic2(diagnostics, ssml, 0, "maxXmlDepth must be a positive integer.");
|
|
4017
|
+
}
|
|
4018
|
+
try {
|
|
4019
|
+
parseSsml2(ssml);
|
|
4020
|
+
} catch (error) {
|
|
4021
|
+
const message = error instanceof Error ? error.message.replace(/ at position \d+$/, "") : String(error);
|
|
4022
|
+
const match = / at position (\d+)$/.exec(error instanceof Error ? error.message : "");
|
|
4023
|
+
addDiagnostic2(diagnostics, ssml, match ? Number(match[1]) : 0, message);
|
|
4024
|
+
return diagnostics;
|
|
4025
|
+
}
|
|
4026
|
+
const tokens = tokenizeElements2(ssml);
|
|
4027
|
+
if (options.maxXmlDepth !== void 0) {
|
|
4028
|
+
for (const token of tokens) {
|
|
4029
|
+
if (token.depth > options.maxXmlDepth) {
|
|
4030
|
+
addDiagnostic2(
|
|
4031
|
+
diagnostics,
|
|
4032
|
+
ssml,
|
|
4033
|
+
token.start,
|
|
4034
|
+
`XML nesting depth ${token.depth} exceeds the configured maximum of ${options.maxXmlDepth}.`
|
|
4035
|
+
);
|
|
4036
|
+
}
|
|
4037
|
+
}
|
|
4038
|
+
}
|
|
4039
|
+
const speak = tokens.find((token) => token.name.toLowerCase() === "speak");
|
|
4040
|
+
const voices = tokens.filter((token) => token.name.toLowerCase() === "voice");
|
|
4041
|
+
const backgroundAudioTokens = tokens.filter((token) => token.name.toLowerCase() === "mstts:backgroundaudio");
|
|
4042
|
+
for (const [index, token] of backgroundAudioTokens.entries()) {
|
|
4043
|
+
if (index > 0)
|
|
4044
|
+
addDiagnostic2(
|
|
4045
|
+
diagnostics,
|
|
4046
|
+
ssml,
|
|
4047
|
+
token.start,
|
|
4048
|
+
"An SSML document can contain at most one <mstts:backgroundaudio> element."
|
|
4049
|
+
);
|
|
4050
|
+
}
|
|
4051
|
+
if (!speak || voices.length === 0)
|
|
4052
|
+
addDiagnostic2(
|
|
4053
|
+
diagnostics,
|
|
4054
|
+
ssml,
|
|
4055
|
+
speak?.start ?? 0,
|
|
4056
|
+
"Azure SSML requires at least one <voice> element under <speak>."
|
|
4057
|
+
);
|
|
4058
|
+
const voiceName = voices[0] ? attr2(voices[0], "name") : void 0;
|
|
4059
|
+
const voiceCatalog = normalizeVoiceCatalog2(options);
|
|
4060
|
+
const normalizeLanguage = createLanguageNormalizer2(options);
|
|
4061
|
+
const policySeverity = diagnosticSeverity2(options.unknownVoicePolicy ?? "warn");
|
|
4062
|
+
const voicesToValidate = options.validateNestedVoices === false ? voices.slice(0, 1) : voices;
|
|
4063
|
+
for (const token of voicesToValidate) {
|
|
4064
|
+
const name = attr2(token, "name")?.trim();
|
|
4065
|
+
const language = attr2(token, "xml:lang")?.trim() || (speak ? attr2(speak, "xml:lang")?.trim() : void 0);
|
|
4066
|
+
const definition = name ? voiceCatalog.get(name.toLowerCase()) : void 0;
|
|
4067
|
+
if (name && definition?.status === "preview")
|
|
4068
|
+
addDiagnostic2(
|
|
4069
|
+
diagnostics,
|
|
4070
|
+
ssml,
|
|
4071
|
+
token.start,
|
|
4072
|
+
`Voice "${name}" is an Azure Speech preview voice and may change or require preview access.`,
|
|
4073
|
+
"warning",
|
|
4074
|
+
"azure-preview-voice"
|
|
4075
|
+
);
|
|
4076
|
+
if (name && definition?.status === "deprecated")
|
|
4077
|
+
addDiagnostic2(
|
|
4078
|
+
diagnostics,
|
|
4079
|
+
ssml,
|
|
4080
|
+
token.start,
|
|
4081
|
+
`Voice "${name}" is deprecated by Azure Speech; migrate to a supported voice.`,
|
|
4082
|
+
"info",
|
|
4083
|
+
"azure-deprecated-voice"
|
|
4084
|
+
);
|
|
4085
|
+
if (name && !definition && policySeverity)
|
|
4086
|
+
addDiagnostic2(
|
|
4087
|
+
diagnostics,
|
|
4088
|
+
ssml,
|
|
4089
|
+
token.start,
|
|
4090
|
+
`Unknown voice "${name}" is not registered in the voice catalog.`,
|
|
4091
|
+
policySeverity,
|
|
4092
|
+
"azure-unknown-voice"
|
|
4093
|
+
);
|
|
4094
|
+
if (name && language && definitionMatchesLanguage2(definition, name, language, normalizeLanguage) === false)
|
|
4095
|
+
addDiagnostic2(
|
|
4096
|
+
diagnostics,
|
|
4097
|
+
ssml,
|
|
4098
|
+
token.start,
|
|
4099
|
+
`Voice "${name}" does not match language "${language}"; the voice name prefix indicates a different language or region.`,
|
|
4100
|
+
"warning",
|
|
4101
|
+
"azure-locale-mismatch"
|
|
4102
|
+
);
|
|
4103
|
+
}
|
|
4104
|
+
for (const token of tokens) {
|
|
4105
|
+
const tokenName = token.name.toLowerCase();
|
|
4106
|
+
const tokenVoiceName = tokenName === "voice" ? attr2(token, "name")?.trim() : tokenName === "mstts:turn" ? attr2(token, "voice")?.trim() || token.parentVoiceName : options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
4107
|
+
validateElement2(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
4108
|
+
const definition = tokenVoiceName ? voiceCatalog.get(tokenVoiceName.toLowerCase()) : void 0;
|
|
4109
|
+
validateVoiceFeatureMatrix2(token, ssml, diagnostics, tokenVoiceName, definition);
|
|
4110
|
+
if (tokenName === "voice" && options.model && definition?.models && !definition.models.some((model) => model.toLowerCase() === options.model?.toLowerCase())) {
|
|
4111
|
+
addDiagnostic2(
|
|
4112
|
+
diagnostics,
|
|
4113
|
+
ssml,
|
|
4114
|
+
token.start,
|
|
4115
|
+
`Voice "${tokenVoiceName}" does not support model "${options.model}" according to the configured feature matrix.`,
|
|
4116
|
+
"error",
|
|
4117
|
+
"azure-unsupported-model-for-voice"
|
|
4118
|
+
);
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
4121
|
+
return diagnostics;
|
|
4122
|
+
}
|
|
4123
|
+
function urlAttributes2(token) {
|
|
4124
|
+
const tag = canonicalTagName2(token.name);
|
|
4125
|
+
const attributes = tag === "audio" || tag === "mstts:backgroundaudio" ? ["src"] : tag === "lexicon" ? ["uri"] : tag === "mstts:voiceconversion" ? ["url"] : [];
|
|
4126
|
+
return attributes.flatMap((attribute) => {
|
|
4127
|
+
const value = attr2(token, attribute);
|
|
4128
|
+
return value === void 0 ? [] : [{ attribute, value }];
|
|
4129
|
+
});
|
|
4130
|
+
}
|
|
4131
|
+
function validateAzureSsml2(ssml, options = {}) {
|
|
4132
|
+
const diagnostics = validateAzureSsmlStatic2(ssml, options);
|
|
4133
|
+
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
4134
|
+
if (!validator || typeof ssml !== "string") return diagnostics;
|
|
4135
|
+
let tokens;
|
|
4136
|
+
try {
|
|
4137
|
+
tokens = tokenizeElements2(ssml);
|
|
4138
|
+
} catch {
|
|
4139
|
+
return diagnostics;
|
|
4140
|
+
}
|
|
4141
|
+
const checks = tokens.flatMap(
|
|
4142
|
+
(token) => urlAttributes2(token).map(async ({ attribute, value }) => {
|
|
4143
|
+
try {
|
|
4144
|
+
const result = await validator(value, { tag: token.name, attribute });
|
|
4145
|
+
const valid = typeof result === "boolean" ? result : result.valid;
|
|
4146
|
+
if (!valid) {
|
|
4147
|
+
const reason = typeof result === "boolean" ? void 0 : result.reason;
|
|
4148
|
+
addDiagnostic2(
|
|
4149
|
+
diagnostics,
|
|
4150
|
+
ssml,
|
|
4151
|
+
token.start,
|
|
4152
|
+
`<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
|
|
4153
|
+
);
|
|
4154
|
+
}
|
|
4155
|
+
} catch (error) {
|
|
4156
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
4157
|
+
addDiagnostic2(
|
|
4158
|
+
diagnostics,
|
|
4159
|
+
ssml,
|
|
4160
|
+
token.start,
|
|
4161
|
+
`<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
|
|
4162
|
+
);
|
|
4163
|
+
}
|
|
4164
|
+
})
|
|
4165
|
+
);
|
|
4166
|
+
return Promise.all(checks).then(() => diagnostics);
|
|
4167
|
+
}
|
|
4168
|
+
var AZURE_VOICE_CATALOG_METADATA2 = {
|
|
4169
|
+
apiVersion: "2025-10-01",
|
|
4170
|
+
generatedAt: "2026-08-28T00:00:00.000Z",
|
|
4171
|
+
regions: [],
|
|
4172
|
+
voiceCount: AZURE_VOICE_DEFINITIONS2.length
|
|
4173
|
+
};
|
|
4174
|
+
|
|
4175
|
+
// packages/azure-tts-client/src/safe.ts
|
|
4176
|
+
async function synthesizeSsmlSafe(client, ssml, options = {}) {
|
|
4177
|
+
const validationOptions = options.validation ?? options;
|
|
4178
|
+
const diagnostics = await Promise.resolve(validateAzureSsml2(ssml, validationOptions));
|
|
4179
|
+
const errors = diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
4180
|
+
if (errors.length > 0) {
|
|
4181
|
+
return {
|
|
4182
|
+
ok: false,
|
|
4183
|
+
success: false,
|
|
4184
|
+
status: "validation-error",
|
|
4185
|
+
error: {
|
|
4186
|
+
kind: "validation",
|
|
4187
|
+
message: "SSML validation failed; the Azure Speech API was not called.",
|
|
4188
|
+
diagnostics: errors
|
|
4189
|
+
}
|
|
4190
|
+
};
|
|
4191
|
+
}
|
|
4192
|
+
try {
|
|
4193
|
+
return { ok: true, success: true, status: "success", value: await client.synthesizeSsml(ssml) };
|
|
4194
|
+
} catch (error) {
|
|
4195
|
+
const azureError = error instanceof AzureTtsError ? error : createSpeechSdkError(error);
|
|
4196
|
+
return { ok: false, success: false, status: "azure-api-error", error: azureError };
|
|
4197
|
+
}
|
|
4198
|
+
}
|
|
4199
|
+
|
|
4200
|
+
// packages/azure-tts-client/src/client.ts
|
|
4201
|
+
var ENDPOINT_TEMPLATE = "https://{region}.tts.speech.microsoft.com/cognitiveservices/v1";
|
|
4202
|
+
var _options;
|
|
4203
|
+
var AzureTtsClient = class {
|
|
4204
|
+
constructor(options) {
|
|
4205
|
+
__privateAdd(this, _options);
|
|
4206
|
+
__privateSet(this, _options, options);
|
|
4207
|
+
}
|
|
4208
|
+
async synthesize(ssml) {
|
|
4209
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
4210
|
+
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
4211
|
+
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
4212
|
+
const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };
|
|
4213
|
+
return synthesizeSpeech(ssml, config);
|
|
4214
|
+
}
|
|
4215
|
+
async synthesizeSsml(ssml) {
|
|
4216
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
4217
|
+
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
4218
|
+
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
4219
|
+
return synthesizeSsml(ssml, { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs });
|
|
4220
|
+
}
|
|
4221
|
+
async synthesizeChunks(chunks, options = {}) {
|
|
4222
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
4223
|
+
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
4224
|
+
return synthesizeSsmlChunks(chunks, {
|
|
4225
|
+
endpoint,
|
|
4226
|
+
region,
|
|
4227
|
+
subscriptionKey,
|
|
4228
|
+
outputFormat,
|
|
4229
|
+
signal,
|
|
4230
|
+
timeoutMs,
|
|
4231
|
+
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress
|
|
4232
|
+
});
|
|
4233
|
+
}
|
|
4234
|
+
async synthesizeSsmlSafe(ssml, options = {}) {
|
|
4235
|
+
return synthesizeSsmlSafe(this, ssml, options);
|
|
2537
4236
|
}
|
|
2538
4237
|
};
|
|
2539
4238
|
_options = new WeakMap();
|
|
@@ -2631,11 +4330,14 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
2631
4330
|
getBuiltInVoiceCatalogMetadata,
|
|
2632
4331
|
isValidAzureAudioDuration,
|
|
2633
4332
|
mapSsmlTextNodes,
|
|
4333
|
+
mergeSynthesisResults,
|
|
2634
4334
|
normalizeAzureLanguage,
|
|
2635
4335
|
parseSsml,
|
|
2636
4336
|
splitSsmlDocument,
|
|
2637
4337
|
synthesizeSpeech,
|
|
2638
4338
|
synthesizeSsml,
|
|
4339
|
+
synthesizeSsmlChunks,
|
|
4340
|
+
synthesizeSsmlSafe,
|
|
2639
4341
|
validateAzureSsml,
|
|
2640
4342
|
validateSsml,
|
|
2641
4343
|
validateSsmlStructureIntegrity
|