ssml-builder-js 2.8.0 → 2.8.1

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/dist/core.d.ts CHANGED
@@ -99,11 +99,15 @@ interface MsttsVisemeElement extends SsmlElementBase {
99
99
  typeValue?: string;
100
100
  visemeType?: string;
101
101
  }
102
+ interface MsttsAudioDurationElement extends SsmlElementBase {
103
+ type: "mstts:audioduration";
104
+ value?: SsmlAttributeValue;
105
+ }
102
106
  interface CustomElement extends SsmlElementBase {
103
107
  type: "custom" | "element";
104
108
  name: string;
105
109
  }
106
- type SsmlElement = VoiceElement | ProsodyElement | BreakElement | ExpressAsElement | SayAsElement | PhonemeElement | EmphasisElement | AudioElement | SubElement | LangElement | MarkElement | BookmarkElement | LexiconElement | ParagraphElement | SentenceElement | WordElement | MsttsSilenceElement | MsttsVisemeElement | CustomElement;
110
+ type SsmlElement = VoiceElement | ProsodyElement | BreakElement | ExpressAsElement | SayAsElement | PhonemeElement | EmphasisElement | AudioElement | SubElement | LangElement | MarkElement | BookmarkElement | LexiconElement | ParagraphElement | SentenceElement | WordElement | MsttsSilenceElement | MsttsVisemeElement | MsttsAudioDurationElement | CustomElement;
107
111
  interface SsmlDocument {
108
112
  type?: "speak";
109
113
  version: string;
@@ -209,10 +213,12 @@ interface AzureValidationOptions {
209
213
  type AzureLanguageNormalizationOptions = Pick<AzureValidationOptions, "languageAliases" | "normalizeLanguage">;
210
214
  /** @deprecated Use AzureValidationOptions instead. */
211
215
  type AzureSsmlValidationOptions = AzureValidationOptions;
216
+ /** Returns whether a value is a positive Azure SSML audio duration. */
217
+ declare function isValidAzureAudioDuration(value: string): boolean;
212
218
  /** Normalizes an Azure language tag using BCP 47 and the configured aliases. */
213
219
  declare function normalizeAzureLanguage(language: string, options?: AzureLanguageNormalizationOptions): string;
214
220
  /** Compares two Azure language tags after BCP 47 and alias normalization. */
215
221
  declare function areAzureLanguagesEquivalent(first: string, second: string, options?: AzureLanguageNormalizationOptions): boolean;
216
222
  declare function validateAzureSsml(ssml: string, options?: AzureValidationOptions): SsmlDiagnostic[];
217
223
 
218
- export { type AudioElement, type AzureDiagnosticCode, type AzureLanguageNormalizationOptions, type AzureSsmlValidationOptions, type AzureValidationOptions, type AzureVoiceDefinition, type AzureVoiceMetadata, type BookmarkElement, type BreakElement, type BuildPartialSsmlOptions, type CustomElement, type EmphasisElement, type ExpressAsElement, type LangElement, type LexiconElement, type MapSsmlTextNodesOptions, type MarkElement, type MsttsSilenceElement, type MsttsVisemeElement, type NamedElement, type ParagraphElement, type PhonemeElement, type ProsodyElement, type SayAsElement, type SentenceElement, type SsmlAttributeValue, type SsmlAttributes, type SsmlBreakElement, type SsmlDiagnostic, type SsmlDiagnosticSeverity, type SsmlDiagnosticSource, type SsmlDocument, type SsmlElement, type SsmlElementBase, type SsmlExpressAsElement, type SsmlNode, type SsmlPartialContext, type SsmlPartialProsody, type SsmlPartialVoice, type SsmlPhonemeElement, type SsmlProsodyElement, type SsmlSayAsElement, type SsmlText, type SsmlTextNodeContext, type SsmlValidationError, type SsmlVoiceElement, type SubElement, type VoiceElement, type WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, validateAzureSsml, validateSsml };
224
+ export { type AudioElement, type AzureDiagnosticCode, type AzureLanguageNormalizationOptions, type AzureSsmlValidationOptions, type AzureValidationOptions, type AzureVoiceDefinition, type AzureVoiceMetadata, type BookmarkElement, type BreakElement, type BuildPartialSsmlOptions, type CustomElement, type EmphasisElement, type ExpressAsElement, type LangElement, type LexiconElement, type MapSsmlTextNodesOptions, type MarkElement, type MsttsAudioDurationElement, type MsttsSilenceElement, type MsttsVisemeElement, type NamedElement, type ParagraphElement, type PhonemeElement, type ProsodyElement, type SayAsElement, type SentenceElement, type SsmlAttributeValue, type SsmlAttributes, type SsmlBreakElement, type SsmlDiagnostic, type SsmlDiagnosticSeverity, type SsmlDiagnosticSource, type SsmlDocument, type SsmlElement, type SsmlElementBase, type SsmlExpressAsElement, type SsmlNode, type SsmlPartialContext, type SsmlPartialProsody, type SsmlPartialVoice, type SsmlPhonemeElement, type SsmlProsodyElement, type SsmlSayAsElement, type SsmlText, type SsmlTextNodeContext, type SsmlValidationError, type SsmlVoiceElement, type SubElement, type VoiceElement, type WordElement, areAzureLanguagesEquivalent, buildPartialSsml, buildSsml, extractSsmlText, isValidAzureAudioDuration, mapSsmlTextNodes, normalizeAzureLanguage, parseSsml, validateAzureSsml, validateSsml };
package/dist/core.js CHANGED
@@ -31,6 +31,7 @@ __export(core_exports, {
31
31
  buildPartialSsml: () => buildPartialSsml,
32
32
  buildSsml: () => buildSsml,
33
33
  extractSsmlText: () => extractSsmlText,
34
+ isValidAzureAudioDuration: () => isValidAzureAudioDuration,
34
35
  mapSsmlTextNodes: () => mapSsmlTextNodes,
35
36
  normalizeAzureLanguage: () => normalizeAzureLanguage,
36
37
  parseSsml: () => parseSsml,
@@ -71,7 +72,8 @@ var SSML_TAGS = {
71
72
  MSTTS_SILENCE: "mstts:silence",
72
73
  SILENCE: "silence",
73
74
  MSTTS_VISEME: "mstts:viseme",
74
- VISEME: "viseme"
75
+ VISEME: "viseme",
76
+ MSTTS_AUDIO_DURATION: "mstts:audioduration"
75
77
  };
76
78
  var SSML_ATTRS = {
77
79
  VERSION: "version",
@@ -200,6 +202,9 @@ function getAttributes(element) {
200
202
  case SSML_TAGS.VISEME:
201
203
  addAttribute(attributes, SSML_ATTRS.TYPE, element.typeValue ?? element.visemeType);
202
204
  break;
205
+ case SSML_TAGS.MSTTS_AUDIO_DURATION:
206
+ addAttribute(attributes, SSML_ATTRS.VALUE, element.value);
207
+ break;
203
208
  case SSML_TAGS.PARAGRAPH:
204
209
  case SSML_TAGS.SENTENCE:
205
210
  case SSML_TAGS.WORD:
@@ -224,6 +229,8 @@ function getTagName(element) {
224
229
  case SSML_TAGS.VISEME:
225
230
  case SSML_TAGS.MSTTS_VISEME:
226
231
  return SSML_TAGS.MSTTS_VISEME;
232
+ case SSML_TAGS.MSTTS_AUDIO_DURATION:
233
+ return SSML_TAGS.MSTTS_AUDIO_DURATION;
227
234
  case "element":
228
235
  case "custom":
229
236
  return element.name;
@@ -758,6 +765,12 @@ function convertElement(node) {
758
765
  if (typeValue !== void 0) element.typeValue = typeValue;
759
766
  return finishElement(element, node, attributes);
760
767
  }
768
+ case SSML_TAGS.MSTTS_AUDIO_DURATION: {
769
+ const element = { type: SSML_TAGS.MSTTS_AUDIO_DURATION };
770
+ const value = readAttribute(attributes, SSML_ATTRS.VALUE);
771
+ if (value !== void 0) element.value = value;
772
+ return finishElement(element, node, attributes);
773
+ }
761
774
  default: {
762
775
  const element = {
763
776
  name: node.name,
@@ -1032,93 +1045,126 @@ async function mapSsmlTextNodes(ssml, transform, options = {}) {
1032
1045
  return result + ssml.slice(cursor);
1033
1046
  }
1034
1047
 
1048
+ // packages/ssml-core/src/generated/azureVoiceDefinitions.ts
1049
+ var AZURE_VOICE_DEFINITIONS = [
1050
+ { name: "de-DE-ConradNeural", locale: "de-DE", styles: ["cheerful", "sad"] },
1051
+ { name: "de-DE-KatjaNeural", locale: "de-DE", styles: ["cheerful", "sad"] },
1052
+ { name: "en-US-AndrewNeural", locale: "en-US", styles: ["empathetic", "relieved"] },
1053
+ {
1054
+ name: "en-US-GuyNeural",
1055
+ locale: "en-US",
1056
+ styles: [
1057
+ "angry",
1058
+ "cheerful",
1059
+ "excited",
1060
+ "friendly",
1061
+ "hopeful",
1062
+ "newscast",
1063
+ "sad",
1064
+ "shouting",
1065
+ "terrified",
1066
+ "unfriendly",
1067
+ "whispering"
1068
+ ]
1069
+ },
1070
+ {
1071
+ name: "en-US-JennyMultilingualNeural",
1072
+ locale: "en-US",
1073
+ styles: [
1074
+ "cheerful",
1075
+ "empathetic",
1076
+ "excited",
1077
+ "friendly",
1078
+ "hopeful",
1079
+ "sad",
1080
+ "shouting",
1081
+ "terrified",
1082
+ "unfriendly",
1083
+ "whispering"
1084
+ ]
1085
+ },
1086
+ {
1087
+ name: "en-US-JennyNeural",
1088
+ locale: "en-US",
1089
+ styles: [
1090
+ "assistant",
1091
+ "chat",
1092
+ "customerservice",
1093
+ "newscast",
1094
+ "cheerful",
1095
+ "empathetic",
1096
+ "excited",
1097
+ "friendly",
1098
+ "hopeful",
1099
+ "sad",
1100
+ "shouting",
1101
+ "terrified",
1102
+ "unfriendly",
1103
+ "whispering"
1104
+ ]
1105
+ },
1106
+ { name: "es-ES-ElviraNeural", locale: "es-ES" },
1107
+ { name: "fil-PH-AngeloNeural", locale: "fil-PH" },
1108
+ { name: "fr-FR-DeniseNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
1109
+ { name: "fr-FR-HenriNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
1110
+ { name: "id-ID-GadisNeural", locale: "id-ID" },
1111
+ { name: "it-IT-ElsaNeural", locale: "it-IT", styles: ["cheerful", "sad"] },
1112
+ { name: "ja-JP-KeitaNeural", locale: "ja-JP", styles: ["chat"] },
1113
+ { name: "ja-JP-MayuNeural", locale: "ja-JP", styles: ["calm", "cheerful", "sad"] },
1114
+ { name: "ja-JP-NanamiNeural", locale: "ja-JP", styles: ["chat", "customerservice", "cheerful", "whispering", "sad"] },
1115
+ { name: "ko-KR-SunHiNeural", locale: "ko-KR", styles: ["cheerful", "sad"] },
1116
+ { name: "ms-MY-YasminNeural", locale: "ms-MY" },
1117
+ { name: "pt-BR-FranciscaNeural", locale: "pt-BR", styles: ["calm"] },
1118
+ {
1119
+ name: "ru-RU-SvetlanaNeural",
1120
+ locale: "ru-RU",
1121
+ styles: ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"]
1122
+ },
1123
+ { name: "th-TH-PremwadeeNeural", locale: "th-TH" },
1124
+ { name: "vi-VN-HoaiMyNeural", locale: "vi-VN" },
1125
+ {
1126
+ name: "zh-CN-XiaoxiaoNeural",
1127
+ locale: "zh-CN",
1128
+ styles: [
1129
+ "assistant",
1130
+ "chat",
1131
+ "customerservice",
1132
+ "newscast",
1133
+ "cheerful",
1134
+ "empathetic",
1135
+ "excited",
1136
+ "friendly",
1137
+ "hopeful",
1138
+ "sad",
1139
+ "terrified",
1140
+ "whispering",
1141
+ "poetry-reading",
1142
+ "sports_commentary",
1143
+ "sports_commentary_excited",
1144
+ "story"
1145
+ ]
1146
+ },
1147
+ {
1148
+ name: "zh-CN-YunxiNeural",
1149
+ locale: "zh-CN",
1150
+ styles: [
1151
+ "narration-relaxed",
1152
+ "embarrassed",
1153
+ "fearful",
1154
+ "sad",
1155
+ "disgruntled",
1156
+ "serious",
1157
+ "angry",
1158
+ "depressed",
1159
+ "chat",
1160
+ "cheerful",
1161
+ "assistant"
1162
+ ]
1163
+ },
1164
+ { name: "zh-TW-HsiaoChenNeural", locale: "zh-TW" }
1165
+ ];
1166
+
1035
1167
  // packages/ssml-core/src/azureValidation.ts
1036
- var EXPRESS_AS_STYLES = {
1037
- "en-us-jennyneural": [
1038
- "assistant",
1039
- "chat",
1040
- "customerservice",
1041
- "newscast",
1042
- "cheerful",
1043
- "empathetic",
1044
- "excited",
1045
- "friendly",
1046
- "hopeful",
1047
- "sad",
1048
- "shouting",
1049
- "terrified",
1050
- "unfriendly",
1051
- "whispering"
1052
- ],
1053
- "en-us-guyneural": [
1054
- "angry",
1055
- "cheerful",
1056
- "excited",
1057
- "friendly",
1058
- "hopeful",
1059
- "newscast",
1060
- "sad",
1061
- "shouting",
1062
- "terrified",
1063
- "unfriendly",
1064
- "whispering"
1065
- ],
1066
- "en-us-jennymultilingualneural": [
1067
- "cheerful",
1068
- "empathetic",
1069
- "excited",
1070
- "friendly",
1071
- "hopeful",
1072
- "sad",
1073
- "shouting",
1074
- "terrified",
1075
- "unfriendly",
1076
- "whispering"
1077
- ],
1078
- "en-us-andrewneural": ["empathetic", "relieved"],
1079
- "ja-jp-mayuneural": ["calm", "cheerful", "sad"],
1080
- "ja-jp-nanamineural": ["chat", "customerservice", "cheerful", "whispering", "sad"],
1081
- "ja-jp-keitaneural": ["chat"],
1082
- "ko-kr-sunhineural": ["cheerful", "sad"],
1083
- "zh-cn-yunxineural": [
1084
- "narration-relaxed",
1085
- "embarrassed",
1086
- "fearful",
1087
- "sad",
1088
- "disgruntled",
1089
- "serious",
1090
- "angry",
1091
- "depressed",
1092
- "chat",
1093
- "cheerful",
1094
- "assistant"
1095
- ],
1096
- "zh-cn-xiaoxiaoneural": [
1097
- "assistant",
1098
- "chat",
1099
- "customerservice",
1100
- "newscast",
1101
- "cheerful",
1102
- "empathetic",
1103
- "excited",
1104
- "friendly",
1105
- "hopeful",
1106
- "sad",
1107
- "terrified",
1108
- "whispering",
1109
- "poetry-reading",
1110
- "sports_commentary",
1111
- "sports_commentary_excited",
1112
- "story"
1113
- ],
1114
- "fr-fr-deniseneural": ["cheerful", "sad"],
1115
- "fr-fr-henrineural": ["cheerful", "sad"],
1116
- "pt-br-franciscaneural": ["calm"],
1117
- "it-it-elsaneural": ["cheerful", "sad"],
1118
- "de-de-katjaneural": ["cheerful", "sad"],
1119
- "de-de-conradneural": ["cheerful", "sad"],
1120
- "ru-ru-svetlananeural": ["cheerful", "sad", "angry", "disgruntled", "embarrassed", "fearful"]
1121
- };
1122
1168
  var ALLOWED_BREAK_STRENGTHS = /* @__PURE__ */ new Set(["none", "x-weak", "weak", "medium", "strong", "x-strong"]);
1123
1169
  var ALLOWED_SAY_AS = /* @__PURE__ */ new Set([
1124
1170
  "characters",
@@ -1247,18 +1293,17 @@ function isSupportedProsodyRate(value) {
1247
1293
  const numericValue = Number(multiplier[1]);
1248
1294
  return numericValue >= 0.5 && numericValue <= 2;
1249
1295
  }
1296
+ function isValidAzureAudioDuration(value) {
1297
+ const trimmed = value.trim();
1298
+ const numeric = /^(\d+(?:\.\d+)?)(ms|s)$/.exec(trimmed);
1299
+ if (numeric) return Number(numeric[1]) > 0;
1300
+ const clock = /^(\d{2,}):([0-5]\d):([0-5]\d)(?:\.(\d{1,3}))?$/.exec(trimmed);
1301
+ if (!clock) return false;
1302
+ return Number(clock[1]) > 0 || Number(clock[2]) > 0 || Number(clock[3]) > 0 || Number(clock[4] ?? 0) > 0;
1303
+ }
1250
1304
  function attr(token, name) {
1251
1305
  return token.attributes.get(name.toLowerCase());
1252
1306
  }
1253
- var ADDITIONAL_VOICE_DEFINITIONS = [
1254
- { name: "zh-TW-HsiaoChenNeural", locale: "zh-TW" },
1255
- { name: "es-ES-ElviraNeural", locale: "es-ES" },
1256
- { name: "th-TH-PremwadeeNeural", locale: "th-TH" },
1257
- { name: "fil-PH-AngeloNeural", locale: "fil-PH" },
1258
- { name: "vi-VN-HoaiMyNeural", locale: "vi-VN" },
1259
- { name: "id-ID-GadisNeural", locale: "id-ID" },
1260
- { name: "ms-MY-YasminNeural", locale: "ms-MY" }
1261
- ];
1262
1307
  var DEFAULT_LANGUAGE_ALIASES = {
1263
1308
  "zh-CN": ["zh-Hans"],
1264
1309
  "zh-TW": ["zh-Hant"]
@@ -1321,10 +1366,7 @@ function definitionFromStyleMap(voiceName, styles) {
1321
1366
  }
1322
1367
  function normalizeVoiceCatalog(options) {
1323
1368
  const definitions = /* @__PURE__ */ new Map();
1324
- for (const [name, styles] of Object.entries(EXPRESS_AS_STYLES)) {
1325
- definitions.set(name.toLowerCase(), definitionFromStyleMap(name, styles));
1326
- }
1327
- for (const definition of ADDITIONAL_VOICE_DEFINITIONS) definitions.set(definition.name.toLowerCase(), definition);
1369
+ for (const definition of AZURE_VOICE_DEFINITIONS) definitions.set(definition.name.toLowerCase(), definition);
1328
1370
  for (const definition of options.voiceCatalog ?? []) definitions.set(definition.name.toLowerCase(), definition);
1329
1371
  for (const definition of options.voiceDefinitions ?? []) definitions.set(definition.name.toLowerCase(), definition);
1330
1372
  for (const definition of options.customVoiceDefinitions ?? [])
@@ -1461,6 +1503,18 @@ function validateElement(token, source, diagnostics, voiceName, options, voiceCa
1461
1503
  if (!value || !/^\d+(?:\.\d+)?(?:ms|s)$/.test(value.trim()))
1462
1504
  addDiagnostic(diagnostics, source, token.start, '<mstts:silence> requires a time-valued "value" attribute.');
1463
1505
  }
1506
+ if (name === "mstts:audioduration") {
1507
+ const value = attr(token, "value");
1508
+ if (!value || !isValidAzureAudioDuration(value))
1509
+ addDiagnostic(
1510
+ diagnostics,
1511
+ source,
1512
+ token.start,
1513
+ '<mstts:audioduration> requires a positive duration such as "10s", "5000ms", or "00:00:10".'
1514
+ );
1515
+ if (!token.selfClosing)
1516
+ addDiagnostic(diagnostics, source, token.start, "<mstts:audioduration> must be self-closing.");
1517
+ }
1464
1518
  if (name === "mstts:viseme") {
1465
1519
  const type = attr(token, "type");
1466
1520
  if (!type || !ALLOWED_VISEME_TYPES.has(type))
@@ -1566,6 +1620,7 @@ function validateAzureSsml(ssml, options = {}) {
1566
1620
  buildPartialSsml,
1567
1621
  buildSsml,
1568
1622
  extractSsmlText,
1623
+ isValidAzureAudioDuration,
1569
1624
  mapSsmlTextNodes,
1570
1625
  normalizeAzureLanguage,
1571
1626
  parseSsml,