ssml-builder-js 2.13.0 → 2.14.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/dist/index.mjs CHANGED
@@ -2,6 +2,7 @@ import {
2
2
  areAzureLanguagesEquivalent,
3
3
  buildPartialSsml,
4
4
  buildSsml,
5
+ createAzureUrlValidatorRunner,
5
6
  extractSsmlText,
6
7
  extractSsmlTranslatableText,
7
8
  fromPlainTextToSsml,
@@ -15,10 +16,10 @@ import {
15
16
  validateAzureSsml,
16
17
  validateSsml,
17
18
  validateSsmlStructureIntegrity
18
- } from "./chunk-25LOR4AJ.mjs";
19
+ } from "./chunk-AQ55MOPU.mjs";
19
20
  import {
20
21
  validateAzureSsml as validateAzureSsml2
21
- } from "./chunk-CZ2F3TET.mjs";
22
+ } from "./chunk-QFIBPCO4.mjs";
22
23
  import {
23
24
  __privateAdd,
24
25
  __privateGet,
@@ -44,6 +45,13 @@ var AzureTtsSdkError = class extends AzureTtsError {
44
45
  this.errorDetails = errorDetails;
45
46
  }
46
47
  };
48
+ var UnsupportedMergeFormatError = class extends Error {
49
+ constructor(format) {
50
+ super(`Audio format "${format}" cannot be safely concatenated; container re-multiplexing is required.`);
51
+ this.name = "UnsupportedMergeFormatError";
52
+ this.format = format;
53
+ }
54
+ };
47
55
  function createSpeechSdkError(error) {
48
56
  const message = error instanceof Error ? error.message : String(error);
49
57
  return new AzureTtsSdkError(message);
@@ -121,6 +129,149 @@ function createSpeechConfig(config) {
121
129
  }
122
130
 
123
131
  // packages/azure-tts-client/src/synthesis.ts
132
+ function ascii(bytes, offset, value) {
133
+ return [...value].every((character, index) => bytes[offset + index] === character.charCodeAt(0));
134
+ }
135
+ function readUint32(bytes, offset) {
136
+ return new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength).getUint32(offset, true);
137
+ }
138
+ function parseWav(buffer) {
139
+ const bytes = new Uint8Array(buffer);
140
+ if (bytes.byteLength < 12 || !ascii(bytes, 0, "RIFF") || !ascii(bytes, 8, "WAVE")) {
141
+ throw new Error("Invalid WAV/RIFF audio buffer.");
142
+ }
143
+ const chunks = [];
144
+ const dataParts = [];
145
+ let format;
146
+ let offset = 12;
147
+ while (offset < bytes.byteLength) {
148
+ if (offset + 8 > bytes.byteLength) throw new Error("Invalid WAV chunk header.");
149
+ const id = String.fromCharCode(...bytes.slice(offset, offset + 4));
150
+ const size = readUint32(bytes, offset + 4);
151
+ const dataStart = offset + 8;
152
+ const dataEnd = dataStart + size;
153
+ if (dataEnd > bytes.byteLength) throw new Error(`WAV chunk "${id}" exceeds the audio buffer.`);
154
+ const data2 = bytes.slice(dataStart, dataEnd);
155
+ chunks.push({ id, data: data2 });
156
+ if (id === "fmt ") format ?? (format = data2);
157
+ if (id === "data") dataParts.push(data2);
158
+ offset = dataEnd + (size & 1);
159
+ if (offset > bytes.byteLength) throw new Error("Invalid WAV chunk padding.");
160
+ }
161
+ if (!format || dataParts.length === 0) throw new Error("WAV audio must contain fmt and data chunks.");
162
+ const dataLength = dataParts.reduce((total, part) => total + part.byteLength, 0);
163
+ const data = new Uint8Array(dataLength);
164
+ let dataOffset = 0;
165
+ for (const part of dataParts) {
166
+ data.set(part, dataOffset);
167
+ dataOffset += part.byteLength;
168
+ }
169
+ return { chunks, data, format };
170
+ }
171
+ function writeUint32(target, offset, value) {
172
+ new DataView(target.buffer).setUint32(offset, value, true);
173
+ }
174
+ function writeChunk(target, offset, id, data) {
175
+ for (let index = 0; index < 4; index += 1) target[offset + index] = id.charCodeAt(index) ?? 0;
176
+ writeUint32(target, offset + 4, data.byteLength);
177
+ target.set(data, offset + 8);
178
+ const end = offset + 8 + data.byteLength;
179
+ if (data.byteLength & 1) target[end] = 0;
180
+ return end + (data.byteLength & 1);
181
+ }
182
+ function mergeWavBuffers(buffers) {
183
+ if (buffers.length === 0) return new ArrayBuffer(0);
184
+ const parsed = buffers.map(parseWav);
185
+ const first = parsed[0];
186
+ if (!first) throw new Error("At least one WAV buffer is required.");
187
+ if (parsed.some(
188
+ (item) => item.format.length !== first.format.length || item.format.some((value, i) => value !== first.format[i])
189
+ ))
190
+ throw new Error("WAV buffers have incompatible fmt chunks.");
191
+ const dataLength = parsed.reduce((total, item) => total + item.data.byteLength, 0);
192
+ const nonDataLength = first.chunks.reduce(
193
+ (total, chunk) => chunk.id === "data" ? total : total + 8 + chunk.data.byteLength + (chunk.data.byteLength & 1),
194
+ 0
195
+ );
196
+ const outputLength = 12 + nonDataLength + 8 + dataLength + (dataLength & 1);
197
+ if (outputLength - 8 > 4294967295) throw new RangeError("Merged WAV exceeds the RIFF format size limit.");
198
+ const output = new Uint8Array(outputLength);
199
+ output.set(Uint8Array.from([82, 73, 70, 70]), 0);
200
+ writeUint32(output, 4, outputLength - 8);
201
+ output.set(Uint8Array.from([87, 65, 86, 69]), 8);
202
+ let outputOffset = 12;
203
+ let dataWritten = false;
204
+ for (const chunk of first.chunks) {
205
+ if (chunk.id === "data") {
206
+ if (dataWritten) continue;
207
+ const data = new Uint8Array(dataLength);
208
+ let dataOffset = 0;
209
+ for (const item of parsed) {
210
+ data.set(item.data, dataOffset);
211
+ dataOffset += item.data.byteLength;
212
+ }
213
+ outputOffset = writeChunk(output, outputOffset, "data", data);
214
+ dataWritten = true;
215
+ } else {
216
+ outputOffset = writeChunk(output, outputOffset, chunk.id, chunk.data);
217
+ }
218
+ }
219
+ if (!dataWritten) throw new Error("WAV audio must contain a data chunk.");
220
+ return output.buffer;
221
+ }
222
+ function skipId3v2(bytes) {
223
+ if (!ascii(bytes, 0, "ID3") || bytes.byteLength < 10) return 0;
224
+ const size = [bytes[6], bytes[7], bytes[8], bytes[9]].reduce((total, value) => total << 7 | value & 127, 0);
225
+ const hasFooter = (bytes[5] & 16) !== 0;
226
+ return Math.min(bytes.byteLength, 10 + size + (hasFooter ? 10 : 0));
227
+ }
228
+ function stripMp3Tags(buffer) {
229
+ const bytes = new Uint8Array(buffer);
230
+ const start = skipId3v2(bytes);
231
+ const end = bytes.byteLength >= 128 && ascii(bytes, bytes.byteLength - 128, "TAG") ? bytes.byteLength - 128 : bytes.byteLength;
232
+ return bytes.slice(Math.min(start, end), end);
233
+ }
234
+ function isMp3Format(format) {
235
+ return /(?:mp3|mpeg)/i.test(format);
236
+ }
237
+ function isWavFormat(format) {
238
+ return /(?:wav|wave|riff)/i.test(format);
239
+ }
240
+ function isRawFormat(format) {
241
+ return /^raw(?:-|$)/i.test(format);
242
+ }
243
+ function resolveMergeAudioFormat(format) {
244
+ if (isWavFormat(format)) return "wav";
245
+ if (isMp3Format(format)) return "mp3";
246
+ if (isRawFormat(format)) return "raw";
247
+ return void 0;
248
+ }
249
+ function canMergeAudioFormat(format) {
250
+ return resolveMergeAudioFormat(format) !== void 0;
251
+ }
252
+ function mergeAudioBuffers(buffers, format) {
253
+ if (isWavFormat(format)) return mergeWavBuffers(buffers);
254
+ if (isMp3Format(format)) {
255
+ const parts = buffers.map(stripMp3Tags);
256
+ const output = new Uint8Array(parts.reduce((total, part) => total + part.byteLength, 0));
257
+ let offset = 0;
258
+ for (const part of parts) {
259
+ output.set(part, offset);
260
+ offset += part.byteLength;
261
+ }
262
+ return output.buffer;
263
+ }
264
+ if (isRawFormat(format)) {
265
+ const output = new Uint8Array(buffers.reduce((total, buffer) => total + buffer.byteLength, 0));
266
+ let offset = 0;
267
+ for (const buffer of buffers) {
268
+ output.set(new Uint8Array(buffer), offset);
269
+ offset += buffer.byteLength;
270
+ }
271
+ return output.buffer;
272
+ }
273
+ throw new UnsupportedMergeFormatError(format);
274
+ }
124
275
  function closeSpeechResources(speechConfig, synthesizer) {
125
276
  try {
126
277
  synthesizer.close();
@@ -197,6 +348,9 @@ async function synthesizeSsml(ssml, config) {
197
348
  const addSourceMetadata = (event) => ({
198
349
  ...event,
199
350
  ...config.sourceTextRange ? { textRange: { ...config.sourceTextRange } } : {},
351
+ ...config.sourceTextRange ? { originalTextRange: { ...config.sourceTextRange } } : {},
352
+ ...config.chunkIndex !== void 0 ? { chunkIndex: config.chunkIndex } : {},
353
+ ...config.sourceNodePath ? { sourceNodePath: [...config.sourceNodePath] } : {},
200
354
  ...requestId ? { requestId } : {}
201
355
  });
202
356
  const sourceBoundaries = boundaries.map((boundary) => addSourceMetadata(boundary));
@@ -232,60 +386,126 @@ async function synthesizeSsml(ssml, config) {
232
386
  async function synthesizeSsmlChunks(chunks, config) {
233
387
  const results = [];
234
388
  const totalChunks = chunks.length;
389
+ const report = (event) => config.onProgress?.(event);
235
390
  for (const [index, chunk] of chunks.entries()) {
236
391
  const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
237
- const result = await synthesizeSsml(input.ssml, {
238
- ...config,
239
- ...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
240
- onProgress: void 0
392
+ report({
393
+ currentChunk: index,
394
+ totalChunks,
395
+ percent: totalChunks === 0 ? 100 : Math.round(index / totalChunks * 100),
396
+ chunkIndex: index,
397
+ originalTextRange: input.originalTextRange,
398
+ status: "pending",
399
+ durationMs: 0
241
400
  });
242
- results.push(result);
243
- config.onProgress?.({
244
- currentChunk: index + 1,
401
+ }
402
+ for (const [index, chunk] of chunks.entries()) {
403
+ const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
404
+ report({
405
+ currentChunk: index,
245
406
  totalChunks,
246
- percent: totalChunks === 0 ? 100 : Math.round((index + 1) / totalChunks * 100)
407
+ percent: totalChunks === 0 ? 100 : Math.round(index / totalChunks * 100),
408
+ chunkIndex: index,
409
+ originalTextRange: input.originalTextRange,
410
+ status: "synthesizing",
411
+ durationMs: 0
247
412
  });
413
+ const startedAt = Date.now();
414
+ try {
415
+ const result = await synthesizeSsml(input.ssml, {
416
+ ...config,
417
+ ...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
418
+ ...input.sourceNodePath ? { sourceNodePath: input.sourceNodePath } : {},
419
+ chunkIndex: index,
420
+ onProgress: void 0
421
+ });
422
+ results.push(result);
423
+ report({
424
+ currentChunk: index + 1,
425
+ totalChunks,
426
+ percent: totalChunks === 0 ? 100 : Math.round((index + 1) / totalChunks * 100),
427
+ chunkIndex: index,
428
+ originalTextRange: input.originalTextRange,
429
+ status: "success",
430
+ durationMs: Date.now() - startedAt
431
+ });
432
+ } catch (error) {
433
+ report({
434
+ currentChunk: index,
435
+ totalChunks,
436
+ percent: totalChunks === 0 ? 100 : Math.round(index / totalChunks * 100),
437
+ chunkIndex: index,
438
+ originalTextRange: input.originalTextRange,
439
+ status: "failed",
440
+ durationMs: Date.now() - startedAt,
441
+ error
442
+ });
443
+ throw error;
444
+ }
248
445
  }
249
- return mergeSynthesisResults(results);
446
+ return mergeSynthesisResults(results, config.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3");
250
447
  }
251
- function mergeSynthesisResults(results) {
252
- const audioLength = results.reduce((total, result) => total + result.audioData.byteLength, 0);
253
- const audioData = new Uint8Array(audioLength);
448
+ function mergeSynthesisResults(results, format) {
449
+ const audioData = format ? new Uint8Array(
450
+ mergeAudioBuffers(
451
+ results.map((result) => result.audioData),
452
+ format
453
+ )
454
+ ) : new Uint8Array(results.reduce((total, result) => total + result.audioData.byteLength, 0));
455
+ if (!format) {
456
+ let offset = 0;
457
+ for (const result of results) {
458
+ audioData.set(new Uint8Array(result.audioData), offset);
459
+ offset += result.audioData.byteLength;
460
+ }
461
+ }
254
462
  const boundaries = [];
255
463
  const visemes = [];
256
464
  const bookmarks = [];
257
- let byteOffset = 0;
258
465
  let durationOffset = 0;
259
466
  for (const result of results) {
260
- audioData.set(new Uint8Array(result.audioData), byteOffset);
261
- byteOffset += result.audioData.byteLength;
262
467
  const chunkBoundaries = result.boundaries && result.boundaries.length > 0 ? result.boundaries : result.wordBoundary ?? result.wordBoundaries ?? [];
263
468
  for (const boundary of chunkBoundaries) {
264
469
  const textRange = boundary.textRange ?? result.textRange;
470
+ const originalTextRange = boundary.originalTextRange ?? textRange;
265
471
  const requestId = boundary.requestId ?? result.requestId;
266
472
  boundaries.push({
267
473
  ...boundary,
268
474
  audioOffsetMs: boundary.audioOffsetMs + durationOffset,
475
+ chunkAudioOffsetMs: boundary.chunkAudioOffsetMs ?? boundary.audioOffsetMs,
476
+ ...boundary.chunkIndex === void 0 ? { chunkIndex: results.indexOf(result) } : {},
477
+ ...boundary.sourceNodePath ? { sourceNodePath: [...boundary.sourceNodePath] } : {},
478
+ ...originalTextRange ? { originalTextRange: { ...originalTextRange } } : {},
269
479
  ...textRange ? { textRange: { ...textRange } } : {},
270
480
  ...requestId ? { requestId } : {}
271
481
  });
272
482
  }
273
483
  for (const viseme of result.visemes ?? []) {
274
484
  const textRange = viseme.textRange ?? result.textRange;
485
+ const originalTextRange = viseme.originalTextRange ?? textRange;
275
486
  const requestId = viseme.requestId ?? result.requestId;
276
487
  visemes.push({
277
488
  ...viseme,
278
489
  audioOffsetMs: viseme.audioOffsetMs + durationOffset,
490
+ chunkAudioOffsetMs: viseme.chunkAudioOffsetMs ?? viseme.audioOffsetMs,
491
+ ...viseme.chunkIndex === void 0 ? { chunkIndex: results.indexOf(result) } : {},
492
+ ...viseme.sourceNodePath ? { sourceNodePath: [...viseme.sourceNodePath] } : {},
493
+ ...originalTextRange ? { originalTextRange: { ...originalTextRange } } : {},
279
494
  ...textRange ? { textRange: { ...textRange } } : {},
280
495
  ...requestId ? { requestId } : {}
281
496
  });
282
497
  }
283
498
  for (const bookmark of result.bookmarks ?? []) {
284
499
  const textRange = bookmark.textRange ?? result.textRange;
500
+ const originalTextRange = bookmark.originalTextRange ?? textRange;
285
501
  const requestId = bookmark.requestId ?? result.requestId;
286
502
  bookmarks.push({
287
503
  ...bookmark,
288
504
  audioOffsetMs: bookmark.audioOffsetMs + durationOffset,
505
+ chunkAudioOffsetMs: bookmark.chunkAudioOffsetMs ?? bookmark.audioOffsetMs,
506
+ ...bookmark.chunkIndex === void 0 ? { chunkIndex: results.indexOf(result) } : {},
507
+ ...bookmark.sourceNodePath ? { sourceNodePath: [...bookmark.sourceNodePath] } : {},
508
+ ...originalTextRange ? { originalTextRange: { ...originalTextRange } } : {},
289
509
  ...textRange ? { textRange: { ...textRange } } : {},
290
510
  ...requestId ? { requestId } : {}
291
511
  });
@@ -307,6 +527,15 @@ async function synthesizeSpeech(ssml, config) {
307
527
  }
308
528
 
309
529
  // packages/azure-tts-client/src/safe.ts
530
+ var ChunkValidationError = class extends Error {
531
+ constructor(chunkIndex, diagnostics) {
532
+ super(`SSML validation failed for chunk ${chunkIndex}; the Azure Speech API was not called.`);
533
+ this.kind = "chunk-validation";
534
+ this.name = "ChunkValidationError";
535
+ this.chunkIndex = chunkIndex;
536
+ this.diagnostics = diagnostics;
537
+ }
538
+ };
310
539
  async function synthesizeSsmlSafe(client, ssml, options = {}) {
311
540
  const validationOptions = options.validation ?? options;
312
541
  const diagnostics = await Promise.resolve(validateAzureSsml2(ssml, validationOptions));
@@ -330,6 +559,95 @@ async function synthesizeSsmlSafe(client, ssml, options = {}) {
330
559
  return { ok: false, success: false, status: "azure-api-error", error: azureError };
331
560
  }
332
561
  }
562
+ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
563
+ const validationOptions = options.validation ?? options;
564
+ const pending = (index, status, error) => {
565
+ options.onProgress?.({
566
+ currentChunk: status === "success" ? index + 1 : index,
567
+ totalChunks: chunks.length,
568
+ percent: chunks.length === 0 ? 100 : Math.round((status === "success" ? index + 1 : index) / chunks.length * 100),
569
+ chunkIndex: index,
570
+ originalTextRange: typeof chunks[index] === "string" ? void 0 : chunks[index]?.originalTextRange,
571
+ status,
572
+ durationMs: 0,
573
+ ...error ? { error } : {}
574
+ });
575
+ };
576
+ chunks.forEach((_chunk, index) => {
577
+ pending(index, "pending");
578
+ });
579
+ const validations = await Promise.all(
580
+ chunks.map(async (chunk) => {
581
+ const ssml = typeof chunk === "string" ? chunk : chunk.ssml;
582
+ const diagnostics = await Promise.resolve(validateAzureSsml2(ssml, validationOptions));
583
+ return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
584
+ })
585
+ );
586
+ const firstInvalidIndex = validations.findIndex((diagnostics) => diagnostics.length > 0);
587
+ if (firstInvalidIndex >= 0) {
588
+ const error = new ChunkValidationError(firstInvalidIndex, validations[firstInvalidIndex] ?? []);
589
+ pending(firstInvalidIndex, "failed", error);
590
+ return { ok: false, success: false, status: "validation-error", error };
591
+ }
592
+ try {
593
+ if (client.synthesizeChunks) {
594
+ const value = await client.synthesizeChunks(chunks, { onProgress: options.onProgress });
595
+ return { ok: true, success: true, status: "success", value };
596
+ }
597
+ const results = [];
598
+ for (const [index, chunk] of chunks.entries()) {
599
+ const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
600
+ const sourceNodePath = input.sourceNodePath;
601
+ pending(index, "synthesizing");
602
+ const startedAt = Date.now();
603
+ try {
604
+ const result = await client.synthesizeSsml(input.ssml);
605
+ results.push({
606
+ ...result,
607
+ ...input.originalTextRange ? { textRange: { ...input.originalTextRange } } : {},
608
+ ...sourceNodePath ? {
609
+ boundaries: result.boundaries?.map((event) => ({
610
+ ...event,
611
+ sourceNodePath: [...sourceNodePath]
612
+ })),
613
+ visemes: result.visemes?.map((event) => ({ ...event, sourceNodePath: [...sourceNodePath] })),
614
+ bookmarks: result.bookmarks?.map((event) => ({ ...event, sourceNodePath: [...sourceNodePath] }))
615
+ } : {}
616
+ });
617
+ options.onProgress?.({
618
+ currentChunk: index + 1,
619
+ totalChunks: chunks.length,
620
+ percent: chunks.length === 0 ? 100 : Math.round((index + 1) / chunks.length * 100),
621
+ chunkIndex: index,
622
+ originalTextRange: input.originalTextRange,
623
+ status: "success",
624
+ durationMs: Date.now() - startedAt
625
+ });
626
+ } catch (error) {
627
+ options.onProgress?.({
628
+ currentChunk: index,
629
+ totalChunks: chunks.length,
630
+ percent: chunks.length === 0 ? 100 : Math.round(index / chunks.length * 100),
631
+ chunkIndex: index,
632
+ originalTextRange: input.originalTextRange,
633
+ status: "failed",
634
+ durationMs: Date.now() - startedAt,
635
+ error
636
+ });
637
+ throw error;
638
+ }
639
+ }
640
+ return {
641
+ ok: true,
642
+ success: true,
643
+ status: "success",
644
+ value: mergeSynthesisResults(results, options.outputFormat)
645
+ };
646
+ } catch (error) {
647
+ const azureError = error instanceof AzureTtsError ? error : createSpeechSdkError(error);
648
+ return { ok: false, success: false, status: "azure-api-error", error: azureError };
649
+ }
650
+ }
333
651
 
334
652
  // packages/azure-tts-client/src/client.ts
335
653
  var ENDPOINT_TEMPLATE = "https://{region}.tts.speech.microsoft.com/cognitiveservices/v1";
@@ -368,6 +686,16 @@ var AzureTtsClient = class {
368
686
  async synthesizeSsmlSafe(ssml, options = {}) {
369
687
  return synthesizeSsmlSafe(this, ssml, options);
370
688
  }
689
+ async synthesizeChunksSafe(chunks, options = {}) {
690
+ return synthesizeSsmlChunksSafe(this, chunks, {
691
+ ...options,
692
+ outputFormat: options.outputFormat ?? __privateGet(this, _options).outputFormat,
693
+ onProgress: options.onProgress ?? __privateGet(this, _options).onProgress
694
+ });
695
+ }
696
+ async synthesizeSsmlChunksSafe(chunks, options = {}) {
697
+ return this.synthesizeChunksSafe(chunks, options);
698
+ }
371
699
  };
372
700
  _options = new WeakMap();
373
701
 
@@ -423,6 +751,9 @@ async function fetchAzureVoiceCatalog(options) {
423
751
  const secondaryLocales = stringList(record.SecondaryLocaleList);
424
752
  const styles = stringList(record.StyleList);
425
753
  const status = normalizeStatus(record.Status);
754
+ const supportedTags = stringList(record.SupportedTags);
755
+ const unsupportedTags = stringList(record.UnsupportedTags);
756
+ const models = stringList(record.Models);
426
757
  const merged = {
427
758
  name: existing?.name ?? name,
428
759
  locale: existing?.locale ?? locale,
@@ -432,6 +763,12 @@ async function fetchAzureVoiceCatalog(options) {
432
763
  if (mergedSecondaryLocales.length > 0) merged.secondaryLocales = mergedSecondaryLocales;
433
764
  const mergedStyles = [.../* @__PURE__ */ new Set([...existing?.styles ?? [], ...styles])];
434
765
  if (mergedStyles.length > 0) merged.styles = mergedStyles;
766
+ const mergedSupportedTags = [.../* @__PURE__ */ new Set([...existing?.supportedTags ?? [], ...supportedTags])];
767
+ if (mergedSupportedTags.length > 0) merged.supportedTags = mergedSupportedTags;
768
+ const mergedUnsupportedTags = [.../* @__PURE__ */ new Set([...existing?.unsupportedTags ?? [], ...unsupportedTags])];
769
+ if (mergedUnsupportedTags.length > 0) merged.unsupportedTags = mergedUnsupportedTags;
770
+ const mergedModels = [.../* @__PURE__ */ new Set([...existing?.models ?? [], ...models])];
771
+ if (mergedModels.length > 0) merged.models = mergedModels;
435
772
  if (status) merged.status = status;
436
773
  else if (existing?.status) merged.status = existing.status;
437
774
  voices.set(key, merged);
@@ -452,9 +789,13 @@ export {
452
789
  AzureTtsClient,
453
790
  AzureTtsError,
454
791
  AzureTtsSdkError,
792
+ ChunkValidationError,
793
+ UnsupportedMergeFormatError,
455
794
  areAzureLanguagesEquivalent,
456
795
  buildPartialSsml,
457
796
  buildSsml,
797
+ canMergeAudioFormat,
798
+ createAzureUrlValidatorRunner,
458
799
  extractSsmlText,
459
800
  extractSsmlTranslatableText,
460
801
  fetchAzureVoiceCatalog,
@@ -463,13 +804,16 @@ export {
463
804
  getBuiltInVoiceCatalogMetadata,
464
805
  isValidAzureAudioDuration,
465
806
  mapSsmlTextNodes,
807
+ mergeAudioBuffers,
466
808
  mergeSynthesisResults,
467
809
  normalizeAzureLanguage,
468
810
  parseSsml,
811
+ resolveMergeAudioFormat,
469
812
  splitSsmlDocument,
470
813
  synthesizeSpeech,
471
814
  synthesizeSsml,
472
815
  synthesizeSsmlChunks,
816
+ synthesizeSsmlChunksSafe,
473
817
  synthesizeSsmlSafe,
474
818
  validateAzureSsml,
475
819
  validateSsml,