ssml-builder-js 2.10.0 → 2.11.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 CHANGED
@@ -179,7 +179,7 @@ SSML の検証は、構文、Azure 固有の静的な意味、実サービスの
179
179
 
180
180
  `voice`、`prosody`、`break`、`express-as`、`say-as`、`phoneme`、`audio`、`lang`、`mark` に加えて、`mstts:dialog`、`mstts:turn`、`mstts:backgroundaudio`、`mstts:ttsembedding`、`mstts:embedding`、`mstts:voiceconversion` を型付きで表現できます。`type: "custom"` と `name` を指定すれば、未定義の XML 要素や追加属性も扱えます。`mstts:` 要素を含むドキュメントを生成すると、必要な Azure Speech 名前空間が自動的に追加されます。
181
181
 
182
- `mstts:turn` は `voice` またはマルチトーカー用の `speaker` を指定でき、`mstts:ttsembedding` は `speakerProfileId`、`mstts:embedding` は `id`、`mstts:voiceconversion` は `url` と `profile` を専用プロパティで指定できます。`mstts:backgroundaudio` は `<speak>` 直下の先頭要素として 1 文書に 1 つだけ配置します。
182
+ `mstts:turn` は `voice` またはマルチトーカー用の `speaker` を指定でき、`mstts:ttsembedding` は `speakerProfileId`、`mstts:embedding` は `id`、`mstts:voiceconversion` は `url` と `profile` を専用プロパティで指定できます。`mstts:backgroundaudio` は `<speak>` 直下の先頭要素として 1 文書に 1 つだけ配置し、`fadein`/`fadeout` は単位なしの生ミリ秒(0〜10000)で指定します。
183
183
 
184
184
  `validateAzureSsml` は Azure Speech へ送信する前に実行する事前静的チェックです。返却される診断の `source` はパッケージ側の静的解析結果であることを示し、Azure Speech サービス側でのランタイム生成結果や実際の合成可否を表すものではありません。
185
185
 
@@ -208,7 +208,7 @@ const diagnostics = validateAzureSsml(ssml, {
208
208
  });
