ssml-builder-js 2.11.0 → 2.12.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 +36 -3
- package/dist/{chunk-SNRI43QJ.mjs → chunk-4BVNAUVR.mjs} +123 -3
- package/dist/chunk-4BVNAUVR.mjs.map +1 -0
- package/dist/{chunk-FWWMWI2C.mjs → chunk-FHDFRM2F.mjs} +24 -3
- package/dist/chunk-FHDFRM2F.mjs.map +1 -0
- package/dist/core.d.mts +11 -1
- package/dist/core.d.ts +11 -1
- package/dist/core.js +123 -2
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +3 -1
- package/dist/elements.js +23 -2
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +1 -1
- package/dist/index.d.mts +30 -2
- package/dist/index.d.ts +30 -2
- package/dist/index.js +166 -4
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +45 -3
- package/dist/index.mjs.map +1 -1
- package/dist/react.js +163 -4
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +141 -3
- package/dist/react.mjs.map +1 -1
- package/package.json +1 -1
- package/dist/chunk-FWWMWI2C.mjs.map +0 -1
- package/dist/chunk-SNRI43QJ.mjs.map +0 -1
package/dist/elements.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AudioElement, AzureDiagnosticCode, AzureLanguageNormalizationOptions, AzureSsmlValidationOptions, AzureValidationOptions, AzureVoiceCatalogMetadata, AzureVoiceDefinition, AzureVoiceMetadata, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, ExtractSsmlTranslatableTextOptions, FromPlainTextToSsmlOptions, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsAudioDurationElement, MsttsEmbeddingElement, MsttsSilenceElement, MsttsTtsEmbeddingElement, MsttsVisemeElement, MsttsVoiceConversionElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBackgroundAudioNode, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDiagnosticSource, SsmlDialogNode, SsmlDocument, SsmlElement, SsmlElementBase, SsmlEmbeddingNode, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlStructureIntegrityResult, SsmlStructureMismatch, SsmlText, SsmlTextNodeContext, SsmlTtsEmbeddingNode, SsmlTurnNode, SsmlValidationError, SsmlVoiceConversionNode, SsmlVoiceElement, SubElement, VoiceElement, WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, extractSsmlTranslatableText, fromPlainTextToSsml, getAzureVoiceCatalogMetadata, getBuiltInVoiceCatalogMetadata, isValidAzureAudioDuration, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, validateAzureSsml, validateSsml, validateSsmlStructureIntegrity } from './core.mjs';
|
|
1
|
+
export { AudioElement, AzureDiagnosticCode, AzureLanguageNormalizationOptions, AzureSsmlValidationOptions, AzureValidationOptions, AzureVoiceCatalogMetadata, AzureVoiceDefinition, AzureVoiceMetadata, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, ExtractSsmlTranslatableTextOptions, FromPlainTextToSsmlOptions, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsAudioDurationElement, MsttsEmbeddingElement, MsttsSilenceElement, MsttsTtsEmbeddingElement, MsttsVisemeElement, MsttsVoiceConversionElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBackgroundAudioNode, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDiagnosticSource, SsmlDialogNode, SsmlDocument, SsmlElement, SsmlElementBase, SsmlEmbeddingNode, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlStructureIntegrityResult, SsmlStructureMismatch, SsmlText, SsmlTextNodeContext, SsmlTtsEmbeddingNode, SsmlTurnNode, SsmlValidationError, SsmlVoiceConversionNode, SsmlVoiceElement, SubElement, VoiceElement, WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, extractSsmlTranslatableText, fromPlainTextToSsml, getAzureVoiceCatalogMetadata, getBuiltInVoiceCatalogMetadata, isValidAzureAudioDuration, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, splitSsmlDocument, validateAzureSsml, validateSsml, validateSsmlStructureIntegrity } from './core.mjs';
|
|
2
2
|
|
|
3
3
|
interface TtsConfig {
|
|
4
4
|
signal?: AbortSignal;
|
|
@@ -8,6 +8,31 @@ interface TtsConfig {
|
|
|
8
8
|
region: string;
|
|
9
9
|
outputFormat?: string;
|
|
10
10
|
}
|
|
11
|
+
interface SsmlSynthesisBoundary {
|
|
12
|
+
text: string;
|
|
13
|
+
audioOffsetMs: number;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
}
|
|
16
|
+
interface SsmlSynthesisViseme {
|
|
17
|
+
visemeId: number;
|
|
18
|
+
audioOffsetMs: number;
|
|
19
|
+
}
|
|
20
|
+
interface SsmlSynthesisBookmark {
|
|
21
|
+
name: string;
|
|
22
|
+
audioOffsetMs: number;
|
|
23
|
+
}
|
|
24
|
+
/** Audio and Azure Speech synchronization events emitted for one SSML request. */
|
|
25
|
+
interface SsmlSynthesisResult {
|
|
26
|
+
audioData: ArrayBuffer;
|
|
27
|
+
durationMs: number;
|
|
28
|
+
boundaries?: SsmlSynthesisBoundary[];
|
|
29
|
+
/** Alias matching the Azure Speech event name. */
|
|
30
|
+
wordBoundary?: SsmlSynthesisBoundary[];
|
|
31
|
+
/** Alias for consumers that use Azure's word-boundary terminology. */
|
|
32
|
+
wordBoundaries?: SsmlSynthesisBoundary[];
|
|
33
|
+
visemes?: SsmlSynthesisViseme[];
|
|
34
|
+
bookmarks?: SsmlSynthesisBookmark[];
|
|
35
|
+
}
|
|
11
36
|
interface AzureTtsLogger {
|
|
12
37
|
debug?: (...args: unknown[]) => void;
|
|
13
38
|
info?: (...args: unknown[]) => void;
|
|
@@ -40,8 +65,11 @@ declare class AzureTtsClient {
|
|
|
40
65
|
#private;
|
|
41
66
|
constructor(options: AzureTtsClientOptions);
|
|
42
67
|
synthesize(ssml: string): Promise<ArrayBuffer>;
|
|
68
|
+
synthesizeSsml(ssml: string): Promise<SsmlSynthesisResult>;
|
|
43
69
|
}
|
|
44
70
|
|
|
71
|
+
declare function synthesizeSsml(ssml: string, config: TtsConfig): Promise<SsmlSynthesisResult>;
|
|
72
|
+
/** Backward-compatible audio-only synthesis helper. */
|
|
45
73
|
declare function synthesizeSpeech(ssml: string, config: TtsConfig): Promise<ArrayBuffer>;
|
|
46
74
|
|
|
47
75
|
interface FetchAzureVoiceCatalogOptions {
|
|
@@ -69,4 +97,4 @@ interface AzureVoiceCatalog {
|
|
|
69
97
|
/** Fetches and deduplicates the current Azure Speech voice catalog for one or more regions. */
|
|
70
98
|
declare function fetchAzureVoiceCatalog(options: FetchAzureVoiceCatalogOptions): Promise<AzureVoiceCatalog>;
|
|
71
99
|
|
|
72
|
-
export { AzureTtsClient, type AzureTtsClientOptions, AzureTtsError, type AzureTtsLogger, AzureTtsSdkError, type AzureVoiceCatalog, type AzureVoiceCatalogVoice, type FetchAzureVoiceCatalogOptions, type FetchedAzureVoiceCatalogMetadata, type TtsConfig, fetchAzureVoiceCatalog, synthesizeSpeech };
|
|
100
|
+
export { AzureTtsClient, type AzureTtsClientOptions, AzureTtsError, type AzureTtsLogger, AzureTtsSdkError, type AzureVoiceCatalog, type AzureVoiceCatalogVoice, type FetchAzureVoiceCatalogOptions, type FetchedAzureVoiceCatalogMetadata, type SsmlSynthesisBookmark, type SsmlSynthesisBoundary, type SsmlSynthesisResult, type SsmlSynthesisViseme, type TtsConfig, fetchAzureVoiceCatalog, synthesizeSpeech, synthesizeSsml };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { AudioElement, AzureDiagnosticCode, AzureLanguageNormalizationOptions, AzureSsmlValidationOptions, AzureValidationOptions, AzureVoiceCatalogMetadata, AzureVoiceDefinition, AzureVoiceMetadata, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, ExtractSsmlTranslatableTextOptions, FromPlainTextToSsmlOptions, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsAudioDurationElement, MsttsEmbeddingElement, MsttsSilenceElement, MsttsTtsEmbeddingElement, MsttsVisemeElement, MsttsVoiceConversionElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBackgroundAudioNode, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDiagnosticSource, SsmlDialogNode, SsmlDocument, SsmlElement, SsmlElementBase, SsmlEmbeddingNode, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlStructureIntegrityResult, SsmlStructureMismatch, SsmlText, SsmlTextNodeContext, SsmlTtsEmbeddingNode, SsmlTurnNode, SsmlValidationError, SsmlVoiceConversionNode, SsmlVoiceElement, SubElement, VoiceElement, WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, extractSsmlTranslatableText, fromPlainTextToSsml, getAzureVoiceCatalogMetadata, getBuiltInVoiceCatalogMetadata, isValidAzureAudioDuration, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, validateAzureSsml, validateSsml, validateSsmlStructureIntegrity } from './core.js';
|
|
1
|
+
export { AudioElement, AzureDiagnosticCode, AzureLanguageNormalizationOptions, AzureSsmlValidationOptions, AzureValidationOptions, AzureVoiceCatalogMetadata, AzureVoiceDefinition, AzureVoiceMetadata, BookmarkElement, BreakElement, BuildPartialSsmlOptions, CustomElement, EmphasisElement, ExpressAsElement, ExtractSsmlTranslatableTextOptions, FromPlainTextToSsmlOptions, LangElement, LexiconElement, MapSsmlTextNodesOptions, MarkElement, MsttsAudioDurationElement, MsttsEmbeddingElement, MsttsSilenceElement, MsttsTtsEmbeddingElement, MsttsVisemeElement, MsttsVoiceConversionElement, NamedElement, ParagraphElement, PhonemeElement, ProsodyElement, SayAsElement, SentenceElement, SsmlAttributeValue, SsmlAttributes, SsmlBackgroundAudioNode, SsmlBreakElement, SsmlDiagnostic, SsmlDiagnosticSeverity, SsmlDiagnosticSource, SsmlDialogNode, SsmlDocument, SsmlElement, SsmlElementBase, SsmlEmbeddingNode, SsmlExpressAsElement, SsmlNode, SsmlPartialContext, SsmlPartialProsody, SsmlPartialVoice, SsmlPhonemeElement, SsmlProsodyElement, SsmlSayAsElement, SsmlStructureIntegrityResult, SsmlStructureMismatch, SsmlText, SsmlTextNodeContext, SsmlTtsEmbeddingNode, SsmlTurnNode, SsmlValidationError, SsmlVoiceConversionNode, SsmlVoiceElement, SubElement, VoiceElement, WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, extractSsmlTranslatableText, fromPlainTextToSsml, getAzureVoiceCatalogMetadata, getBuiltInVoiceCatalogMetadata, isValidAzureAudioDuration, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, splitSsmlDocument, validateAzureSsml, validateSsml, validateSsmlStructureIntegrity } from './core.js';
|
|
2
2
|
|
|
3
3
|
interface TtsConfig {
|
|
4
4
|
signal?: AbortSignal;
|
|
@@ -8,6 +8,31 @@ interface TtsConfig {
|
|
|
8
8
|
region: string;
|
|
9
9
|
outputFormat?: string;
|
|
10
10
|
}
|
|
11
|
+
interface SsmlSynthesisBoundary {
|
|
12
|
+
text: string;
|
|
13
|
+
audioOffsetMs: number;
|
|
14
|
+
durationMs: number;
|
|
15
|
+
}
|
|
16
|
+
interface SsmlSynthesisViseme {
|
|
17
|
+
visemeId: number;
|
|
18
|
+
audioOffsetMs: number;
|
|
19
|
+
}
|
|
20
|
+
interface SsmlSynthesisBookmark {
|
|
21
|
+
name: string;
|
|
22
|
+
audioOffsetMs: number;
|
|
23
|
+
}
|
|
24
|
+
/** Audio and Azure Speech synchronization events emitted for one SSML request. */
|
|
25
|
+
interface SsmlSynthesisResult {
|
|
26
|
+
audioData: ArrayBuffer;
|
|
27
|
+
durationMs: number;
|
|
28
|
+
boundaries?: SsmlSynthesisBoundary[];
|
|
29
|
+
/** Alias matching the Azure Speech event name. */
|
|
30
|
+
wordBoundary?: SsmlSynthesisBoundary[];
|
|
31
|
+
/** Alias for consumers that use Azure's word-boundary terminology. */
|
|
32
|
+
wordBoundaries?: SsmlSynthesisBoundary[];
|
|
33
|
+
visemes?: SsmlSynthesisViseme[];
|
|
34
|
+
bookmarks?: SsmlSynthesisBookmark[];
|
|
35
|
+
}
|
|
11
36
|
interface AzureTtsLogger {
|
|
12
37
|
debug?: (...args: unknown[]) => void;
|
|
13
38
|
info?: (...args: unknown[]) => void;
|
|
@@ -40,8 +65,11 @@ declare class AzureTtsClient {
|
|
|
40
65
|
#private;
|
|
41
66
|
constructor(options: AzureTtsClientOptions);
|
|
42
67
|
synthesize(ssml: string): Promise<ArrayBuffer>;
|
|
68
|
+
synthesizeSsml(ssml: string): Promise<SsmlSynthesisResult>;
|
|
43
69
|
}
|
|
44
70
|
|
|
71
|
+
declare function synthesizeSsml(ssml: string, config: TtsConfig): Promise<SsmlSynthesisResult>;
|
|
72
|
+
/** Backward-compatible audio-only synthesis helper. */
|
|
45
73
|
declare function synthesizeSpeech(ssml: string, config: TtsConfig): Promise<ArrayBuffer>;
|
|
46
74
|
|
|
47
75
|
interface FetchAzureVoiceCatalogOptions {
|
|
@@ -69,4 +97,4 @@ interface AzureVoiceCatalog {
|
|
|
69
97
|
/** Fetches and deduplicates the current Azure Speech voice catalog for one or more regions. */
|
|
70
98
|
declare function fetchAzureVoiceCatalog(options: FetchAzureVoiceCatalogOptions): Promise<AzureVoiceCatalog>;
|
|
71
99
|
|
|
72
|
-
export { AzureTtsClient, type AzureTtsClientOptions, AzureTtsError, type AzureTtsLogger, AzureTtsSdkError, type AzureVoiceCatalog, type AzureVoiceCatalogVoice, type FetchAzureVoiceCatalogOptions, type FetchedAzureVoiceCatalogMetadata, type TtsConfig, fetchAzureVoiceCatalog, synthesizeSpeech };
|
|
100
|
+
export { AzureTtsClient, type AzureTtsClientOptions, AzureTtsError, type AzureTtsLogger, AzureTtsSdkError, type AzureVoiceCatalog, type AzureVoiceCatalogVoice, type FetchAzureVoiceCatalogOptions, type FetchedAzureVoiceCatalogMetadata, type SsmlSynthesisBookmark, type SsmlSynthesisBoundary, type SsmlSynthesisResult, type SsmlSynthesisViseme, type TtsConfig, fetchAzureVoiceCatalog, synthesizeSpeech, synthesizeSsml };
|
package/dist/index.js
CHANGED
|
@@ -53,7 +53,9 @@ __export(src_exports, {
|
|
|
53
53
|
mapSsmlTextNodes: () => mapSsmlTextNodes,
|
|
54
54
|
normalizeAzureLanguage: () => normalizeAzureLanguage,
|
|
55
55
|
parseSsml: () => parseSsml,
|
|
56
|
+
splitSsmlDocument: () => splitSsmlDocument,
|
|
56
57
|
synthesizeSpeech: () => synthesizeSpeech,
|
|
58
|
+
synthesizeSsml: () => synthesizeSsml,
|
|
57
59
|
validateAzureSsml: () => validateAzureSsml,
|
|
58
60
|
validateSsml: () => validateSsml,
|
|
59
61
|
validateSsmlStructureIntegrity: () => validateSsmlStructureIntegrity
|
|
@@ -987,6 +989,104 @@ function buildPartialSsml(textOrOptions, context) {
|
|
|
987
989
|
return serializePartialSsml(textOrOptions.text, textOrOptions);
|
|
988
990
|
}
|
|
989
991
|
|
|
992
|
+
// packages/ssml-core/src/split.ts
|
|
993
|
+
var DEFAULT_MAX_LENGTH = 1e4;
|
|
994
|
+
function cloneElement(element, children) {
|
|
995
|
+
return { ...element, children };
|
|
996
|
+
}
|
|
997
|
+
function documentWithChildren(document, children) {
|
|
998
|
+
return buildSsml({ ...document, children });
|
|
999
|
+
}
|
|
1000
|
+
function wrapWithContext(node, context) {
|
|
1001
|
+
return context.reduceRight((current, parent) => cloneElement(parent, [current]), node);
|
|
1002
|
+
}
|
|
1003
|
+
function documentWithNode(document, node, context) {
|
|
1004
|
+
return documentWithChildren(document, [wrapWithContext(node, context)]);
|
|
1005
|
+
}
|
|
1006
|
+
function splitTextNode(text, document, context, maxLength) {
|
|
1007
|
+
if (!text) return [text];
|
|
1008
|
+
const parts = [];
|
|
1009
|
+
let start = 0;
|
|
1010
|
+
while (start < text.length) {
|
|
1011
|
+
let end = start + 1;
|
|
1012
|
+
let bestEnd = end;
|
|
1013
|
+
while (end <= text.length) {
|
|
1014
|
+
if (documentWithNode(document, text.slice(start, end), context).length > maxLength) break;
|
|
1015
|
+
bestEnd = end;
|
|
1016
|
+
end += 1;
|
|
1017
|
+
}
|
|
1018
|
+
if (bestEnd === start) {
|
|
1019
|
+
throw new RangeError("maxLength is too small to contain the SSML document wrapper");
|
|
1020
|
+
}
|
|
1021
|
+
const segment = text.slice(start, bestEnd);
|
|
1022
|
+
const boundary = Math.max(segment.lastIndexOf(" "), segment.lastIndexOf("\n"), segment.lastIndexOf(" "));
|
|
1023
|
+
const splitEnd = boundary > 0 ? start + boundary + 1 : bestEnd;
|
|
1024
|
+
parts.push(text.slice(start, splitEnd));
|
|
1025
|
+
start = splitEnd;
|
|
1026
|
+
}
|
|
1027
|
+
return parts;
|
|
1028
|
+
}
|
|
1029
|
+
function splitNode(document, node, maxLength, context = []) {
|
|
1030
|
+
if (typeof node === "string" || node.type === "text") {
|
|
1031
|
+
const value = typeof node === "string" ? node : node.value;
|
|
1032
|
+
if (documentWithNode(document, node, context).length <= maxLength) return [node];
|
|
1033
|
+
return splitTextNode(value, document, context, maxLength);
|
|
1034
|
+
}
|
|
1035
|
+
if (documentWithNode(document, node, context).length <= maxLength) return [node];
|
|
1036
|
+
const children = node.children ?? [];
|
|
1037
|
+
if (children.length === 0) {
|
|
1038
|
+
throw new RangeError(`maxLength is too small to contain <${node.type}>`);
|
|
1039
|
+
}
|
|
1040
|
+
const splitChildren = children.flatMap((child) => splitNode(document, child, maxLength, [...context, node]));
|
|
1041
|
+
const parts = [];
|
|
1042
|
+
let group = [];
|
|
1043
|
+
const flush = () => {
|
|
1044
|
+
if (group.length > 0) {
|
|
1045
|
+
parts.push(cloneElement(node, group));
|
|
1046
|
+
group = [];
|
|
1047
|
+
}
|
|
1048
|
+
};
|
|
1049
|
+
for (const child of splitChildren) {
|
|
1050
|
+
const candidate = cloneElement(node, [...group, child]);
|
|
1051
|
+
if (documentWithNode(document, candidate, context).length <= maxLength) {
|
|
1052
|
+
group.push(child);
|
|
1053
|
+
continue;
|
|
1054
|
+
}
|
|
1055
|
+
flush();
|
|
1056
|
+
if (documentWithNode(document, cloneElement(node, [child]), context).length > maxLength) {
|
|
1057
|
+
throw new RangeError(`maxLength is too small to contain <${node.type}>`);
|
|
1058
|
+
}
|
|
1059
|
+
group.push(child);
|
|
1060
|
+
}
|
|
1061
|
+
flush();
|
|
1062
|
+
return parts;
|
|
1063
|
+
}
|
|
1064
|
+
function splitSsmlDocument(ssml, maxLength = DEFAULT_MAX_LENGTH) {
|
|
1065
|
+
if (!Number.isInteger(maxLength) || maxLength <= 0) {
|
|
1066
|
+
throw new RangeError("maxLength must be a positive integer");
|
|
1067
|
+
}
|
|
1068
|
+
const document = parseSsml(ssml);
|
|
1069
|
+
if (ssml.length <= maxLength) return [ssml];
|
|
1070
|
+
const children = document.children ?? [];
|
|
1071
|
+
const splitChildren = children.flatMap((child) => splitNode(document, child, maxLength));
|
|
1072
|
+
const chunks = [];
|
|
1073
|
+
let group = [];
|
|
1074
|
+
for (const child of splitChildren) {
|
|
1075
|
+
const candidate = [...group, child];
|
|
1076
|
+
if (documentWithChildren(document, candidate).length <= maxLength) {
|
|
1077
|
+
group = candidate;
|
|
1078
|
+
continue;
|
|
1079
|
+
}
|
|
1080
|
+
if (group.length > 0) chunks.push(group);
|
|
1081
|
+
group = [child];
|
|
1082
|
+
if (documentWithChildren(document, group).length > maxLength) {
|
|
1083
|
+
throw new RangeError("maxLength is too small to contain the SSML document wrapper");
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
if (group.length > 0) chunks.push(group);
|
|
1087
|
+
return chunks.map((chunk) => documentWithChildren(document, chunk));
|
|
1088
|
+
}
|
|
1089
|
+
|
|
990
1090
|
// packages/ssml-core/src/validation.ts
|
|
991
1091
|
var PARSER_POSITION_SUFFIX = / at position (\d+)$/;
|
|
992
1092
|
function validateSsml(xmlString) {
|
|
@@ -1681,6 +1781,7 @@ function tokenizeElements(source) {
|
|
|
1681
1781
|
attributes,
|
|
1682
1782
|
childElementIndex,
|
|
1683
1783
|
end,
|
|
1784
|
+
depth: openElements.length + 1,
|
|
1684
1785
|
name: tokenName,
|
|
1685
1786
|
parentName: parent?.name,
|
|
1686
1787
|
parentVoiceName,
|
|
@@ -1871,13 +1972,18 @@ function validateAudioSource(token, source, diagnostics, options, elementName2)
|
|
|
1871
1972
|
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must be an absolute HTTP(S) URL.`);
|
|
1872
1973
|
return;
|
|
1873
1974
|
}
|
|
1975
|
+
if (parsed.username || parsed.password)
|
|
1976
|
+
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must not contain URL credentials.`);
|
|
1874
1977
|
if (parsed.protocol !== "https:" && !(options.allowHttpAudio && parsed.protocol === "http:"))
|
|
1875
1978
|
addDiagnostic(diagnostics, source, token.start, `<${elementName2} src> must use HTTPS.`);
|
|
1876
1979
|
const isAllowedOrigin = options.allowedAudioOrigins?.some((allowedOrigin) => {
|
|
1877
1980
|
try {
|
|
1878
|
-
|
|
1981
|
+
const configured = new URL(allowedOrigin);
|
|
1982
|
+
if (configured.protocol !== "https:" && configured.protocol !== "http:" || configured.username || configured.password || configured.pathname !== "/" || configured.search || configured.hash)
|
|
1983
|
+
return false;
|
|
1984
|
+
return configured.origin === parsed.origin;
|
|
1879
1985
|
} catch {
|
|
1880
|
-
return
|
|
1986
|
+
return false;
|
|
1881
1987
|
}
|
|
1882
1988
|
}) ?? false;
|
|
1883
1989
|
if (options.allowedAudioOrigins && !isAllowedOrigin)
|
|
@@ -2085,6 +2191,9 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
2085
2191
|
const maxLength = options.maxLength ?? 1e4;
|
|
2086
2192
|
if (ssml.length > maxLength)
|
|
2087
2193
|
addDiagnostic(diagnostics, ssml, maxLength, `SSML exceeds the maximum length of ${maxLength} characters.`);
|
|
2194
|
+
if (options.maxXmlDepth !== void 0 && (!Number.isInteger(options.maxXmlDepth) || options.maxXmlDepth <= 0)) {
|
|
2195
|
+
addDiagnostic(diagnostics, ssml, 0, "maxXmlDepth must be a positive integer.");
|
|
2196
|
+
}
|
|
2088
2197
|
try {
|
|
2089
2198
|
parseSsml(ssml);
|
|
2090
2199
|
} catch (error) {
|
|
@@ -2094,6 +2203,18 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
2094
2203
|
return diagnostics;
|
|
2095
2204
|
}
|
|
2096
2205
|
const tokens = tokenizeElements(ssml);
|
|
2206
|
+
if (options.maxXmlDepth !== void 0) {
|
|
2207
|
+
for (const token of tokens) {
|
|
2208
|
+
if (token.depth > options.maxXmlDepth) {
|
|
2209
|
+
addDiagnostic(
|
|
2210
|
+
diagnostics,
|
|
2211
|
+
ssml,
|
|
2212
|
+
token.start,
|
|
2213
|
+
`XML nesting depth ${token.depth} exceeds the configured maximum of ${options.maxXmlDepth}.`
|
|
2214
|
+
);
|
|
2215
|
+
}
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2097
2218
|
const speak = tokens.find((token) => token.name.toLowerCase() === "speak");
|
|
2098
2219
|
const voices = tokens.filter((token) => token.name.toLowerCase() === "voice");
|
|
2099
2220
|
const backgroundAudioTokens = tokens.filter((token) => token.name.toLowerCase() === "mstts:backgroundaudio");
|
|
@@ -2302,7 +2423,8 @@ function closeSpeechResources(speechConfig, synthesizer) {
|
|
|
2302
2423
|
} catch {
|
|
2303
2424
|
}
|
|
2304
2425
|
}
|
|
2305
|
-
|
|
2426
|
+
var ticksToMilliseconds = (ticks) => Math.max(0, ticks) / 1e4;
|
|
2427
|
+
async function synthesizeSsml(ssml, config) {
|
|
2306
2428
|
if (config.signal?.aborted) {
|
|
2307
2429
|
throw createSpeechSdkError("Speech synthesis was cancelled.");
|
|
2308
2430
|
}
|
|
@@ -2329,6 +2451,22 @@ async function synthesizeSpeech(ssml, config) {
|
|
|
2329
2451
|
closeResources();
|
|
2330
2452
|
reject(createSpeechSdkError(error));
|
|
2331
2453
|
};
|
|
2454
|
+
const boundaries = [];
|
|
2455
|
+
const visemes = [];
|
|
2456
|
+
const bookmarks = [];
|
|
2457
|
+
synthesizer.wordBoundary = (_sender, event) => {
|
|
2458
|
+
boundaries.push({
|
|
2459
|
+
text: event.text,
|
|
2460
|
+
audioOffsetMs: ticksToMilliseconds(event.audioOffset),
|
|
2461
|
+
durationMs: ticksToMilliseconds(event.duration)
|
|
2462
|
+
});
|
|
2463
|
+
};
|
|
2464
|
+
synthesizer.visemeReceived = (_sender, event) => {
|
|
2465
|
+
visemes.push({ visemeId: event.visemeId, audioOffsetMs: ticksToMilliseconds(event.audioOffset) });
|
|
2466
|
+
};
|
|
2467
|
+
synthesizer.bookmarkReached = (_sender, event) => {
|
|
2468
|
+
bookmarks.push({ name: event.text, audioOffsetMs: ticksToMilliseconds(event.audioOffset) });
|
|
2469
|
+
};
|
|
2332
2470
|
const cb = (result) => {
|
|
2333
2471
|
if (settled) return;
|
|
2334
2472
|
const { reason, errorDetails } = result;
|
|
@@ -2340,7 +2478,20 @@ async function synthesizeSpeech(ssml, config) {
|
|
|
2340
2478
|
settled = true;
|
|
2341
2479
|
cleanup();
|
|
2342
2480
|
closeResources();
|
|
2343
|
-
|
|
2481
|
+
const eventDurationMs = Math.max(
|
|
2482
|
+
0,
|
|
2483
|
+
...(boundaries ?? []).map((boundary) => boundary.audioOffsetMs + boundary.durationMs),
|
|
2484
|
+
...(visemes ?? []).map((viseme) => viseme.audioOffsetMs),
|
|
2485
|
+
...(bookmarks ?? []).map((bookmark) => bookmark.audioOffsetMs)
|
|
2486
|
+
);
|
|
2487
|
+
const durationMs = result.audioDuration ? ticksToMilliseconds(result.audioDuration) : eventDurationMs;
|
|
2488
|
+
resolve({
|
|
2489
|
+
audioData: result.audioData,
|
|
2490
|
+
durationMs,
|
|
2491
|
+
...boundaries.length > 0 ? { boundaries, wordBoundary: boundaries, wordBoundaries: boundaries } : {},
|
|
2492
|
+
...visemes.length > 0 ? { visemes } : {},
|
|
2493
|
+
...bookmarks.length > 0 ? { bookmarks } : {}
|
|
2494
|
+
});
|
|
2344
2495
|
};
|
|
2345
2496
|
try {
|
|
2346
2497
|
if (config.signal) {
|
|
@@ -2359,6 +2510,9 @@ async function synthesizeSpeech(ssml, config) {
|
|
|
2359
2510
|
}
|
|
2360
2511
|
});
|
|
2361
2512
|
}
|
|
2513
|
+
async function synthesizeSpeech(ssml, config) {
|
|
2514
|
+
return (await synthesizeSsml(ssml, config)).audioData;
|
|
2515
|
+
}
|
|
2362
2516
|
|
|
2363
2517
|
// packages/azure-tts-client/src/client.ts
|
|
2364
2518
|
var ENDPOINT_TEMPLATE = "https://{region}.tts.speech.microsoft.com/cognitiveservices/v1";
|
|
@@ -2375,6 +2529,12 @@ var AzureTtsClient = class {
|
|
|
2375
2529
|
const config = { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs };
|
|
2376
2530
|
return synthesizeSpeech(ssml, config);
|
|
2377
2531
|
}
|
|
2532
|
+
async synthesizeSsml(ssml) {
|
|
2533
|
+
const { region, subscriptionKey, outputFormat, signal, timeoutMs } = __privateGet(this, _options);
|
|
2534
|
+
const endpoint = __privateGet(this, _options).endpoint?.trim() || ENDPOINT_TEMPLATE.replace("{region}", region);
|
|
2535
|
+
__privateGet(this, _options).logger?.debug?.("Using Azure TTS endpoint:", endpoint);
|
|
2536
|
+
return synthesizeSsml(ssml, { endpoint, region, subscriptionKey, outputFormat, signal, timeoutMs });
|
|
2537
|
+
}
|
|
2378
2538
|
};
|
|
2379
2539
|
_options = new WeakMap();
|
|
2380
2540
|
|
|
@@ -2473,7 +2633,9 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
2473
2633
|
mapSsmlTextNodes,
|
|
2474
2634
|
normalizeAzureLanguage,
|
|
2475
2635
|
parseSsml,
|
|
2636
|
+
splitSsmlDocument,
|
|
2476
2637
|
synthesizeSpeech,
|
|
2638
|
+
synthesizeSsml,
|
|
2477
2639
|
validateAzureSsml,
|
|
2478
2640
|
validateSsml,
|
|
2479
2641
|
validateSsmlStructureIntegrity
|