ssml-builder-js 2.15.0 → 2.16.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 +5 -1
- package/dist/{chunk-BDY2Q2JL.mjs → chunk-F2EMU3HM.mjs} +2 -2
- package/dist/{chunk-WXFLUCLR.mjs → chunk-HI74FTKY.mjs} +34 -5
- package/dist/chunk-HI74FTKY.mjs.map +1 -0
- package/dist/{chunk-FXUM45ZY.mjs → chunk-NKLZGITR.mjs} +50 -5
- package/dist/chunk-NKLZGITR.mjs.map +1 -0
- package/dist/core.d.mts +20 -1
- package/dist/core.d.ts +20 -1
- package/dist/core.js +50 -4
- package/dist/core.js.map +1 -1
- package/dist/core.mjs +3 -1
- package/dist/elements.js +32 -4
- package/dist/elements.js.map +1 -1
- package/dist/elements.mjs +2 -2
- package/dist/{index.d-8BvkB9gz.d.mts → index.d-DR5Qz43p.d.mts} +15 -0
- package/dist/{index.d-8BvkB9gz.d.ts → index.d-DR5Qz43p.d.ts} +15 -0
- package/dist/index.d.mts +54 -7
- package/dist/index.d.ts +54 -7
- package/dist/index.js +592 -162
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +513 -156
- package/dist/index.mjs.map +1 -1
- package/dist/react.d.mts +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +32 -4
- package/dist/react.js.map +1 -1
- package/dist/react.mjs +2 -2
- package/package.json +1 -1
- package/dist/chunk-FXUM45ZY.mjs.map +0 -1
- package/dist/chunk-WXFLUCLR.mjs.map +0 -1
- /package/dist/{chunk-BDY2Q2JL.mjs.map → chunk-F2EMU3HM.mjs.map} +0 -0
package/dist/index.js
CHANGED
|
@@ -37,6 +37,7 @@ var __privateSet = (obj, member, value, setter) => (__accessCheck(obj, member, "
|
|
|
37
37
|
// src/index.ts
|
|
38
38
|
var src_exports = {};
|
|
39
39
|
__export(src_exports, {
|
|
40
|
+
AudioFormatMismatchError: () => AudioFormatMismatchError,
|
|
40
41
|
AzureTtsClient: () => AzureTtsClient,
|
|
41
42
|
AzureTtsError: () => AzureTtsError,
|
|
42
43
|
AzureTtsSdkError: () => AzureTtsSdkError,
|
|
@@ -58,6 +59,7 @@ __export(src_exports, {
|
|
|
58
59
|
getAzureVoiceCatalogMetadata: () => getAzureVoiceCatalogMetadata,
|
|
59
60
|
getBuiltInVoiceCatalogMetadata: () => getBuiltInVoiceCatalogMetadata,
|
|
60
61
|
getSsmlSourceMap: () => getSsmlSourceMap,
|
|
62
|
+
inspectAudioSpecification: () => inspectAudioSpecification,
|
|
61
63
|
isValidAzureAudioDuration: () => isValidAzureAudioDuration,
|
|
62
64
|
mapSsmlTextNodes: () => mapSsmlTextNodes,
|
|
63
65
|
mergeAudioBuffers: () => mergeAudioBuffers,
|
|
@@ -73,6 +75,7 @@ __export(src_exports, {
|
|
|
73
75
|
synthesizeSsmlChunksSafe: () => synthesizeSsmlChunksSafe,
|
|
74
76
|
synthesizeSsmlSafe: () => synthesizeSsmlSafe,
|
|
75
77
|
validateAzureSsml: () => validateAzureSsml,
|
|
78
|
+
validateAzureSsmlChunks: () => validateAzureSsmlChunks,
|
|
76
79
|
validateSsml: () => validateSsml,
|
|
77
80
|
validateSsmlStructureIntegrity: () => validateSsmlStructureIntegrity
|
|
78
81
|
});
|
|
@@ -2089,6 +2092,7 @@ function tokenizeElements(source) {
|
|
|
2089
2092
|
if (parent) parent.childElementCount += 1;
|
|
2090
2093
|
const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
|
|
2091
2094
|
const tokenName = nameMatch[1];
|
|
2095
|
+
const path = parent ? [...parent.path, `${tokenName}[${childElementIndex ?? 0}]`] : [tokenName];
|
|
2092
2096
|
const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
|
|
2093
2097
|
tokens.push({
|
|
2094
2098
|
attributes,
|
|
@@ -2099,12 +2103,14 @@ function tokenizeElements(source) {
|
|
|
2099
2103
|
parentName: parent?.name,
|
|
2100
2104
|
parentVoiceName,
|
|
2101
2105
|
selfClosing,
|
|
2102
|
-
start
|
|
2106
|
+
start,
|
|
2107
|
+
path
|
|
2103
2108
|
});
|
|
2104
2109
|
if (!selfClosing) {
|
|
2105
2110
|
openElements.push({
|
|
2106
2111
|
childElementCount: 0,
|
|
2107
2112
|
name: tokenName,
|
|
2113
|
+
path,
|
|
2108
2114
|
voiceName: tokenVoiceName
|
|
2109
2115
|
});
|
|
2110
2116
|
}
|
|
@@ -2117,13 +2123,14 @@ function location(source, offset) {
|
|
|
2117
2123
|
const line = before.split("\n").length;
|
|
2118
2124
|
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
2119
2125
|
}
|
|
2120
|
-
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code) {
|
|
2126
|
+
function addDiagnostic(diagnostics, source, offset, message, severity = "error", code, metadata = {}) {
|
|
2121
2127
|
diagnostics.push({
|
|
2122
2128
|
...location(source, offset),
|
|
2123
2129
|
message,
|
|
2124
2130
|
severity,
|
|
2125
2131
|
source: "ssml-static-validator",
|
|
2126
|
-
...code ? { code } : {}
|
|
2132
|
+
...code ? { code } : {},
|
|
2133
|
+
...metadata
|
|
2127
2134
|
});
|
|
2128
2135
|
}
|
|
2129
2136
|
function isSupportedProsodyRate(value) {
|
|
@@ -2596,9 +2603,11 @@ function validateAzureSsmlStatic(ssml, options = {}) {
|
|
|
2596
2603
|
for (const token of tokens) {
|
|
2597
2604
|
const tokenName = token.name.toLowerCase();
|
|
2598
2605
|
const tokenVoiceName = tokenName === "voice" ? attr(token, "name")?.trim() : tokenName === "mstts:turn" ? attr(token, "voice")?.trim() || token.parentVoiceName : options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
2606
|
+
const tokenDiagnosticStart = diagnostics.length;
|
|
2599
2607
|
validateElement(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
2600
2608
|
const definition = tokenVoiceName ? voiceCatalog.get(tokenVoiceName.toLowerCase()) : void 0;
|
|
2601
2609
|
validateVoiceFeatureMatrix(token, ssml, diagnostics, tokenVoiceName, definition);
|
|
2610
|
+
annotateTokenDiagnostics(diagnostics, tokenDiagnosticStart, token, options, tokenVoiceName);
|
|
2602
2611
|
if (tokenName === "voice" && options.model && definition?.models && !definition.models.some((model) => model.toLowerCase() === options.model?.toLowerCase())) {
|
|
2603
2612
|
addDiagnostic(
|
|
2604
2613
|
diagnostics,
|
|
@@ -2620,12 +2629,30 @@ function urlAttributes(token) {
|
|
|
2620
2629
|
return value === void 0 ? [] : [{ attribute, value }];
|
|
2621
2630
|
});
|
|
2622
2631
|
}
|
|
2632
|
+
function annotateTokenDiagnostics(diagnostics, startIndex, token, options, voiceName) {
|
|
2633
|
+
const attributes = [...token.attributes.keys()];
|
|
2634
|
+
for (const diagnostic of diagnostics.slice(startIndex)) {
|
|
2635
|
+
const attributeName = attributes.find(
|
|
2636
|
+
(attribute) => new RegExp(`(?:<[^> ]+\\s+|")${attribute}(?:"|>|\\s)`, "i").test(diagnostic.message)
|
|
2637
|
+
);
|
|
2638
|
+
const nodePath = options.sourceNodePath ? [...options.sourceNodePath] : [...token.path];
|
|
2639
|
+
Object.assign(diagnostic, {
|
|
2640
|
+
range: { start: token.start, end: token.end + 1 },
|
|
2641
|
+
tagName: token.name,
|
|
2642
|
+
...attributeName ? { attributeName } : {},
|
|
2643
|
+
...voiceName ? { voiceName } : {},
|
|
2644
|
+
...options.chunkIndex !== void 0 ? { chunkIndex: options.chunkIndex } : {},
|
|
2645
|
+
nodePath,
|
|
2646
|
+
targetNodePath: [...token.path]
|
|
2647
|
+
});
|
|
2648
|
+
}
|
|
2649
|
+
}
|
|
2623
2650
|
function validateAzureSsml(ssml, options = {}) {
|
|
2624
2651
|
const diagnostics = validateAzureSsmlStatic(ssml, options);
|
|
2625
2652
|
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
2626
2653
|
if (!validator || typeof ssml !== "string") return diagnostics;
|
|
2627
2654
|
const runnerOptions = options.urlValidation ?? {};
|
|
2628
|
-
const boundedValidator = createAzureUrlValidatorRunner(validator, {
|
|
2655
|
+
const boundedValidator = options.urlValidatorRunner ?? createAzureUrlValidatorRunner(validator, {
|
|
2629
2656
|
...runnerOptions,
|
|
2630
2657
|
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
2631
2658
|
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
@@ -2650,26 +2677,46 @@ function validateAzureSsml(ssml, options = {}) {
|
|
|
2650
2677
|
const valid = typeof result === "boolean" ? result : result.valid;
|
|
2651
2678
|
if (!valid) {
|
|
2652
2679
|
const reason = typeof result === "boolean" ? void 0 : result.reason;
|
|
2680
|
+
const diagnosticStart = diagnostics.length;
|
|
2653
2681
|
addDiagnostic(
|
|
2654
2682
|
diagnostics,
|
|
2655
2683
|
ssml,
|
|
2656
2684
|
token.start,
|
|
2657
2685
|
`<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
|
|
2658
2686
|
);
|
|
2687
|
+
annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
2659
2688
|
}
|
|
2660
2689
|
} catch (error) {
|
|
2661
2690
|
const reason = error instanceof Error ? error.message : String(error);
|
|
2691
|
+
const diagnosticStart = diagnostics.length;
|
|
2662
2692
|
addDiagnostic(
|
|
2663
2693
|
diagnostics,
|
|
2664
2694
|
ssml,
|
|
2665
2695
|
token.start,
|
|
2666
2696
|
`<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
|
|
2667
2697
|
);
|
|
2698
|
+
annotateTokenDiagnostics(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
2668
2699
|
}
|
|
2669
2700
|
})
|
|
2670
2701
|
);
|
|
2671
2702
|
return Promise.all(checks).then(() => diagnostics);
|
|
2672
2703
|
}
|
|
2704
|
+
async function validateAzureSsmlChunks(chunks, options = {}) {
|
|
2705
|
+
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
2706
|
+
const sharedOptions = validator ? {
|
|
2707
|
+
...options,
|
|
2708
|
+
urlValidatorRunner: options.urlValidatorRunner ?? createAzureUrlValidatorRunner(validator, {
|
|
2709
|
+
...options.urlValidation ?? {},
|
|
2710
|
+
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
2711
|
+
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
2712
|
+
...options.urlValidatorSignal ? { signal: options.urlValidatorSignal } : {},
|
|
2713
|
+
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
2714
|
+
})
|
|
2715
|
+
} : options;
|
|
2716
|
+
return Promise.all(
|
|
2717
|
+
chunks.map((chunk, chunkIndex) => Promise.resolve(validateAzureSsml(chunk, { ...sharedOptions, chunkIndex })))
|
|
2718
|
+
);
|
|
2719
|
+
}
|
|
2673
2720
|
|
|
2674
2721
|
// packages/ssml-core/src/generated/azureVoiceCatalog.ts
|
|
2675
2722
|
var AZURE_VOICE_CATALOG_METADATA = {
|
|
@@ -2730,6 +2777,14 @@ var MergeError = class extends Error {
|
|
|
2730
2777
|
this.cause = cause;
|
|
2731
2778
|
}
|
|
2732
2779
|
};
|
|
2780
|
+
var AudioFormatMismatchError = class extends Error {
|
|
2781
|
+
constructor(message, inputSpecs = []) {
|
|
2782
|
+
super(message);
|
|
2783
|
+
this.kind = "audio-format-mismatch";
|
|
2784
|
+
this.name = "AudioFormatMismatchError";
|
|
2785
|
+
this.inputSpecs = inputSpecs;
|
|
2786
|
+
}
|
|
2787
|
+
};
|
|
2733
2788
|
var UnsupportedMergeFormatError = class extends Error {
|
|
2734
2789
|
constructor(format) {
|
|
2735
2790
|
super(`Audio format "${format}" cannot be safely concatenated; container re-multiplexing is required.`);
|
|
@@ -2739,7 +2794,7 @@ var UnsupportedMergeFormatError = class extends Error {
|
|
|
2739
2794
|
}
|
|
2740
2795
|
};
|
|
2741
2796
|
function toSynthesisError(error) {
|
|
2742
|
-
if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError)
|
|
2797
|
+
if (error instanceof AzureTtsError || error instanceof MergeError || error instanceof AudioFormatMismatchError || error instanceof UnsupportedMergeFormatError || error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError)
|
|
2743
2798
|
return error;
|
|
2744
2799
|
const message = error instanceof Error ? error.message : String(error);
|
|
2745
2800
|
if (/cancel|abort/i.test(message)) return new SynthesisCancelledError(message);
|
|
@@ -3840,6 +3895,7 @@ function tokenizeElements2(source) {
|
|
|
3840
3895
|
if (parent) parent.childElementCount += 1;
|
|
3841
3896
|
const parentVoiceName = [...openElements].reverse().find((element) => element.voiceName)?.voiceName;
|
|
3842
3897
|
const tokenName = nameMatch[1];
|
|
3898
|
+
const path = parent ? [...parent.path, `${tokenName}[${childElementIndex ?? 0}]`] : [tokenName];
|
|
3843
3899
|
const tokenVoiceName = tokenName.toLowerCase() === "voice" ? attributes.get("name") : tokenName.toLowerCase() === "mstts:turn" ? attributes.get("voice") ?? parentVoiceName : parentVoiceName;
|
|
3844
3900
|
tokens.push({
|
|
3845
3901
|
attributes,
|
|
@@ -3850,12 +3906,14 @@ function tokenizeElements2(source) {
|
|
|
3850
3906
|
parentName: parent?.name,
|
|
3851
3907
|
parentVoiceName,
|
|
3852
3908
|
selfClosing,
|
|
3853
|
-
start
|
|
3909
|
+
start,
|
|
3910
|
+
path
|
|
3854
3911
|
});
|
|
3855
3912
|
if (!selfClosing) {
|
|
3856
3913
|
openElements.push({
|
|
3857
3914
|
childElementCount: 0,
|
|
3858
3915
|
name: tokenName,
|
|
3916
|
+
path,
|
|
3859
3917
|
voiceName: tokenVoiceName
|
|
3860
3918
|
});
|
|
3861
3919
|
}
|
|
@@ -3868,13 +3926,14 @@ function location2(source, offset) {
|
|
|
3868
3926
|
const line = before.split("\n").length;
|
|
3869
3927
|
return { line, column: before.length - (before.lastIndexOf("\n") + 1) + 1 };
|
|
3870
3928
|
}
|
|
3871
|
-
function addDiagnostic2(diagnostics, source, offset, message, severity = "error", code) {
|
|
3929
|
+
function addDiagnostic2(diagnostics, source, offset, message, severity = "error", code, metadata = {}) {
|
|
3872
3930
|
diagnostics.push({
|
|
3873
3931
|
...location2(source, offset),
|
|
3874
3932
|
message,
|
|
3875
3933
|
severity,
|
|
3876
3934
|
source: "ssml-static-validator",
|
|
3877
|
-
...code ? { code } : {}
|
|
3935
|
+
...code ? { code } : {},
|
|
3936
|
+
...metadata
|
|
3878
3937
|
});
|
|
3879
3938
|
}
|
|
3880
3939
|
function isSupportedProsodyRate2(value) {
|
|
@@ -4337,9 +4396,11 @@ function validateAzureSsmlStatic2(ssml, options = {}) {
|
|
|
4337
4396
|
for (const token of tokens) {
|
|
4338
4397
|
const tokenName = token.name.toLowerCase();
|
|
4339
4398
|
const tokenVoiceName = tokenName === "voice" ? attr2(token, "name")?.trim() : tokenName === "mstts:turn" ? attr2(token, "voice")?.trim() || token.parentVoiceName : options.validateNestedVoices === false ? voiceName : token.parentVoiceName;
|
|
4399
|
+
const tokenDiagnosticStart = diagnostics.length;
|
|
4340
4400
|
validateElement2(token, ssml, diagnostics, tokenVoiceName, options, voiceCatalog);
|
|
4341
4401
|
const definition = tokenVoiceName ? voiceCatalog.get(tokenVoiceName.toLowerCase()) : void 0;
|
|
4342
4402
|
validateVoiceFeatureMatrix2(token, ssml, diagnostics, tokenVoiceName, definition);
|
|
4403
|
+
annotateTokenDiagnostics2(diagnostics, tokenDiagnosticStart, token, options, tokenVoiceName);
|
|
4343
4404
|
if (tokenName === "voice" && options.model && definition?.models && !definition.models.some((model) => model.toLowerCase() === options.model?.toLowerCase())) {
|
|
4344
4405
|
addDiagnostic2(
|
|
4345
4406
|
diagnostics,
|
|
@@ -4361,12 +4422,30 @@ function urlAttributes2(token) {
|
|
|
4361
4422
|
return value === void 0 ? [] : [{ attribute, value }];
|
|
4362
4423
|
});
|
|
4363
4424
|
}
|
|
4425
|
+
function annotateTokenDiagnostics2(diagnostics, startIndex, token, options, voiceName) {
|
|
4426
|
+
const attributes = [...token.attributes.keys()];
|
|
4427
|
+
for (const diagnostic of diagnostics.slice(startIndex)) {
|
|
4428
|
+
const attributeName = attributes.find(
|
|
4429
|
+
(attribute) => new RegExp(`(?:<[^> ]+\\s+|")${attribute}(?:"|>|\\s)`, "i").test(diagnostic.message)
|
|
4430
|
+
);
|
|
4431
|
+
const nodePath = options.sourceNodePath ? [...options.sourceNodePath] : [...token.path];
|
|
4432
|
+
Object.assign(diagnostic, {
|
|
4433
|
+
range: { start: token.start, end: token.end + 1 },
|
|
4434
|
+
tagName: token.name,
|
|
4435
|
+
...attributeName ? { attributeName } : {},
|
|
4436
|
+
...voiceName ? { voiceName } : {},
|
|
4437
|
+
...options.chunkIndex !== void 0 ? { chunkIndex: options.chunkIndex } : {},
|
|
4438
|
+
nodePath,
|
|
4439
|
+
targetNodePath: [...token.path]
|
|
4440
|
+
});
|
|
4441
|
+
}
|
|
4442
|
+
}
|
|
4364
4443
|
function validateAzureSsml2(ssml, options = {}) {
|
|
4365
4444
|
const diagnostics = validateAzureSsmlStatic2(ssml, options);
|
|
4366
4445
|
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
4367
4446
|
if (!validator || typeof ssml !== "string") return diagnostics;
|
|
4368
4447
|
const runnerOptions = options.urlValidation ?? {};
|
|
4369
|
-
const boundedValidator = createAzureUrlValidatorRunner2(validator, {
|
|
4448
|
+
const boundedValidator = options.urlValidatorRunner ?? createAzureUrlValidatorRunner2(validator, {
|
|
4370
4449
|
...runnerOptions,
|
|
4371
4450
|
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
4372
4451
|
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
@@ -4391,21 +4470,25 @@ function validateAzureSsml2(ssml, options = {}) {
|
|
|
4391
4470
|
const valid = typeof result === "boolean" ? result : result.valid;
|
|
4392
4471
|
if (!valid) {
|
|
4393
4472
|
const reason = typeof result === "boolean" ? void 0 : result.reason;
|
|
4473
|
+
const diagnosticStart = diagnostics.length;
|
|
4394
4474
|
addDiagnostic2(
|
|
4395
4475
|
diagnostics,
|
|
4396
4476
|
ssml,
|
|
4397
4477
|
token.start,
|
|
4398
4478
|
`<${token.name} ${attribute}> was rejected by the custom URL validator${reason ? `: ${reason}` : "."}`
|
|
4399
4479
|
);
|
|
4480
|
+
annotateTokenDiagnostics2(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
4400
4481
|
}
|
|
4401
4482
|
} catch (error) {
|
|
4402
4483
|
const reason = error instanceof Error ? error.message : String(error);
|
|
4484
|
+
const diagnosticStart = diagnostics.length;
|
|
4403
4485
|
addDiagnostic2(
|
|
4404
4486
|
diagnostics,
|
|
4405
4487
|
ssml,
|
|
4406
4488
|
token.start,
|
|
4407
4489
|
`<${token.name} ${attribute}> could not be validated by the custom URL validator: ${reason}`
|
|
4408
4490
|
);
|
|
4491
|
+
annotateTokenDiagnostics2(diagnostics, diagnosticStart, token, options, token.parentVoiceName);
|
|
4409
4492
|
}
|
|
4410
4493
|
})
|
|
4411
4494
|
);
|
|
@@ -4532,6 +4615,105 @@ function parseWav(buffer) {
|
|
|
4532
4615
|
}
|
|
4533
4616
|
return { chunks, data, format };
|
|
4534
4617
|
}
|
|
4618
|
+
function formatNumber(format, pattern, fallback) {
|
|
4619
|
+
const match = pattern.exec(format);
|
|
4620
|
+
return match?.[1] ? Number(match[1]) : fallback;
|
|
4621
|
+
}
|
|
4622
|
+
function formatChannels(format, fallback) {
|
|
4623
|
+
if (/stereo|2ch|dual/i.test(format)) return 2;
|
|
4624
|
+
if (/mono|1ch/i.test(format)) return 1;
|
|
4625
|
+
return fallback;
|
|
4626
|
+
}
|
|
4627
|
+
function formatAudioSpecification(format) {
|
|
4628
|
+
const sampleRate = formatNumber(format, /(\d+)(?:khz|kHz|hz|Hz)/, 0);
|
|
4629
|
+
const channels = formatChannels(format, 0);
|
|
4630
|
+
const bitrateMatch = /(\d+)(?:kbitrate|kbps|kbits?)/i.exec(format);
|
|
4631
|
+
const bitrate = bitrateMatch?.[1] ? Number(bitrateMatch[1]) * 1e3 : void 0;
|
|
4632
|
+
const codec = /mp3|mpeg/i.test(format) ? "mp3" : /opus/i.test(format) ? "opus" : /silk/i.test(format) ? "silk" : /pcm|mulaw|alaw|siren/i.test(format) ? "pcm" : "unknown";
|
|
4633
|
+
return {
|
|
4634
|
+
format,
|
|
4635
|
+
mimeType: resolveMimeType(format),
|
|
4636
|
+
codec,
|
|
4637
|
+
sampleRate,
|
|
4638
|
+
channels,
|
|
4639
|
+
...bitrate ? { bitrate } : {},
|
|
4640
|
+
isCompressed: codec === "mp3" || codec === "opus" || codec === "silk"
|
|
4641
|
+
};
|
|
4642
|
+
}
|
|
4643
|
+
function parseMp3Specification(buffer, format) {
|
|
4644
|
+
const bytes = stripMp3Tags(buffer);
|
|
4645
|
+
const bitrates = [
|
|
4646
|
+
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
|
|
4647
|
+
[0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],
|
|
4648
|
+
[0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0]
|
|
4649
|
+
];
|
|
4650
|
+
const sampleRates = [
|
|
4651
|
+
[44100, 48e3, 32e3],
|
|
4652
|
+
[22050, 24e3, 16e3],
|
|
4653
|
+
[11025, 12e3, 8e3]
|
|
4654
|
+
];
|
|
4655
|
+
for (let index = 0; index + 4 <= bytes.length; index += 1) {
|
|
4656
|
+
if (bytes[index] !== 255 || (bytes[index + 1] ?? 0) < 224) continue;
|
|
4657
|
+
const header = bytes[index + 1] ?? 0;
|
|
4658
|
+
const versionBits = header >> 3 & 3;
|
|
4659
|
+
const layer = header >> 1 & 3;
|
|
4660
|
+
const bitrateIndex = (bytes[index + 2] ?? 0) >> 4;
|
|
4661
|
+
const sampleIndex = (bytes[index + 2] ?? 0) >> 2 & 3;
|
|
4662
|
+
if (versionBits === 1 || layer !== 1 || bitrateIndex === 0 || bitrateIndex === 15 || sampleIndex === 3) continue;
|
|
4663
|
+
const versionIndex = versionBits === 3 ? 0 : versionBits === 2 ? 1 : 2;
|
|
4664
|
+
const bitrateTable = versionBits === 3 ? bitrates[1] : bitrates[2];
|
|
4665
|
+
const sampleRate = sampleRates[versionIndex]?.[sampleIndex] ?? 0;
|
|
4666
|
+
const bitrateKbps = bitrateTable?.[bitrateIndex] ?? 0;
|
|
4667
|
+
if (!sampleRate || !bitrateKbps) continue;
|
|
4668
|
+
return {
|
|
4669
|
+
format,
|
|
4670
|
+
mimeType: "audio/mpeg",
|
|
4671
|
+
codec: "mp3",
|
|
4672
|
+
sampleRate,
|
|
4673
|
+
channels: (bytes[index + 3] ?? 0) >> 6 === 3 ? 1 : 2,
|
|
4674
|
+
bitrate: bitrateKbps * 1e3,
|
|
4675
|
+
isCompressed: true
|
|
4676
|
+
};
|
|
4677
|
+
}
|
|
4678
|
+
return void 0;
|
|
4679
|
+
}
|
|
4680
|
+
function inspectAudioSpecification(buffer, format) {
|
|
4681
|
+
if (isWavFormat(format) || ascii(new Uint8Array(buffer), 0, "RIFF")) {
|
|
4682
|
+
const parsed = parseWav(buffer);
|
|
4683
|
+
if (parsed.format.byteLength < 16) throw new Error("Invalid WAV fmt chunk.");
|
|
4684
|
+
const view = new DataView(parsed.format.buffer, parsed.format.byteOffset, parsed.format.byteLength);
|
|
4685
|
+
const sampleRate = view.getUint32(4, true);
|
|
4686
|
+
const channels = view.getUint16(2, true);
|
|
4687
|
+
const bitsPerSample = parsed.format.byteLength >= 16 ? view.getUint16(14, true) : 0;
|
|
4688
|
+
const formatCode = view.getUint16(0, true);
|
|
4689
|
+
return {
|
|
4690
|
+
format,
|
|
4691
|
+
mimeType: "audio/wav",
|
|
4692
|
+
codec: formatCode === 1 ? "pcm" : "unknown",
|
|
4693
|
+
sampleRate,
|
|
4694
|
+
channels,
|
|
4695
|
+
...sampleRate && channels && bitsPerSample ? { bitrate: sampleRate * channels * bitsPerSample } : {},
|
|
4696
|
+
isCompressed: formatCode !== 1
|
|
4697
|
+
};
|
|
4698
|
+
}
|
|
4699
|
+
if (isMp3Format(format)) return parseMp3Specification(buffer, format) ?? formatAudioSpecification(format);
|
|
4700
|
+
return formatAudioSpecification(format);
|
|
4701
|
+
}
|
|
4702
|
+
function validateAudioSpecifications(specs) {
|
|
4703
|
+
const first = specs[0];
|
|
4704
|
+
if (!first) return;
|
|
4705
|
+
const mismatch = specs.find(
|
|
4706
|
+
(spec) => spec.sampleRate !== first.sampleRate || spec.channels !== first.channels || first.bitrate !== void 0 && spec.bitrate !== void 0 && spec.bitrate !== first.bitrate
|
|
4707
|
+
);
|
|
4708
|
+
if (mismatch)
|
|
4709
|
+
throw new AudioFormatMismatchError(
|
|
4710
|
+
`Audio chunks have incompatible specifications: ${first.sampleRate}Hz/${first.channels}ch versus ${mismatch.sampleRate}Hz/${mismatch.channels}ch.`,
|
|
4711
|
+
specs
|
|
4712
|
+
);
|
|
4713
|
+
}
|
|
4714
|
+
function isAudioFormatMismatch(error) {
|
|
4715
|
+
return error instanceof AudioFormatMismatchError || error !== null && typeof error === "object" && "kind" in error && error.kind === "audio-format-mismatch";
|
|
4716
|
+
}
|
|
4535
4717
|
function writeUint32(target, offset, value) {
|
|
4536
4718
|
new DataView(target.buffer).setUint32(offset, value, true);
|
|
4537
4719
|
}
|
|
@@ -4617,6 +4799,7 @@ function mergeAudioBuffers(buffers, options) {
|
|
|
4617
4799
|
const format = typeof options === "string" ? options : options?.format;
|
|
4618
4800
|
if (!format) throw new UnsupportedMergeFormatError("");
|
|
4619
4801
|
try {
|
|
4802
|
+
validateAudioSpecifications(buffers.map((buffer) => inspectAudioSpecification(buffer, format)));
|
|
4620
4803
|
if (isWavFormat(format)) return mergeWavBuffers(buffers);
|
|
4621
4804
|
if (isMp3Format(format)) {
|
|
4622
4805
|
const parts = buffers.map(stripMp3Tags);
|
|
@@ -4639,7 +4822,8 @@ function mergeAudioBuffers(buffers, options) {
|
|
|
4639
4822
|
}
|
|
4640
4823
|
throw new UnsupportedMergeFormatError(format);
|
|
4641
4824
|
} catch (error) {
|
|
4642
|
-
if (error instanceof UnsupportedMergeFormatError || error instanceof MergeError)
|
|
4825
|
+
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
4826
|
+
throw error;
|
|
4643
4827
|
throw new MergeError(`Audio buffers could not be merged for format "${format}".`, error);
|
|
4644
4828
|
}
|
|
4645
4829
|
}
|
|
@@ -4717,17 +4901,26 @@ async function synthesizeSsml(ssml, config) {
|
|
|
4717
4901
|
return {
|
|
4718
4902
|
originalTextRange: { ...marker.originalTextRange },
|
|
4719
4903
|
sourceNodePath: [...marker.sourceNodePath],
|
|
4720
|
-
textRange: { ...marker.originalTextRange }
|
|
4904
|
+
textRange: { ...marker.originalTextRange },
|
|
4905
|
+
mappingStatus: "exact"
|
|
4721
4906
|
};
|
|
4722
4907
|
}
|
|
4723
|
-
if (sourceSegments.length === 0 && !config.sourceTextRange && !config.sourceNodePath)
|
|
4908
|
+
if (sourceSegments.length === 0 && !config.sourceTextRange && !config.sourceNodePath) {
|
|
4909
|
+
const unmapped = { mappingStatus: "unmapped" };
|
|
4910
|
+
Object.defineProperty(unmapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
4911
|
+
return unmapped;
|
|
4912
|
+
}
|
|
4724
4913
|
const value = text ?? "";
|
|
4725
4914
|
let localStart = Number.isFinite(offsetHint) && (offsetHint ?? 0) >= 0 ? offsetHint : -1;
|
|
4726
|
-
|
|
4915
|
+
let mappingStatus = "exact";
|
|
4916
|
+
if (value && localStart >= 0 && sourceText.slice(localStart, localStart + value.length) !== value) {
|
|
4727
4917
|
localStart = -1;
|
|
4918
|
+
mappingStatus = "fallback";
|
|
4919
|
+
}
|
|
4728
4920
|
if (localStart < 0 || localStart > sourceText.length) {
|
|
4729
4921
|
localStart = value ? sourceText.indexOf(value, sourceEventCursor) : sourceEventCursor;
|
|
4730
4922
|
if (localStart < 0) localStart = value ? sourceText.indexOf(value) : sourceEventCursor;
|
|
4923
|
+
mappingStatus = "fallback";
|
|
4731
4924
|
}
|
|
4732
4925
|
localStart = Math.max(0, localStart);
|
|
4733
4926
|
const localEnd = Math.min(sourceText.length, localStart + value.length);
|
|
@@ -4738,7 +4931,8 @@ async function synthesizeSsml(ssml, config) {
|
|
|
4738
4931
|
return {
|
|
4739
4932
|
originalTextRange: { ...fallbackRange },
|
|
4740
4933
|
textRange: { ...fallbackRange },
|
|
4741
|
-
...segment ? { sourceNodePath: [...segment.sourceNodePath] } : config.sourceNodePath ? { sourceNodePath: [...config.sourceNodePath] } : {}
|
|
4934
|
+
...segment ? { sourceNodePath: [...segment.sourceNodePath] } : config.sourceNodePath ? { sourceNodePath: [...config.sourceNodePath] } : {},
|
|
4935
|
+
mappingStatus: segment || config.sourceTextRange || config.sourceNodePath ? mappingStatus : "unmapped"
|
|
4742
4936
|
};
|
|
4743
4937
|
};
|
|
4744
4938
|
synthesizer.wordBoundary = (_sender, event) => {
|
|
@@ -4787,20 +4981,27 @@ async function synthesizeSsml(ssml, config) {
|
|
|
4787
4981
|
);
|
|
4788
4982
|
const durationMs = result.audioDuration ? ticksToMilliseconds(result.audioDuration) : eventDurationMs;
|
|
4789
4983
|
const requestId = result.resultId;
|
|
4790
|
-
const addSourceMetadata = (event) =>
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4984
|
+
const addSourceMetadata = (event) => {
|
|
4985
|
+
const mapped = {
|
|
4986
|
+
...event,
|
|
4987
|
+
...config.sourceTextRange && !("textRange" in event) ? { textRange: { ...config.sourceTextRange } } : {},
|
|
4988
|
+
...config.sourceTextRange && !("originalTextRange" in event) ? { originalTextRange: { ...config.sourceTextRange } } : {},
|
|
4989
|
+
...config.chunkIndex !== void 0 ? { chunkIndex: config.chunkIndex } : {},
|
|
4990
|
+
...config.sourceNodePath ? { sourceNodePath: [...config.sourceNodePath] } : {},
|
|
4991
|
+
...requestId ? { requestId } : {}
|
|
4992
|
+
};
|
|
4993
|
+
if (event.mappingStatus === "unmapped")
|
|
4994
|
+
Object.defineProperty(mapped, "mappingStatus", { value: "unmapped", enumerable: false });
|
|
4995
|
+
return mapped;
|
|
4996
|
+
};
|
|
4798
4997
|
const sourceBoundaries = boundaries.map((boundary) => addSourceMetadata(boundary));
|
|
4799
4998
|
const sourceVisemes = visemes.map((viseme) => addSourceMetadata(viseme));
|
|
4800
4999
|
const sourceBookmarks = bookmarks.map((bookmark) => addSourceMetadata(bookmark));
|
|
4801
5000
|
resolve({
|
|
4802
5001
|
audioData: result.audioData,
|
|
4803
5002
|
durationMs,
|
|
5003
|
+
audioSpec: inspectAudioSpecification(result.audioData, config.outputFormat ?? DEFAULT_OUTPUT_FORMAT),
|
|
5004
|
+
mimeType: resolveMimeType(config.outputFormat ?? DEFAULT_OUTPUT_FORMAT),
|
|
4804
5005
|
...config.sourceTextRange ? { textRange: { ...config.sourceTextRange } } : {},
|
|
4805
5006
|
...requestId ? { requestId } : {},
|
|
4806
5007
|
...sourceBoundaries.length > 0 ? { boundaries: sourceBoundaries, wordBoundary: sourceBoundaries, wordBoundaries: sourceBoundaries } : {},
|
|
@@ -4825,8 +5026,66 @@ async function synthesizeSsml(ssml, config) {
|
|
|
4825
5026
|
}
|
|
4826
5027
|
});
|
|
4827
5028
|
}
|
|
5029
|
+
function isRetryableSynthesisError(error) {
|
|
5030
|
+
if (error instanceof SynthesisCancelledError || error instanceof SynthesisTimeoutError) return false;
|
|
5031
|
+
if (error instanceof AzureTtsError && error.status !== 0)
|
|
5032
|
+
return error.status === 429 || error.status >= 500 && error.status < 600;
|
|
5033
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5034
|
+
if (/\b4\d{2}\b/.test(message)) return false;
|
|
5035
|
+
const status = error && typeof error === "object" && "status" in error ? error.status : void 0;
|
|
5036
|
+
if (typeof status === "number") return status === 429 || status >= 500 && status < 600;
|
|
5037
|
+
return /network|connection|connect|socket|fetch failed|econn|etimedout|temporar|transient|unavailable/i.test(message);
|
|
5038
|
+
}
|
|
5039
|
+
function retryDelay(options, retryAttempt) {
|
|
5040
|
+
const base = Math.min(options.maxDelayMs, options.initialDelayMs * 2 ** Math.max(0, retryAttempt - 1));
|
|
5041
|
+
return Math.floor(Math.random() * (base + 1));
|
|
5042
|
+
}
|
|
5043
|
+
function resolveConcurrency(value, total) {
|
|
5044
|
+
if (value === void 0) return 1;
|
|
5045
|
+
if (value === Infinity) return Math.max(1, total);
|
|
5046
|
+
return Number.isFinite(value) ? Math.max(1, Math.floor(value)) : 1;
|
|
5047
|
+
}
|
|
5048
|
+
async function waitForRetry(delayMs, signal) {
|
|
5049
|
+
if (signal?.aborted) throw new SynthesisCancelledError();
|
|
5050
|
+
if (delayMs <= 0) return;
|
|
5051
|
+
await new Promise((resolve, reject) => {
|
|
5052
|
+
let timer;
|
|
5053
|
+
const abort = () => {
|
|
5054
|
+
clearTimeout(timer);
|
|
5055
|
+
signal?.removeEventListener("abort", abort);
|
|
5056
|
+
reject(new SynthesisCancelledError());
|
|
5057
|
+
};
|
|
5058
|
+
timer = setTimeout(() => {
|
|
5059
|
+
signal?.removeEventListener("abort", abort);
|
|
5060
|
+
resolve();
|
|
5061
|
+
}, delayMs);
|
|
5062
|
+
if (signal) {
|
|
5063
|
+
signal.addEventListener("abort", abort, { once: true });
|
|
5064
|
+
}
|
|
5065
|
+
});
|
|
5066
|
+
}
|
|
5067
|
+
async function synthesizeWithRetry(ssml, config, retryOptions, onRetry) {
|
|
5068
|
+
const options = retryOptions ? {
|
|
5069
|
+
maxRetries: Math.max(0, Math.floor(retryOptions.maxRetries)),
|
|
5070
|
+
initialDelayMs: Math.max(0, retryOptions.initialDelayMs),
|
|
5071
|
+
maxDelayMs: Math.max(0, retryOptions.maxDelayMs)
|
|
5072
|
+
} : void 0;
|
|
5073
|
+
let attempt = 0;
|
|
5074
|
+
while (true) {
|
|
5075
|
+
if (config.signal?.aborted) throw new SynthesisCancelledError();
|
|
5076
|
+
try {
|
|
5077
|
+
return await synthesizeSsml(ssml, config);
|
|
5078
|
+
} catch (error) {
|
|
5079
|
+
if (!options || attempt >= options.maxRetries || !isRetryableSynthesisError(error)) throw error;
|
|
5080
|
+
attempt += 1;
|
|
5081
|
+
const delayMs = retryDelay(options, attempt);
|
|
5082
|
+
onRetry(attempt, delayMs);
|
|
5083
|
+
await waitForRetry(delayMs, config.signal);
|
|
5084
|
+
}
|
|
5085
|
+
}
|
|
5086
|
+
}
|
|
4828
5087
|
async function synthesizeSsmlChunks(chunks, config) {
|
|
4829
|
-
const results =
|
|
5088
|
+
const results = new Array(chunks.length);
|
|
4830
5089
|
const totalChunks = chunks.length;
|
|
4831
5090
|
const report = (event) => config.onProgress?.(event);
|
|
4832
5091
|
for (const [index, chunk] of chunks.entries()) {
|
|
@@ -4841,57 +5100,85 @@ async function synthesizeSsmlChunks(chunks, config) {
|
|
|
4841
5100
|
durationMs: 0
|
|
4842
5101
|
});
|
|
4843
5102
|
}
|
|
4844
|
-
|
|
4845
|
-
|
|
4846
|
-
|
|
4847
|
-
|
|
4848
|
-
|
|
4849
|
-
|
|
4850
|
-
|
|
4851
|
-
|
|
4852
|
-
|
|
4853
|
-
durationMs: 0
|
|
4854
|
-
});
|
|
4855
|
-
const startedAt = Date.now();
|
|
4856
|
-
try {
|
|
4857
|
-
const result = await synthesizeSsml(input.ssml, {
|
|
4858
|
-
...config,
|
|
4859
|
-
...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
|
|
4860
|
-
...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
|
|
4861
|
-
...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
|
|
4862
|
-
...input.sourceMarkers ? { sourceMarkers: input.sourceMarkers } : {},
|
|
4863
|
-
chunkIndex: index,
|
|
4864
|
-
onProgress: void 0
|
|
4865
|
-
});
|
|
4866
|
-
results.push(result);
|
|
4867
|
-
report({
|
|
4868
|
-
currentChunk: index + 1,
|
|
4869
|
-
totalChunks,
|
|
4870
|
-
percent: totalChunks === 0 ? 100 : Math.round((index + 1) / totalChunks * 100),
|
|
4871
|
-
chunkIndex: index,
|
|
4872
|
-
originalTextRange: input.originalTextRange,
|
|
4873
|
-
status: "success",
|
|
4874
|
-
durationMs: Date.now() - startedAt
|
|
4875
|
-
});
|
|
4876
|
-
} catch (error) {
|
|
5103
|
+
let completed = 0;
|
|
5104
|
+
let nextIndex = 0;
|
|
5105
|
+
const concurrency = resolveConcurrency(config.concurrency, chunks.length);
|
|
5106
|
+
const worker = async () => {
|
|
5107
|
+
while (true) {
|
|
5108
|
+
const index = nextIndex++;
|
|
5109
|
+
if (index >= chunks.length) return;
|
|
5110
|
+
const chunk = chunks[index];
|
|
5111
|
+
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
4877
5112
|
report({
|
|
4878
|
-
currentChunk:
|
|
5113
|
+
currentChunk: completed,
|
|
4879
5114
|
totalChunks,
|
|
4880
|
-
percent: totalChunks === 0 ? 100 : Math.round(
|
|
5115
|
+
percent: totalChunks === 0 ? 100 : Math.round(completed / totalChunks * 100),
|
|
4881
5116
|
chunkIndex: index,
|
|
4882
5117
|
originalTextRange: input.originalTextRange,
|
|
4883
|
-
status: "
|
|
4884
|
-
durationMs:
|
|
4885
|
-
error
|
|
5118
|
+
status: "synthesizing",
|
|
5119
|
+
durationMs: 0
|
|
4886
5120
|
});
|
|
4887
|
-
|
|
5121
|
+
const startedAt = Date.now();
|
|
5122
|
+
try {
|
|
5123
|
+
const result = await synthesizeWithRetry(
|
|
5124
|
+
input.ssml,
|
|
5125
|
+
{
|
|
5126
|
+
...config,
|
|
5127
|
+
...input.originalTextRange ? { sourceTextRange: input.originalTextRange } : {},
|
|
5128
|
+
...input.sourceNodePath ?? config.sourceNodePath ? { sourceNodePath: [...input.sourceNodePath ?? config.sourceNodePath ?? []] } : {},
|
|
5129
|
+
...input.sourceTextSegments ? { sourceTextSegments: input.sourceTextSegments } : {},
|
|
5130
|
+
...input.sourceMarkers ? { sourceMarkers: input.sourceMarkers } : {},
|
|
5131
|
+
chunkIndex: index,
|
|
5132
|
+
onProgress: void 0
|
|
5133
|
+
},
|
|
5134
|
+
config.retryOptions,
|
|
5135
|
+
(retryAttempt, nextRetryDelayMs) => report({
|
|
5136
|
+
currentChunk: completed,
|
|
5137
|
+
totalChunks,
|
|
5138
|
+
percent: totalChunks === 0 ? 100 : Math.round(completed / totalChunks * 100),
|
|
5139
|
+
chunkIndex: index,
|
|
5140
|
+
originalTextRange: input.originalTextRange,
|
|
5141
|
+
status: "synthesizing",
|
|
5142
|
+
durationMs: Date.now() - startedAt,
|
|
5143
|
+
retryAttempt,
|
|
5144
|
+
nextRetryDelayMs,
|
|
5145
|
+
isRetrying: true
|
|
5146
|
+
})
|
|
5147
|
+
);
|
|
5148
|
+
results[index] = result;
|
|
5149
|
+
completed += 1;
|
|
5150
|
+
report({
|
|
5151
|
+
currentChunk: completed,
|
|
5152
|
+
totalChunks,
|
|
5153
|
+
percent: totalChunks === 0 ? 100 : Math.round(completed / totalChunks * 100),
|
|
5154
|
+
chunkIndex: index,
|
|
5155
|
+
originalTextRange: input.originalTextRange,
|
|
5156
|
+
status: "success",
|
|
5157
|
+
durationMs: Date.now() - startedAt
|
|
5158
|
+
});
|
|
5159
|
+
} catch (error) {
|
|
5160
|
+
report({
|
|
5161
|
+
currentChunk: completed,
|
|
5162
|
+
totalChunks,
|
|
5163
|
+
percent: totalChunks === 0 ? 100 : Math.round(completed / totalChunks * 100),
|
|
5164
|
+
chunkIndex: index,
|
|
5165
|
+
originalTextRange: input.originalTextRange,
|
|
5166
|
+
status: "failed",
|
|
5167
|
+
durationMs: Date.now() - startedAt,
|
|
5168
|
+
error
|
|
5169
|
+
});
|
|
5170
|
+
throw error;
|
|
5171
|
+
}
|
|
4888
5172
|
}
|
|
4889
|
-
}
|
|
4890
|
-
|
|
4891
|
-
|
|
5173
|
+
};
|
|
5174
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
5175
|
+
const orderedResults = results.filter((result) => result !== void 0);
|
|
5176
|
+
return mergeSynthesisResults(orderedResults, {
|
|
5177
|
+
format: config.outputFormat ?? DEFAULT_OUTPUT_FORMAT,
|
|
5178
|
+
signal: config.signal
|
|
4892
5179
|
});
|
|
4893
5180
|
}
|
|
4894
|
-
function createMergedResult(results, audioData, format) {
|
|
5181
|
+
function createMergedResult(results, audioData, format, audioSpec, outputMimeType) {
|
|
4895
5182
|
const boundaries = [];
|
|
4896
5183
|
const visemes = [];
|
|
4897
5184
|
const bookmarks = [];
|
|
@@ -4910,7 +5197,8 @@ function createMergedResult(results, audioData, format) {
|
|
|
4910
5197
|
...boundary.sourceNodePath ? { sourceNodePath: [...boundary.sourceNodePath] } : {},
|
|
4911
5198
|
...originalTextRange ? { originalTextRange: { ...originalTextRange } } : {},
|
|
4912
5199
|
...textRange ? { textRange: { ...textRange } } : {},
|
|
4913
|
-
...requestId ? { requestId } : {}
|
|
5200
|
+
...requestId ? { requestId } : {},
|
|
5201
|
+
mappingStatus: boundary.mappingStatus ?? "unmapped"
|
|
4914
5202
|
});
|
|
4915
5203
|
}
|
|
4916
5204
|
for (const viseme of result.visemes ?? []) {
|
|
@@ -4925,7 +5213,8 @@ function createMergedResult(results, audioData, format) {
|
|
|
4925
5213
|
...viseme.sourceNodePath ? { sourceNodePath: [...viseme.sourceNodePath] } : {},
|
|
4926
5214
|
...originalTextRange ? { originalTextRange: { ...originalTextRange } } : {},
|
|
4927
5215
|
...textRange ? { textRange: { ...textRange } } : {},
|
|
4928
|
-
...requestId ? { requestId } : {}
|
|
5216
|
+
...requestId ? { requestId } : {},
|
|
5217
|
+
mappingStatus: viseme.mappingStatus ?? "unmapped"
|
|
4929
5218
|
});
|
|
4930
5219
|
}
|
|
4931
5220
|
for (const bookmark of result.bookmarks ?? []) {
|
|
@@ -4940,7 +5229,8 @@ function createMergedResult(results, audioData, format) {
|
|
|
4940
5229
|
...bookmark.sourceNodePath ? { sourceNodePath: [...bookmark.sourceNodePath] } : {},
|
|
4941
5230
|
...originalTextRange ? { originalTextRange: { ...originalTextRange } } : {},
|
|
4942
5231
|
...textRange ? { textRange: { ...textRange } } : {},
|
|
4943
|
-
...requestId ? { requestId } : {}
|
|
5232
|
+
...requestId ? { requestId } : {},
|
|
5233
|
+
mappingStatus: bookmark.mappingStatus ?? "unmapped"
|
|
4944
5234
|
});
|
|
4945
5235
|
}
|
|
4946
5236
|
durationOffset += Math.max(0, result.durationMs);
|
|
@@ -4949,6 +5239,8 @@ function createMergedResult(results, audioData, format) {
|
|
|
4949
5239
|
audioData,
|
|
4950
5240
|
durationMs: durationOffset,
|
|
4951
5241
|
mimeType: resolveMimeType(format),
|
|
5242
|
+
audioSpec: audioSpec ?? formatAudioSpecification(format),
|
|
5243
|
+
...outputMimeType ? { mimeType: outputMimeType } : {},
|
|
4952
5244
|
...boundaries.length > 0 ? { boundaries, wordBoundary: boundaries, wordBoundaries: boundaries } : {},
|
|
4953
5245
|
...visemes.length > 0 ? { visemes } : {},
|
|
4954
5246
|
...bookmarks.length > 0 ? { bookmarks } : {},
|
|
@@ -4961,19 +5253,47 @@ function mergeSynthesisResults(results, options) {
|
|
|
4961
5253
|
const format = resolvedOptions?.format;
|
|
4962
5254
|
if (!format) throw new UnsupportedMergeFormatError("");
|
|
4963
5255
|
const buffers = results.map((result) => result.audioData);
|
|
5256
|
+
const inputSpecs = results.map((result) => result.audioSpec ?? inspectAudioSpecification(result.audioData, format));
|
|
5257
|
+
validateAudioSpecifications(inputSpecs);
|
|
5258
|
+
const signal = resolvedOptions.signal ?? new AbortController().signal;
|
|
5259
|
+
if (signal.aborted) throw new SynthesisCancelledError();
|
|
4964
5260
|
if (resolvedOptions.customMerger) {
|
|
4965
|
-
return Promise.resolve().then(
|
|
5261
|
+
return Promise.resolve().then(
|
|
5262
|
+
() => resolvedOptions.customMerger?.(buffers, {
|
|
5263
|
+
format,
|
|
5264
|
+
outputMimeType: resolvedOptions.outputMimeType ?? resolveMimeType(format),
|
|
5265
|
+
inputSpecs,
|
|
5266
|
+
signal
|
|
5267
|
+
})
|
|
5268
|
+
).then((merged) => {
|
|
4966
5269
|
if (!merged) throw new MergeError("The custom audio merger returned no audio buffer.");
|
|
4967
|
-
|
|
5270
|
+
if (!(merged instanceof ArrayBuffer) || buffers.some((buffer) => buffer.byteLength > 0) && merged.byteLength === 0)
|
|
5271
|
+
throw new MergeError("The custom audio merger returned an invalid audio buffer.");
|
|
5272
|
+
if (signal.aborted) throw new SynthesisCancelledError();
|
|
5273
|
+
return createMergedResult(
|
|
5274
|
+
results,
|
|
5275
|
+
merged,
|
|
5276
|
+
format,
|
|
5277
|
+
inspectAudioSpecification(merged, format),
|
|
5278
|
+
resolvedOptions.outputMimeType
|
|
5279
|
+
);
|
|
4968
5280
|
}).catch((error) => {
|
|
4969
|
-
if (error instanceof UnsupportedMergeFormatError || error instanceof MergeError)
|
|
5281
|
+
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
5282
|
+
throw error;
|
|
4970
5283
|
throw new MergeError(`Custom audio merger failed for format "${format}".`, error);
|
|
4971
5284
|
});
|
|
4972
5285
|
}
|
|
4973
5286
|
try {
|
|
4974
|
-
return createMergedResult(
|
|
5287
|
+
return createMergedResult(
|
|
5288
|
+
results,
|
|
5289
|
+
mergeAudioBuffers(buffers, { format }),
|
|
5290
|
+
format,
|
|
5291
|
+
inputSpecs[0],
|
|
5292
|
+
resolvedOptions.outputMimeType
|
|
5293
|
+
);
|
|
4975
5294
|
} catch (error) {
|
|
4976
|
-
if (error instanceof UnsupportedMergeFormatError || error instanceof MergeError)
|
|
5295
|
+
if (error instanceof UnsupportedMergeFormatError || isAudioFormatMismatch(error) || error instanceof MergeError)
|
|
5296
|
+
throw error;
|
|
4977
5297
|
throw new MergeError(`Audio buffers could not be merged for format "${format}".`, error);
|
|
4978
5298
|
}
|
|
4979
5299
|
}
|
|
@@ -4994,8 +5314,73 @@ var ChunkValidationError = class extends Error {
|
|
|
4994
5314
|
function failure(error) {
|
|
4995
5315
|
return { ok: false, success: false, status: error.kind, error };
|
|
4996
5316
|
}
|
|
5317
|
+
function isRetryable(error) {
|
|
5318
|
+
if (error instanceof Error && /cancel|abort|tim(?:e|ed) ?out/i.test(error.message)) return false;
|
|
5319
|
+
const status = error && typeof error === "object" && "status" in error ? error.status : void 0;
|
|
5320
|
+
if (typeof status === "number" && status !== 0) return status === 429 || status >= 500 && status < 600;
|
|
5321
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
5322
|
+
if (/\b4\d{2}\b/.test(message)) return false;
|
|
5323
|
+
return /network|connection|connect|socket|fetch failed|econn|etimedout|temporar|transient|unavailable/i.test(message);
|
|
5324
|
+
}
|
|
5325
|
+
function delayForRetry(options, attempt) {
|
|
5326
|
+
const maxDelay = Math.max(0, options.maxDelayMs);
|
|
5327
|
+
const base = Math.min(maxDelay, Math.max(0, options.initialDelayMs) * 2 ** Math.max(0, attempt - 1));
|
|
5328
|
+
return Math.floor(Math.random() * (base + 1));
|
|
5329
|
+
}
|
|
5330
|
+
function resolveConcurrency2(value, total) {
|
|
5331
|
+
if (value === void 0) return 1;
|
|
5332
|
+
if (value === Infinity) return Math.max(1, total);
|
|
5333
|
+
return Number.isFinite(value) ? Math.max(1, Math.floor(value)) : 1;
|
|
5334
|
+
}
|
|
5335
|
+
async function retryableSynthesis(synthesize, options, signal, onRetry) {
|
|
5336
|
+
const retry = options ? {
|
|
5337
|
+
maxRetries: Math.max(0, Math.floor(options.maxRetries)),
|
|
5338
|
+
initialDelayMs: options.initialDelayMs,
|
|
5339
|
+
maxDelayMs: options.maxDelayMs
|
|
5340
|
+
} : void 0;
|
|
5341
|
+
let attempt = 0;
|
|
5342
|
+
while (true) {
|
|
5343
|
+
if (signal?.aborted) throw new Error("Speech synthesis was cancelled.");
|
|
5344
|
+
try {
|
|
5345
|
+
return await synthesize();
|
|
5346
|
+
} catch (error) {
|
|
5347
|
+
if (!retry || attempt >= retry.maxRetries || !isRetryable(error)) throw error;
|
|
5348
|
+
attempt += 1;
|
|
5349
|
+
const delayMs = delayForRetry(retry, attempt);
|
|
5350
|
+
onRetry(attempt, delayMs);
|
|
5351
|
+
if (delayMs > 0)
|
|
5352
|
+
await new Promise((resolve, reject) => {
|
|
5353
|
+
const timer = setTimeout(() => {
|
|
5354
|
+
signal?.removeEventListener("abort", abort);
|
|
5355
|
+
resolve();
|
|
5356
|
+
}, delayMs);
|
|
5357
|
+
const abort = () => {
|
|
5358
|
+
clearTimeout(timer);
|
|
5359
|
+
signal?.removeEventListener("abort", abort);
|
|
5360
|
+
reject(new Error("Speech synthesis was cancelled."));
|
|
5361
|
+
};
|
|
5362
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
5363
|
+
});
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
5366
|
+
}
|
|
5367
|
+
function sharedValidationOptions(options, signal) {
|
|
5368
|
+
const validator = options.urlValidator ?? options.customUrlValidator;
|
|
5369
|
+
if (!validator) return signal ? withValidationSignal(options, signal) : options;
|
|
5370
|
+
const runner = createAzureUrlValidatorRunner2(validator, {
|
|
5371
|
+
...options.urlValidation ?? {},
|
|
5372
|
+
...options.urlValidatorConcurrency !== void 0 ? { concurrency: options.urlValidatorConcurrency } : {},
|
|
5373
|
+
...options.urlValidatorTimeoutMs !== void 0 ? { timeoutMs: options.urlValidatorTimeoutMs } : {},
|
|
5374
|
+
...signal ? { signal } : {},
|
|
5375
|
+
...options.urlValidatorCache ? { cache: options.urlValidatorCache } : {}
|
|
5376
|
+
});
|
|
5377
|
+
return {
|
|
5378
|
+
...withValidationSignal(options, signal),
|
|
5379
|
+
urlValidatorRunner: runner
|
|
5380
|
+
};
|
|
5381
|
+
}
|
|
4997
5382
|
async function synthesizeSsmlSafe(client, ssml, options = {}) {
|
|
4998
|
-
const validationOptions =
|
|
5383
|
+
const validationOptions = sharedValidationOptions(options.validation ?? options, options.signal);
|
|
4999
5384
|
const diagnostics = await Promise.resolve(validateAzureSsml2(ssml, validationOptions));
|
|
5000
5385
|
if (options.signal?.aborted) {
|
|
5001
5386
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
@@ -5022,7 +5407,7 @@ async function synthesizeSsmlSafe(client, ssml, options = {}) {
|
|
|
5022
5407
|
}
|
|
5023
5408
|
}
|
|
5024
5409
|
async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
5025
|
-
const validationOptions =
|
|
5410
|
+
const validationOptions = sharedValidationOptions(options.validation ?? options, options.signal);
|
|
5026
5411
|
if (options.signal?.aborted) {
|
|
5027
5412
|
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
5028
5413
|
return failure(error);
|
|
@@ -5043,16 +5428,24 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
5043
5428
|
pending(index, "pending");
|
|
5044
5429
|
});
|
|
5045
5430
|
const validations = await Promise.all(
|
|
5046
|
-
chunks.map(async (chunk) => {
|
|
5431
|
+
chunks.map(async (chunk, index) => {
|
|
5047
5432
|
const ssml = typeof chunk === "string" ? chunk : chunk.ssml;
|
|
5048
5433
|
const sourceNodePath = typeof chunk === "string" ? options.sourceNodePath : chunk.sourceNodePath ?? options.sourceNodePath;
|
|
5049
5434
|
const diagnostics = await Promise.resolve(
|
|
5050
|
-
validateAzureSsml2(ssml, {
|
|
5435
|
+
validateAzureSsml2(ssml, {
|
|
5436
|
+
...validationOptions,
|
|
5437
|
+
...sourceNodePath ? { sourceNodePath } : {},
|
|
5438
|
+
chunkIndex: index
|
|
5439
|
+
})
|
|
5051
5440
|
);
|
|
5052
5441
|
return diagnostics.filter((diagnostic) => diagnostic.severity === "error");
|
|
5053
5442
|
})
|
|
5054
5443
|
);
|
|
5055
5444
|
const firstInvalidIndex = validations.findIndex((diagnostics) => diagnostics.length > 0);
|
|
5445
|
+
if (options.signal?.aborted) {
|
|
5446
|
+
const error = toSynthesisError(new Error("Speech synthesis was cancelled."));
|
|
5447
|
+
return failure(error);
|
|
5448
|
+
}
|
|
5056
5449
|
if (firstInvalidIndex >= 0) {
|
|
5057
5450
|
const error = new ChunkValidationError(firstInvalidIndex, validations[firstInvalidIndex] ?? []);
|
|
5058
5451
|
pending(firstInvalidIndex, "failed", error);
|
|
@@ -5069,96 +5462,126 @@ async function synthesizeSsmlChunksSafe(client, chunks, options = {}) {
|
|
|
5069
5462
|
outputFormat: options.outputFormat,
|
|
5070
5463
|
signal: options.signal,
|
|
5071
5464
|
timeoutMs: options.timeoutMs,
|
|
5072
|
-
sourceNodePath: options.sourceNodePath
|
|
5465
|
+
sourceNodePath: options.sourceNodePath,
|
|
5466
|
+
concurrency: options.concurrency,
|
|
5467
|
+
retryOptions: options.retryOptions
|
|
5073
5468
|
});
|
|
5074
5469
|
return { ok: true, success: true, status: "success", value };
|
|
5075
5470
|
}
|
|
5076
|
-
const results =
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
const
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
sourceNodePath:
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5119
|
-
|
|
5120
|
-
|
|
5121
|
-
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5139
|
-
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5471
|
+
const results = new Array(chunks.length);
|
|
5472
|
+
let completed = 0;
|
|
5473
|
+
let nextIndex = 0;
|
|
5474
|
+
const concurrency = resolveConcurrency2(options.concurrency, chunks.length);
|
|
5475
|
+
const worker = async () => {
|
|
5476
|
+
while (true) {
|
|
5477
|
+
const index = nextIndex++;
|
|
5478
|
+
if (index >= chunks.length) return;
|
|
5479
|
+
const chunk = chunks[index];
|
|
5480
|
+
const input = typeof chunk === "string" ? { ssml: chunk } : chunk;
|
|
5481
|
+
const sourceNodePath = input.sourceNodePath;
|
|
5482
|
+
const originalTextRange = input.originalTextRange;
|
|
5483
|
+
pending(index, "synthesizing");
|
|
5484
|
+
const startedAt = Date.now();
|
|
5485
|
+
try {
|
|
5486
|
+
const result = await retryableSynthesis(
|
|
5487
|
+
() => client.synthesizeSsml(input.ssml, {
|
|
5488
|
+
outputFormat: options.outputFormat,
|
|
5489
|
+
signal: options.signal,
|
|
5490
|
+
timeoutMs: options.timeoutMs,
|
|
5491
|
+
sourceNodePath: input.sourceNodePath ?? options.sourceNodePath
|
|
5492
|
+
}),
|
|
5493
|
+
options.retryOptions,
|
|
5494
|
+
options.signal,
|
|
5495
|
+
(retryAttempt, nextRetryDelayMs) => options.onProgress?.({
|
|
5496
|
+
currentChunk: completed,
|
|
5497
|
+
totalChunks: chunks.length,
|
|
5498
|
+
percent: chunks.length === 0 ? 100 : Math.round(completed / chunks.length * 100),
|
|
5499
|
+
chunkIndex: index,
|
|
5500
|
+
originalTextRange: input.originalTextRange,
|
|
5501
|
+
status: "synthesizing",
|
|
5502
|
+
durationMs: Date.now() - startedAt,
|
|
5503
|
+
retryAttempt,
|
|
5504
|
+
nextRetryDelayMs,
|
|
5505
|
+
isRetrying: true
|
|
5506
|
+
})
|
|
5507
|
+
);
|
|
5508
|
+
results[index] = {
|
|
5509
|
+
...result,
|
|
5510
|
+
...input.originalTextRange ? { textRange: { ...input.originalTextRange } } : {},
|
|
5511
|
+
...sourceNodePath ? {
|
|
5512
|
+
boundaries: result.boundaries?.map((event) => ({
|
|
5513
|
+
...event,
|
|
5514
|
+
sourceNodePath: [...sourceNodePath],
|
|
5515
|
+
...event.originalTextRange ? { originalTextRange: { ...event.originalTextRange } } : input.originalTextRange ? { originalTextRange: { ...input.originalTextRange } } : {}
|
|
5516
|
+
})),
|
|
5517
|
+
visemes: result.visemes?.map((event) => ({
|
|
5518
|
+
...event,
|
|
5519
|
+
sourceNodePath: [...sourceNodePath],
|
|
5520
|
+
...event.originalTextRange ? { originalTextRange: { ...event.originalTextRange } } : input.originalTextRange ? { originalTextRange: { ...input.originalTextRange } } : {}
|
|
5521
|
+
})),
|
|
5522
|
+
bookmarks: result.bookmarks?.map((event) => ({
|
|
5523
|
+
...event,
|
|
5524
|
+
sourceNodePath: [...sourceNodePath],
|
|
5525
|
+
...event.originalTextRange ? { originalTextRange: { ...event.originalTextRange } } : input.originalTextRange ? { originalTextRange: { ...input.originalTextRange } } : {}
|
|
5526
|
+
}))
|
|
5527
|
+
} : {},
|
|
5528
|
+
...originalTextRange ? {
|
|
5529
|
+
boundaries: result.boundaries?.map((event) => ({
|
|
5530
|
+
...event,
|
|
5531
|
+
originalTextRange: event.originalTextRange ? { ...event.originalTextRange } : { ...originalTextRange }
|
|
5532
|
+
})),
|
|
5533
|
+
wordBoundary: result.wordBoundary?.map((event) => ({
|
|
5534
|
+
...event,
|
|
5535
|
+
originalTextRange: event.originalTextRange ? { ...event.originalTextRange } : { ...originalTextRange }
|
|
5536
|
+
})),
|
|
5537
|
+
wordBoundaries: result.wordBoundaries?.map((event) => ({
|
|
5538
|
+
...event,
|
|
5539
|
+
originalTextRange: event.originalTextRange ? { ...event.originalTextRange } : { ...originalTextRange }
|
|
5540
|
+
})),
|
|
5541
|
+
visemes: result.visemes?.map((event) => ({
|
|
5542
|
+
...event,
|
|
5543
|
+
originalTextRange: event.originalTextRange ? { ...event.originalTextRange } : { ...originalTextRange }
|
|
5544
|
+
})),
|
|
5545
|
+
bookmarks: result.bookmarks?.map((event) => ({
|
|
5546
|
+
...event,
|
|
5547
|
+
originalTextRange: event.originalTextRange ? { ...event.originalTextRange } : { ...originalTextRange }
|
|
5548
|
+
}))
|
|
5549
|
+
} : {}
|
|
5550
|
+
};
|
|
5551
|
+
completed += 1;
|
|
5552
|
+
options.onProgress?.({
|
|
5553
|
+
currentChunk: completed,
|
|
5554
|
+
totalChunks: chunks.length,
|
|
5555
|
+
percent: chunks.length === 0 ? 100 : Math.round(completed / chunks.length * 100),
|
|
5556
|
+
chunkIndex: index,
|
|
5557
|
+
originalTextRange: input.originalTextRange,
|
|
5558
|
+
status: "success",
|
|
5559
|
+
durationMs: Date.now() - startedAt
|
|
5560
|
+
});
|
|
5561
|
+
} catch (error) {
|
|
5562
|
+
options.onProgress?.({
|
|
5563
|
+
currentChunk: completed,
|
|
5564
|
+
totalChunks: chunks.length,
|
|
5565
|
+
percent: chunks.length === 0 ? 100 : Math.round(index / chunks.length * 100),
|
|
5566
|
+
chunkIndex: index,
|
|
5567
|
+
originalTextRange: input.originalTextRange,
|
|
5568
|
+
status: "failed",
|
|
5569
|
+
durationMs: Date.now() - startedAt,
|
|
5570
|
+
error
|
|
5571
|
+
});
|
|
5572
|
+
throw error;
|
|
5573
|
+
}
|
|
5154
5574
|
}
|
|
5155
|
-
}
|
|
5575
|
+
};
|
|
5576
|
+
await Promise.all(Array.from({ length: Math.min(concurrency, chunks.length) }, () => worker()));
|
|
5577
|
+
const orderedResults = results.filter((result) => result !== void 0);
|
|
5156
5578
|
return {
|
|
5157
5579
|
ok: true,
|
|
5158
5580
|
success: true,
|
|
5159
5581
|
status: "success",
|
|
5160
|
-
value: mergeSynthesisResults(
|
|
5161
|
-
format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3"
|
|
5582
|
+
value: mergeSynthesisResults(orderedResults, {
|
|
5583
|
+
format: options.outputFormat ?? "audio-16khz-128kbitrate-mono-mp3",
|
|
5584
|
+
signal: options.signal
|
|
5162
5585
|
})
|
|
5163
5586
|
};
|
|
5164
5587
|
} catch (error) {
|
|
@@ -5217,7 +5640,9 @@ var AzureTtsClient = class {
|
|
|
5217
5640
|
signal: options.signal ?? signal,
|
|
5218
5641
|
timeoutMs: options.timeoutMs ?? timeoutMs,
|
|
5219
5642
|
sourceNodePath: options.sourceNodePath,
|
|
5220
|
-
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress
|
|
5643
|
+
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
5644
|
+
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
5645
|
+
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
5221
5646
|
});
|
|
5222
5647
|
}
|
|
5223
5648
|
async synthesizeSsmlSafe(ssml, options = {}) {
|
|
@@ -5229,7 +5654,9 @@ var AzureTtsClient = class {
|
|
|
5229
5654
|
outputFormat: options.outputFormat ?? __privateGet(this, _options).outputFormat,
|
|
5230
5655
|
signal: options.signal ?? __privateGet(this, _options).signal,
|
|
5231
5656
|
timeoutMs: options.timeoutMs ?? __privateGet(this, _options).timeoutMs,
|
|
5232
|
-
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress
|
|
5657
|
+
onProgress: options.onProgress ?? __privateGet(this, _options).onProgress,
|
|
5658
|
+
concurrency: options.concurrency ?? __privateGet(this, _options).concurrency,
|
|
5659
|
+
retryOptions: options.retryOptions ?? __privateGet(this, _options).retryOptions
|
|
5233
5660
|
});
|
|
5234
5661
|
}
|
|
5235
5662
|
async synthesizeSsmlChunksSafe(chunks, options = {}) {
|
|
@@ -5326,6 +5753,7 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
5326
5753
|
}
|
|
5327
5754
|
// Annotate the CommonJS export names for ESM import in node:
|
|
5328
5755
|
0 && (module.exports = {
|
|
5756
|
+
AudioFormatMismatchError,
|
|
5329
5757
|
AzureTtsClient,
|
|
5330
5758
|
AzureTtsError,
|
|
5331
5759
|
AzureTtsSdkError,
|
|
@@ -5347,6 +5775,7 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
5347
5775
|
getAzureVoiceCatalogMetadata,
|
|
5348
5776
|
getBuiltInVoiceCatalogMetadata,
|
|
5349
5777
|
getSsmlSourceMap,
|
|
5778
|
+
inspectAudioSpecification,
|
|
5350
5779
|
isValidAzureAudioDuration,
|
|
5351
5780
|
mapSsmlTextNodes,
|
|
5352
5781
|
mergeAudioBuffers,
|
|
@@ -5362,6 +5791,7 @@ async function fetchAzureVoiceCatalog(options) {
|
|
|
5362
5791
|
synthesizeSsmlChunksSafe,
|
|
5363
5792
|
synthesizeSsmlSafe,
|
|
5364
5793
|
validateAzureSsml,
|
|
5794
|
+
validateAzureSsmlChunks,
|
|
5365
5795
|
validateSsml,
|
|
5366
5796
|
validateSsmlStructureIntegrity
|
|
5367
5797
|
});
|