209
209
  ```
210
210
 
211
- 音声カタログは `AzureVoiceDefinition` の `name`、`locale`、`secondaryLocales`、`styles`、`supportedTags`、`unsupportedTags`、`models` で表現できます。`voiceDefinitions`(または `voiceCatalog`)を渡すと、組み込み台帳を外部定義で補完・上書きできます。`supportedTags`/`unsupportedTags` による音声別制約違反は `azure-unsupported-tag-for-voice` としてエラーになります。組み込み台帳にない音声は `azure-unknown-voice`(`unknownVoicePolicy` に従う)、登録済み音声への非対応スタイルは `azure-unsupported-style`、ロケール不一致は `azure-locale-mismatch` として区別されます。
211
+ 音声カタログは `AzureVoiceDefinition` の `name`、`locale`、`secondaryLocales`、`styles`、`supportedTags`、`unsupportedTags`、`models`、`regions`、`status` で表現できます。`voiceDefinitions`(または `voiceCatalog`)を渡すと、組み込み台帳を外部定義で補完・上書きできます。`supportedTags`/`unsupportedTags` による音声別制約違反は `azure-unsupported-tag-for-voice` としてエラーになります。組み込み台帳にない音声は `azure-unknown-voice`(`unknownVoicePolicy` に従う)、登録済み音声への非対応スタイルは `azure-unsupported-style`、ロケール不一致は `azure-locale-mismatch` として区別されます。
212
212
 
213
213
  `npm run sync:voices -- --regions eastus,japaneast` は Azure List Voices API をリージョンごとに取得し、重複を除いた TypeScript 音声定義と `azureVoiceCatalog.json`(生成日時、API バージョン、リージョン、収録数)を更新します。認証情報は `AZURE_SPEECH_KEY` と `AZURE_SPEECH_REGION(S)`、または CLI オプションで指定します。
214
214
 
@@ -247,6 +247,24 @@ Azure Speech は `<audio>` の URL を取得するため、任意の URL をそ
247
247
 
248
248
  `editMode="visual"` を指定するかツールバーの **Visual** を選ぶと、XML を直接見ずに構造ツリーとパンくずから親要素を選択し、フォームで本文の変更、rate、pitch、emotion、pause、pronunciation の適用ができます。Azure の配置違反や属性エラーはビジュアル領域にも表示されます。`editMode="code"` で Monaco に戻ります。
249
249
 
250
+ Visual Editor と Code Editor の対応要素は次のとおりです。
251
+
252
+ | 要素 | Visual Editor | Code Editor |
253
+ | --- | --- | --- |
254
+ | `voice`、`prosody`、`break`、`express-as`、`say-as`、`phoneme` | フォーム操作・構造ツリー | 完全対応 |
255
+ | `mstts:dialog` / `mstts:turn` | 話者ターンの追加・音声/話者/本文編集 | 完全対応・補完あり |
256
+ | `mstts:backgroundaudio` | URL、音量、フェードイン/アウト編集 | 完全対応・補完あり |
257
+ | `mstts:ttsembedding`、`mstts:embedding`、`mstts:voiceconversion` | 構造ツリーで保持 | 完全対応 |
258
+ | 未定義の XML 要素 | 構造ツリーで保持 | `custom` として編集 |
259
+
260
+ Azure Speech のライフサイクル対応状況は次のとおりです。GA 要素は通常の静的検証対象、プレビュー要素・音声は Warning、非推奨要素・音声は Info の診断を返します。`AzureVoiceDefinition.status` と `AzureValidationOptions.tagStatuses` で外部カタログの状態も指定できます。
261
+
262
+ | 状態 | 診断 | 対象 |
263
+ | --- | --- | --- |
264
+ | GA | なし | 標準 SSML、`mstts:dialog`、`mstts:backgroundaudio` |
265
+ | Preview | Warning | `mstts:voiceconversion`、`previewTags` / `tagStatuses` に指定した要素、`status: "preview"` の音声 |
266
+ | Deprecated | Info | `deprecatedTags` / `tagStatuses`、`status: "deprecated"` の音声 |
267
+
250
268
  `<mstts:express-as>` の `style` 属性補完と標準の感情メニューは、カーソルまたは選択範囲を囲む最内の `<voice name="...">` が対応するスタイルだけを表示します。音声名が未指定の場合は全候補を表示し、登録済みでスタイル非対応の音声や未登録の音声では候補がないことを表示します。`emotionStyles` を指定した場合、感情メニューではその値と登録済み音声の対応スタイルの共通部分を使用します。
251
269
 
252
270
  ```tsx
@@ -349,6 +367,19 @@ const audio = await client.synthesize(ssml);
349
367
 
350
368
  帯域幅を抑える場合は `audio-24khz-48kbitrate-mono-mp3` または `audio-16khz-32kbitrate-mono-mp3` を指定できます。
351
369
 
370
+ 最新の音声一覧は公開 API から取得できます。複数リージョンを指定した場合は音声名で重複排除され、各音声の `regions` と `metadata`(`voiceCount`、`generatedAt`、`apiVersion`、`regions`)が返ります。
371
+
372
+ ```ts
373
+ import { fetchAzureVoiceCatalog } from "ssml-builder-js";
374
+
375
+ const catalog = await fetchAzureVoiceCatalog({
376
+ apiKey: process.env.AZURE_SPEECH_KEY!,
377
+ region: ["eastus", "japaneast"],
378
+ });
379
+ ```
380
+
381
+ CLI では `npx ssml-builder sync-voices --region eastus --output ./azure-voices.json` を実行します。キーは `AZURE_SPEECH_KEY`(または `--key`)、リージョンは `AZURE_SPEECH_REGION(S)`(または `--region(s)`)から指定できます。
382
+
352
383
  ```ts
353
384
  const client = new AzureTtsClient({
354
385
  subscriptionKey: process.env.AZURE_SPEECH_KEY!,
@@ -629,7 +660,7 @@ SSML validation separates XML syntax, Azure-specific static semantics, and runti
629
660
 
630
661
  Typed representations are available for elements such as `voice`, `prosody`, `break`, `express-as`, `say-as`, `phoneme`, `audio`, `lang`, and `mark`, plus `mstts:dialog`, `mstts:turn`, `mstts:backgroundaudio`, `mstts:ttsembedding`, `mstts:embedding`, and `mstts:voiceconversion`. Use `type: "custom"` and `name` to handle undefined XML elements or additional attributes. When a document contains `mstts:` elements, the required Azure Speech namespace is added automatically.
631
662
 
632
- `mstts:turn` accepts either `voice` or the multi-talker `speaker` property. The typed extension properties include `speakerProfileId` for `mstts:ttsembedding`, `id` for `mstts:embedding`, and `url` plus `profile` for `mstts:voiceconversion`. `mstts:backgroundaudio` must be the first direct child element of `<speak>` and may appear only once per document.
663
+ `mstts:turn` accepts either `voice` or the multi-talker `speaker` property. The typed extension properties include `speakerProfileId` for `mstts:ttsembedding`, `id` for `mstts:embedding`, and `url` plus `profile` for `mstts:voiceconversion`. `mstts:backgroundaudio` must be the first direct child element of `<speak>` and may appear only once per document; `fadein` and `fadeout` are raw milliseconds from 0 through 10000.
633
664
 
634
665
  `validateAzureSsml` is a preflight static check performed by this package before sending SSML to Azure Speech. The `source` on each returned diagnostic identifies package-side static analysis; it is independent of Azure Speech's runtime generation result and does not guarantee that synthesis will succeed.
635
666
 
@@ -654,7 +685,7 @@ const diagnostics = validateAzureSsml(ssml, {
654
685
  });
655
686
  ```
656
687
 
657
- The catalog is represented by `AzureVoiceDefinition` with `name`, `locale`, optional `secondaryLocales`, `styles`, `supportedTags`, `unsupportedTags`, and `models`. Pass `voiceDefinitions` (or `voiceCatalog`) to supplement or override the built-in catalog with an external definition. A tag that violates `supportedTags` or `unsupportedTags` produces `azure-unsupported-tag-for-voice` with error severity. `customVoiceStyleMap` remains supported for backward compatibility and overrides styles for the named voice. Diagnostics distinguish an unregistered voice (`azure-unknown-voice`, controlled by `unknownVoicePolicy`), an unsupported style on a registered voice (`azure-unsupported-style`), and a locale mismatch (`azure-locale-mismatch`). The `<mstts:audioduration value="10s"/>` element accepts positive `ms` or `s` values and `hh:mm:ss[.fff]` clock values.
688
+ The catalog is represented by `AzureVoiceDefinition` with `name`, `locale`, optional `secondaryLocales`, `styles`, `supportedTags`, `unsupportedTags`, `models`, `regions`, and `status`. Pass `voiceDefinitions` (or `voiceCatalog`) to supplement or override the built-in catalog with an external definition. A tag that violates `supportedTags` or `unsupportedTags` produces `azure-unsupported-tag-for-voice` with error severity. `customVoiceStyleMap` remains supported for backward compatibility and overrides styles for the named voice. Diagnostics distinguish an unregistered voice (`azure-unknown-voice`, controlled by `unknownVoicePolicy`), an unsupported style on a registered voice (`azure-unsupported-style`), and a locale mismatch (`azure-locale-mismatch`). The `<mstts:audioduration value="10s"/>` element accepts positive `ms` or `s` values and `hh:mm:ss[.fff]` clock values.
658
689
 
659
690
  `npm run sync:voices -- --regions eastus,japaneast` fetches Azure's List Voices API for each region, deduplicates the results, and updates the generated TypeScript definitions plus `azureVoiceCatalog.json` with generation time, API version, regions, and voice count. Provide credentials through `AZURE_SPEECH_KEY` and `AZURE_SPEECH_REGION(S)`, or CLI options.
660
691
 
@@ -693,6 +724,24 @@ Azure Speech fetches `<audio>` URLs, so a server that accepts arbitrary user-pro
693
724
 
694
725
  Set `editMode="visual"`, or choose **Visual** in the toolbar, to edit without viewing XML source. The structured editor provides a structure tree, parent breadcrumb selection, text editing, and form actions for rate, pitch, emotion, pause, and pronunciation. Azure placement and attribute diagnostics are shown in the visual area. Choose `editMode="code"` to return to Monaco.
695
726
 
727
+ The Visual Editor and Code Editor support the following elements:
728
+
729
+ | Element | Visual Editor | Code Editor |
730
+ | --- | --- | --- |
731
+ | `voice`, `prosody`, `break`, `express-as`, `say-as`, `phoneme` | Form actions and structure tree | Full support |
732
+ | `mstts:dialog` / `mstts:turn` | Add turns and edit voice, speaker, and text | Full support with completion |
733
+ | `mstts:backgroundaudio` | Edit URL, volume, fade-in, and fade-out | Full support with completion |
734
+ | `mstts:ttsembedding`, `mstts:embedding`, `mstts:voiceconversion` | Preserved in the structure tree | Full support |
735
+ | Unknown XML elements | Preserved in the structure tree | Editable as `custom` |
736
+
737
+ Azure lifecycle support is reported by static validation: GA elements produce no lifecycle diagnostic, preview elements and voices produce a Warning, and deprecated elements and voices produce an Info diagnostic. Use `AzureVoiceDefinition.status` and `AzureValidationOptions.tagStatuses` for external catalog metadata.
738
+
739
+ | Status | Diagnostic | Examples |
740
+ | --- | --- | --- |
741
+ | GA | None | Standard SSML, `mstts:dialog`, `mstts:backgroundaudio` |
742
+ | Preview | Warning | `mstts:voiceconversion`, tags listed in `previewTags` / `tagStatuses`, voices with `status: "preview"` |
743
+ | Deprecated | Info | Tags listed in `deprecatedTags` / `tagStatuses`, voices with `status: "deprecated"` |
744
+
696
745
  Completion for the `<mstts:express-as>` `style` attribute and the built-in Emotion menu only show styles supported by the innermost `<voice name="...">` around the cursor or selection. When no voice name is available, all candidates are shown; a registered voice without supported styles or an explicitly unregistered voice displays an empty-state message. When `emotionStyles` is supplied, the Emotion menu uses its intersection with the registered voice's supported styles.
697
746
 
698
747
  ```tsx
@@ -795,6 +844,19 @@ const audio = await client.synthesize(ssml);
795
844
 
796
845
  For lower bandwidth, choose `audio-24khz-48kbitrate-mono-mp3` or `audio-16khz-32kbitrate-mono-mp3`.
797
846
 
847
+ Fetch the current voice catalog from Azure with `fetchAzureVoiceCatalog`. Multiple regions are deduplicated by voice name, and each voice includes its available `regions`; the result also contains `metadata` with `voiceCount`, `generatedAt`, `apiVersion`, and `regions`.
848
+
849
+ ```ts
850
+ import { fetchAzureVoiceCatalog } from "ssml-builder-js";
851
+
852
+ const catalog = await fetchAzureVoiceCatalog({
853
+ apiKey: process.env.AZURE_SPEECH_KEY!,
854
+ region: ["eastus", "japaneast"],
855
+ });
856
+ ```
857
+
858
+ The public updater CLI is `npx ssml-builder sync-voices --region eastus --output ./azure-voices.json`. It reads the key from `AZURE_SPEECH_KEY` (or `--key`) and regions from `AZURE_SPEECH_REGION(S)` (or `--region(s)`).
859
+
798
860
  ```ts
799
861
  const client = new AzureTtsClient({
800
862
  subscriptionKey: process.env.AZURE_SPEECH_KEY!,
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { writeFile } = require("node:fs/promises");
4
+ const { resolve } = require("node:path");
5
+ const { fetchAzureVoiceCatalog } = require("../dist/index.js");
6
+
7
+ function usage() {
8
+ return [
9
+ "Usage: ssml-builder sync-voices --region <region[,region...]> --output <file>",
10
+ "",
11
+ "Environment variables: AZURE_SPEECH_KEY, AZURE_SPEECH_REGION(S)",
12
+ ].join("\n");
13
+ }
14
+
15
+ function parseArgs(args) {
16
+ const options = {};
17
+ for (let index = 0; index < args.length; index += 1) {
18
+ const argument = args[index];
19
+ if (argument === "--region" || argument === "--regions") {
20
+ options.regions = (args[++index] || "")
21
+ .split(",")
22
+ .map((value) => value.trim())
23
+ .filter(Boolean);
24
+ } else if (argument === "--output") {
25
+ options.output = args[++index];
26
+ } else if (argument === "--key") {
27
+ options.apiKey = args[++index];
28
+ } else if (argument === "--help" || argument === "-h") {
29
+ console.log(usage());
30
+ process.exit(0);
31
+ } else {
32
+ throw new Error(`Unknown option: ${argument}\n\n${usage()}`);
33
+ }
34
+ }
35
+ return options;
36
+ }
37
+
38
+ async function main() {
39
+ const [command, ...args] = process.argv.slice(2);
40
+ if (command !== "sync-voices") throw new Error(usage());
41
+ const options = parseArgs(args);
42
+ const regions =
43
+ options.regions ||
44
+ (process.env.AZURE_SPEECH_REGIONS || process.env.AZURE_SPEECH_REGION || "")
45
+ .split(",")
46
+ .map((value) => value.trim())
47
+ .filter(Boolean);
48
+ const apiKey = options.apiKey || process.env.AZURE_SPEECH_KEY;
49
+ const output = options.output;
50
+ if (!apiKey) throw new Error("Set AZURE_SPEECH_KEY or pass --key.");
51
+ if (regions.length === 0) throw new Error("Set AZURE_SPEECH_REGION(S) or pass --region(s).");
52
+ if (!output) throw new Error(`Pass --output <file>.\n\n${usage()}`);
53
+
54
+ const catalog = await fetchAzureVoiceCatalog({ apiKey, region: regions });
55
+ await writeFile(resolve(output), `${JSON.stringify(catalog, null, 2)}\n`, "utf8");
56
+ console.log(`Wrote ${catalog.metadata.voiceCount} Azure voice definitions to ${resolve(output)}.`);
57
+ }
58
+
59
+ main().catch((error) => {
60
+ console.error(error instanceof Error ? error.message : String(error));
61
+ process.exitCode = 1;
62
+ });
@@ -542,6 +542,7 @@ var ssmlPresets_exports = {};
542
542
  __export(ssmlPresets_exports, {
543
543
  AUDIO_DURATION_DESCRIPTIONS: () => AUDIO_DURATION_DESCRIPTIONS,
544
544
  AUDIO_DURATION_PRESETS: () => AUDIO_DURATION_PRESETS,
545
+ BACKGROUND_AUDIO_DURATION_PRESETS: () => BACKGROUND_AUDIO_DURATION_PRESETS,
545
546
  BREAK_STRENGTH_PRESETS: () => BREAK_STRENGTH_PRESETS,
546
547
  BREAK_TIME_DESCRIPTIONS: () => BREAK_TIME_DESCRIPTIONS,
547
548
  BREAK_TIME_PRESETS: () => BREAK_TIME_PRESETS,
@@ -623,6 +624,9 @@ var AZURE_VOICE_STYLE_MAP = {
623
624
  ],
624
625
  "es-ES-ElviraNeural": [],
625
626
  "fil-PH-AngeloNeural": [],
627
+ "fil-PH-Angelo:DragonHDLatestNeural": [],
628
+ "fil-PH-BlessicaNeural": [],
629
+ "fil-PH-Blessica:DragonHDLatestNeural": [],
626
630
  "fr-FR-DeniseNeural": ["cheerful", "sad"],
627
631
  "fr-FR-HenriNeural": ["cheerful", "sad"],
628
632
  "id-ID-GadisNeural": [],
@@ -900,6 +904,7 @@ var SAY_AS_PRESETS = [
900
904
  var LANGUAGE_PRESETS = ["ja-JP", "en-US", "de-DE", "fr-FR"];
901
905
  var SILENCE_VALUE_PRESETS = ["300ms", "500ms", "1s"];
902
906
  var AUDIO_DURATION_PRESETS = ["5s", "10s", "30s"];
907
+ var BACKGROUND_AUDIO_DURATION_PRESETS = ["0", "500", "1000", "3000", "10000"];
903
908
  var SILENCE_TYPE_PRESETS = [
904
909
  "Leading",
905
910
  "Tailing",
@@ -961,8 +966,8 @@ var SSML_ATTRIBUTE_PRESETS = {
961
966
  },
962
967
  "mstts:backgroundaudio": {
963
968
  volume: PROSODY_VOLUME_PRESETS,
964
- fadein: AUDIO_DURATION_PRESETS,
965
- fadeout: AUDIO_DURATION_PRESETS
969
+ fadein: BACKGROUND_AUDIO_DURATION_PRESETS,
970
+ fadeout: BACKGROUND_AUDIO_DURATION_PRESETS
966
971
  },
967
972
  silence: {
968
973
  type: SILENCE_TYPE_PRESETS,
@@ -2308,6 +2313,9 @@ var AZURE_VOICE_DEFINITIONS = [
2308
2313
  },
2309
2314
  { name: "es-ES-ElviraNeural", locale: "es-ES" },
2310
2315
  { name: "fil-PH-AngeloNeural", locale: "fil-PH" },
2316
+ { name: "fil-PH-Angelo:DragonHDLatestNeural", locale: "fil-PH" },
2317
+ { name: "fil-PH-BlessicaNeural", locale: "fil-PH" },
2318
+ { name: "fil-PH-Blessica:DragonHDLatestNeural", locale: "fil-PH" },
2311
2319
  { name: "fr-FR-DeniseNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
2312
2320
  { name: "fr-FR-HenriNeural", locale: "fr-FR", styles: ["cheerful", "sad"] },
2313
2321
  { name: "id-ID-GadisNeural", locale: "id-ID" },
@@ -2402,6 +2410,18 @@ var ALLOWED_SILENCE_TYPES = /* @__PURE__ */ new Set([
2402
2410
  "Enumerationcomma"
2403
2411
  ]);
2404
2412
  var ALLOWED_VISEME_TYPES = /* @__PURE__ */ new Set(["redlips_front", "FacialExpression"]);
2413
+ var DEFAULT_PREVIEW_TAGS = /* @__PURE__ */ new Set(["mstts:voiceconversion"]);
2414
+ function featureStatusForTag(name, options) {
2415
+ const tagName = canonicalTagName(name);
2416
+ const configured = Object.entries(options.tagStatuses ?? {}).find(
2417
+ ([candidate]) => canonicalTagName(candidate) === tagName
2418
+ )?.[1];
2419
+ if (configured) return configured;
2420
+ if ((options.previewTags ?? [...DEFAULT_PREVIEW_TAGS]).some((candidate) => canonicalTagName(candidate) === tagName))
2421
+ return "preview";
2422
+ if ((options.deprecatedTags ?? []).some((candidate) => canonicalTagName(candidate) === tagName)) return "deprecated";
2423
+ return void 0;
2424
+ }
2405
2425
  function decodeAttribute(value) {
2406
2426
  return value.replace(
2407
2427
  /&(?:amp|apos|gt|lt|quot);/gi,
@@ -2518,9 +2538,9 @@ function isValidAzureAudioDuration(value) {
2518
2538
  return Number(clock[1]) > 0 || Number(clock[2]) > 0 || Number(clock[3]) > 0 || Number(clock[4] ?? 0) > 0;
2519
2539
  }
2520
2540
  function isValidAzureBackgroundAudioDuration(value) {
2521
- const match = /^(\d+(?:\.\d+)?)(ms|s)?$/i.exec(value.trim());
2541
+ const match = /^(\d+)$/.exec(value.trim());
2522
2542
  if (!match) return false;
2523
- const milliseconds = Number(match[1]) * (match[2]?.toLowerCase() === "s" ? 1e3 : 1);
2543
+ const milliseconds = Number(match[1]);
2524
2544
  return Number.isFinite(milliseconds) && milliseconds >= 0 && milliseconds <= 1e4;
2525
2545
  }
2526
2546
  function attr(token, name) {
@@ -2671,6 +2691,25 @@ function validateAudioSource(token, source, diagnostics, options, elementName2)
2671
2691
  }
2672
2692
  function validateElement(token, source, diagnostics, voiceName, options, voiceCatalog) {
2673
2693
  const name = token.name.toLowerCase();
2694
+ const tagStatus = featureStatusForTag(token.name, options);
2695
+ if (tagStatus === "preview")
2696
+ addDiagnostic(
2697
+ diagnostics,
2698
+ source,
2699
+ token.start,
2700
+ `<${token.name}> is an Azure Speech preview feature and may change or require preview access.`,
2701
+ "warning",
2702
+ "azure-preview-tag"
2703
+ );
2704
+ if (tagStatus === "deprecated")
2705
+ addDiagnostic(
2706
+ diagnostics,
2707
+ source,
2708
+ token.start,
2709
+ `<${token.name}> is deprecated by Azure Speech; migrate to a supported alternative.`,
2710
+ "info",
2711
+ "azure-deprecated-tag"
2712
+ );
2674
2713
  if (name === "voice" && !attr(token, "name")?.trim())
2675
2714
  addDiagnostic(diagnostics, source, token.start, '<voice> requires a non-empty "name" attribute.');
2676
2715
  if (name === "break") {
@@ -2882,6 +2921,24 @@ function validateAzureSsml(ssml, options = {}) {
2882
2921
  const name = attr(token, "name")?.trim();
2883
2922
  const language = attr(token, "xml:lang")?.trim() || (speak ? attr(speak, "xml:lang")?.trim() : void 0);
2884
2923
  const definition = name ? voiceCatalog.get(name.toLowerCase()) : void 0;
2924
+ if (name && definition?.status === "preview")
2925
+ addDiagnostic(
2926
+ diagnostics,
2927
+ ssml,
2928
+ token.start,
2929
+ `Voice "${name}" is an Azure Speech preview voice and may change or require preview access.`,
2930
+ "warning",
2931
+ "azure-preview-voice"
2932
+ );
2933
+ if (name && definition?.status === "deprecated")
2934
+ addDiagnostic(
2935
+ diagnostics,
2936
+ ssml,
2937
+ token.start,
2938
+ `Voice "${name}" is deprecated by Azure Speech; migrate to a supported voice.`,
2939
+ "info",
2940
+ "azure-deprecated-voice"
2941
+ );
2885
2942
  if (name && !definition && policySeverity)
2886
2943
  addDiagnostic(
2887
2944
  diagnostics,
@@ -3723,7 +3780,7 @@ var SSML_COMPLETION_SNIPPETS = [
3723
3780
  },
3724
3781
  {
3725
3782
  label: "mstts:backgroundaudio",
3726
- insertText: `<mstts:backgroundaudio src="\${1:https://example.com/audio.mp3}" volume="\${2:-3dB}" />`
3783
+ insertText: `<mstts:backgroundaudio src="\${1:https://example.com/audio.mp3}" volume="\${2:70}" fadein="\${3:1000}" fadeout="\${4:1000}" />`
3727
3784
  },
3728
3785
  {
3729
3786
  label: "mstts:ttsembedding",
@@ -4127,18 +4184,18 @@ var SSML_TAG_DEFINITIONS = [
4127
4184
  },
4128
4185
  {
4129
4186
  name: "volume",
4130
- description: "The background audio volume, for example `-3dB` or `medium`.",
4131
- example: "-3dB"
4187
+ description: "The background audio volume from 0 to 100.",
4188
+ example: "70"
4132
4189
  },
4133
4190
  {
4134
4191
  name: "fadein",
4135
- description: "The fade-in duration, for example `1s`.",
4136
- example: "1s"
4192
+ description: "The fade-in duration in milliseconds from 0 to 10000.",
4193
+ example: "1000"
4137
4194
  },
4138
4195
  {
4139
4196
  name: "fadeout",
4140
- description: "The fade-out duration, for example `500ms`.",
4141
- example: "500ms"
4197
+ description: "The fade-out duration in milliseconds from 0 to 10000.",
4198
+ example: "1000"
4142
4199
  }
4143
4200
  ]
4144
4201
  },
@@ -4508,4 +4565,4 @@ export {
4508
4565
  findSsmlHoverTarget,
4509
4566
  formatSsmlHover
4510
4567
  };
4511
- //# sourceMappingURL=chunk-BYIZQL2W.mjs.map
4568
+ //# sourceMappingURL=chunk-FWWMWI2C.mjs.map