Tts Voiceover
MicrosoftOfficialPublished by Microsoft
- Skill
- Tool integration
- Automation and integrations
- MIT
- Low risk
- Has scripts
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill automates the creation of synthetic voice-over audio for slide presentations. It processes speaker notes defined in YAML files, applies custom acronym pronunciations via an SSML-based lexicon, and utilizes Azure AI Speech neural voices to generate individual WAV files for each slide. The tool supports both key-based and Microsoft Entra ID authentication and includes a dry-run mode for verifying SSML templates without incurring synthesis costs.
What it does
- Extracts speaker notes from YAML files within a structured directory.
- Generates WAV audio files using Azure AI Speech neural voices.
- Applies custom pronunciation aliases for acronyms and technical terms.
- Supports SSML template verification through a dry-run mode.
- Allows configuration of speech rate and specific voice selection.
- Enables targeted synthesis for individual slides or entire decks.
When to use it
- When you need to generate consistent synthetic narration for slide-based presentations.
- When you require precise control over the pronunciation of technical acronyms in audio.
- When integrating automated voice-over generation into a slide content pipeline.
- When you need to verify SSML output before committing to full audio synthesis.
Advantages
- Automates the conversion of text-based speaker notes into audio assets.
- Provides granular control over pronunciation using custom lexicon files.
- Supports secure authentication via Microsoft Entra ID.
- Offers flexibility to re-synthesize specific slides without processing the entire deck.
Usage examples
- “Generate voice-over files for all slides in the content directory.”
- The script processes all slide-NNN folders in the content directory and outputs corresponding WAV files to the voice-over folder.
- “Verify the SSML templates for my slides without generating audio.”
- The tool runs in dry-run mode, printing the generated SSML to the console for review without calling the Azure Speech service.
- “Synthesize audio for slide 005 using a specific voice and rate.”
- The tool generates a WAV file for slide-005 using the specified Azure neural voice and prosody rate settings.
Install
Download the skill as a ZIP:
https://mp-staging.autogineer.com/agent-skills/microsoft/tts-voiceover/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip tts-voiceover.zip -d .agents/skills
Documentation by Microsoft
TTS Voice Over Skill
Generates per-slide WAV voice-over files from YAML speaker_notes using Azure AI Speech neural voices with SSML pronunciation control.
Overview
This skill reads content.yaml files from a slide content directory (slide-NNN/content.yaml), extracts speaker_notes fields, applies acronym aliases for correct pronunciation of technical terms, and produces one WAV file per slide. Supports dry-run mode for SSML template verification without Azure credentials.
Synthesis uses Azure AI Speech neural voices, including the HD voices such as en-US-Andrew:DragonHDLatestNeural. HD voices are offered in a subset of Azure regions; check the Speech service regions before choosing SPEECH_REGION.
Narration produced by this skill is synthetic. Tell listeners that the voice is AI-generated, following Microsoft's disclosure design guidelines for synthetic voices. HVE demo-material CI does not synthesize speech; it generates silent videos.
Prerequisites
Azure Speech resource: see Azure AI Speech pricing for the free-tier allowance and HD voice rates.
Authentication: Microsoft Entra ID (
SPEECH_RESOURCE_ID, recommended) or a resource key (SPEECH_KEY).Region:
SPEECH_REGIONis required for synthesis and has no default. Dry-run mode does not need it.Python 3.11+ with
uvfor virtual environment management.Data handling note: Speaker-notes content is transmitted to the configured
SPEECH_REGIONfor synthesis. For prebuilt neural voices, Microsoft states that neither the input text nor the output audio is stored in Microsoft logs; see Data, privacy, and security for text to speech. Operators must still set an approved region and avoid sending regulated or confidential narration.
Microsoft Entra ID Auth (Recommended)
Entra ID avoids storing a long-lived key. It requires a custom domain on the Speech resource and the Cognitive Services Speech User role for the signed-in identity. The script resolves the identity through DefaultAzureCredential, so an az login session, a managed identity, or a GitHub Actions OIDC login through azure/login all work.
export SPEECH_RESOURCE_ID="/subscriptions/.../Microsoft.CognitiveServices/accounts/your-resource"
export SPEECH_REGION="eastus"Key-Based Auth
export SPEECH_KEY="your-speech-key"
export SPEECH_REGION="eastus"When both SPEECH_KEY and SPEECH_RESOURCE_ID are set, the script warns and uses the key. Unset SPEECH_KEY to use Entra ID.
Install dependencies:
# run from this skill folder
uv syncQuick Start
Verify SSML templates without generating audio:
uv run scripts/generate_voiceover.py --dry-run --content-dir path/to/contentGenerate voice-over WAV files:
uv run scripts/generate_voiceover.py --content-dir path/to/content --output-dir voice-overUse demo-video to combine the WAV files with approved frames or clips.
Use hve-slides for HTML presentations and hve-demo-material for the
HTML/frame/MP4/caption workflow. Audio embedding into presentation files is retired.
Parameters Reference
For different speakers, synthesize one voice's selected slides per invocation
using repeated --slide <number> arguments. Only those canonical slide-NNN
folders are read and their WAVs replaced; other speakers' outputs are untouched.
Omit --slide to synthesize the whole deck.
generate_voiceover.py
| Parameter | Type | Default | Description |
|---|---|---|---|
--dry-run | flag | false | Print SSML without generating audio |
--voice | string | en-US-Andrew:DragonHDLatestNeural | Azure AI Speech voice name |
--rate | string | +10% | Speech prosody rate |
--content-dir | path | content | Path to slide content directory |
--output-dir | path | voice-over | Path to WAV output directory |
--slide | integer | all slides | Repeat to synthesize only the selected canonical slide numbers |
--lexicon | path | (auto-detect) | Custom acronyms.yaml path |
--collapse-newlines | flag | false | Collapse newlines and whitespace runs in speaker notes into single spaces before synthesis |
--verbose / -v | flag | false | Enable verbose (DEBUG) logging output |
Script Reference
Generate with custom voice and rate:
uv run scripts/generate_voiceover.py \
--content-dir content \
--output-dir voice-over \
--voice "en-US-Jenny:DragonHDLatestNeural" \
--rate "+5%"Use a custom lexicon:
uv run scripts/generate_voiceover.py \
--content-dir content \
--lexicon custom-acronyms.yamlCollapse newlines in speaker notes (recommended for block-scalar | notes,
whose line breaks are otherwise spoken as pauses):
uv run scripts/generate_voiceover.py \
--content-dir content \
--collapse-newlinesAcronym Lexicon
The lexicon controls SSML <sub alias> replacements for acronyms and technical terms. Create an acronyms.yaml file:
acronyms:
HVE-Core: "H V E Core"
OWASP: "Oh wasp"
SBOM: "S Bomb"
SLSA: "Salsa"
CI/CD: "C I C D"Lexicon resolution order:
Path specified via
--lexiconargument.acronyms.yamlin the content directory.Built-in defaults covering common technical acronyms.
SSML Template
Each slide produces an SSML document:
<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
xmlns:mstts="http://www.w3.org/2001/mstts" xml:lang="en-US">
<voice name="en-US-Andrew:DragonHDLatestNeural">
<prosody rate="+10%">
Text with <sub alias="Oh wasp">OWASP</sub> aliases applied.
</prosody>
</voice>
</speak>Content Directory Contract
This skill reads a slide content directory with this structure:
content/
├── slide-001/
│ └── content.yaml # Must include speaker_notes: field
├── slide-002/
│ └── content.yaml
└── ...Each content.yaml should contain a speaker_notes: field with the narration text. The generated WAV files are named slide-NNN.wav matching the directory names.
Troubleshooting
| Issue | Solution |
|---|---|
Set SPEECH_KEY ... or SPEECH_RESOURCE_ID | Export SPEECH_KEY (key auth) or SPEECH_RESOURCE_ID (Entra ID) with SPEECH_REGION. |
SPEECH_REGION must be set | Export SPEECH_REGION with an approved Azure region. Synthesis has no default region. |
| 401 with Entra ID auth | Verify custom domain on the Speech resource and Cognitive Services Speech User role. RBAC propagation takes up to 5 minutes. |
| Empty WAV files or skipped slides | Verify speaker_notes: is present and non-empty in content.yaml. |
| Mispronounced acronyms | Add entries to acronyms.yaml with phonetic aliases. |
azure-cognitiveservices-speech package is required | Run uv sync in the skill directory. |
Files
- ATTRIBUTION.mdAdded by AutoGineer874 B
- LICENSELicence file added by AutoGineer1.1 KiB
- SECURITY.md20.1 KiB
- SKILL.md9.0 KiB
- pip-audit-known-vulnerabilities.txt260 B
- pyproject.toml1.6 KiB
- scripts/Invoke-GenerateVoiceover.ps1Script4.3 KiB
- scripts/Modules/TtsVoiceoverHelpers.psm1Script2.1 KiB
- scripts/generate-voiceover.shScript2.8 KiB
- scripts/generate_voiceover.pyScript16.8 KiB
- tests/Invoke-GenerateVoiceover.Tests.ps1Script2.3 KiB
- tests/TtsVoiceoverHelpers.Tests.ps1Script4.9 KiB
- tests/conftest.pyScript7.2 KiB
- tests/corpus/0_acronym_text19 B
- tests/corpus/0_empty0 B
- tests/corpus/0_ssml_fragment29 B
- tests/corpus/0_valid_yaml25 B
- tests/fuzz_harness.pyScript6.0 KiB
- tests/test_generate_voiceover.pyScript10.5 KiB
- tests/test_generate_voiceover_boundaries.pyScript9.1 KiB
- uv.lock144.1 KiB
SECURITY.md
title: TTS Voice-Over Skill Security Model description: STRIDE threat model for the tts-voiceover skill organized by assets, adversaries, and trust buckets (CLI to Azure Speech, environment/Entra credentials, untrusted content inputs, CLI caller process) with in-code mitigations and acknowledged enterprise readiness gaps author: microsoft/hve-core ms.date: 2026-10-28 ms.topic: reference estimated_reading_time: 10 keywords:
security
STRIDE
tts-voiceover
azure speech
threat model
<!-- markdownlint-disable-file -->
TTS Voice-Over Skill Security Model
This document records the STRIDE threat model for the tts-voiceover skill (scripts/generate_voiceover.py and its wrappers). The model is organized by trust bucket: CLI → Azure Speech API (B1), Environment and Entra credentials (B2), Untrusted content inputs (B3), and CLI caller process and filesystem (B4). Each bucket enumerates all six STRIDE categories. Presentation-file parsing and audio embedding are retired; WAV synthesis remains.
The skill reads content.yaml speaker notes, escapes them into SSML, and synthesizes one WAV per slide through the Azure Cognitive Services Speech SDK over TLS. It runs no local listener and persists no credentials to disk; credentials are read from the process environment (or resolved through DefaultAzureCredential) per invocation.
See also: repo-wide STRIDE model. This skill participates in the repository-wide threat model at
docs/security/security-model.mdand is registered in its Skill Security Models section.
Executive Summary
The skill synthesizes narration by sending speaker notes to Azure Speech over TLS. Its highest-risk behavior remains content egress to the configured region without a data-classification gate. Credentials are read per invocation, never persisted; YAML uses safe_load and dynamic SSML values are escaped. Synthesis requires an explicit SPEECH_REGION. Retirement removes presentation/XML archive parsing and its lxml/Pillow dependencies, not speech authentication or disclosure risk.
Security Posture Overview
| Dimension | Value |
|---|---|
| Runtime surface | Python CLI; Azure Speech SDK (TLS); YAML-to-SSML and WAV output; no local listener |
| Trust buckets | B1 CLI→Azure Speech, B2 env/Entra credentials, B3 untrusted inputs, B4 caller |
| Credentials | Entra token via DefaultAzureCredential or SPEECH_KEY; never persisted to disk |
| Network egress | HTTPS to the configured Azure Speech region endpoint |
| Open residual gaps | 4 (InfoDisc-Med: speaker-notes content egress to the Azure region) |
Contents
System Description
Trust Boundaries
Assets
Adversaries
Bucket B1: CLI → Azure Speech API
Bucket B2: Environment and Entra credentials
Bucket B3: Untrusted content inputs
Bucket B4: CLI caller process and filesystem
Enterprise Readiness Gaps
References
System Description
Components
scripts/generate_voiceover.py— readscontent.yaml, escapes speaker notes into SSML, and synthesizes one WAV per slide via the Azure Speech SDK.Bash/PowerShell wrappers manage the existing skill environment and invoke synthesis.
Credential resolution —
SPEECH_KEYfrom the environment, or an Entra token minted byDefaultAzureCredential.
Data Flow
flowchart TD
subgraph HOST["Operator Workstation / Runner (trust zone)"]
GEN["generate_voiceover.py"]
CRED["SPEECH_KEY env /<br/>DefaultAzureCredential"]
OUTW["Per-slide WAV files"]
end
subgraph INPUT["Inputs (operator-supplied, may be upstream-generated)"]
YAML["content.yaml / lexicon"]
end
subgraph AZURE["Azure Speech (network boundary)"]
SPEECH["Speech endpoint (region)"]
end
YAML -->|"parsed (safe_load), escaped to SSML"| GEN
GEN -->|"reads"| CRED
GEN -->|"SSML synthesis request (TLS)"| SPEECH
SPEECH -->|"WAV audio"| GEN
GEN -->|"writes"| OUTWTrust Boundaries
Boundary Diagram
┌───────────────────────────────────────────────────────────────┐
│ TRUST BOUNDARY: Operator Workstation / Runner │
│ ┌────────────────────┐ ┌──────────────┐ ┌───────────────┐ │
│ │ generate_voiceover │ │ Credentials │ │ WAV outputs │ │
│ │ + wrappers │ │ (env/Entra) │ │ │ │
│ └────────────────────┘ └──────────────┘ └───────────────┘ │
└───────────────┬─────────────────────────┬─────────────────────┘
│ TLS │ parse (no egress)
┌─────────────▼──────────────┐ ┌────────▼─────────────────────┐
│ BOUNDARY: Azure Speech │ │ BOUNDARY: Inputs (untrusted) │
│ Speech endpoint (region) │ │ content.yaml / lexicon │
└────────────────────────────┘ └──────────────────────────────┘Boundary Descriptions
| Boundary | Assets Protected | Controls Enforced |
|---|---|---|
| Operator Workstation / Runner | Credentials, output files | Per-invocation credential resolution; no disk persistence; partial WAV cleanup |
| Azure Speech | Synthesis request integrity, bearer token | TLS via SDK (system trust store); credentials sent only to the SDK |
| Inputs | Host process integrity | yaml.safe_load; SSML XML-escaping and quoteattr |
Assets
| Id | Asset | Lifetime | Notes |
|---|---|---|---|
| A1 | SPEECH_KEY subscription key | Operator-managed | Read from SPEECH_KEY env at invocation. Passed to the Speech SDK and sent to the Azure region endpoint over TLS. |
| A2 | Entra ID access token | Command lifetime | Minted by DefaultAzureCredential for https://cognitiveservices.azure.com/.default; embedded as aad#{resource_id}#{token} and refreshed near expiry. |
| A3 | Speaker-notes content | Command lifetime | Read from content.yaml; leaves the trust boundary to the Azure Speech endpoint for synthesis. May contain confidential narration. |
| A4 | Lexicon YAML | Command lifetime | Operator-supplied data parsed through PyYAML |
| A5 | Output WAV files | Command lifetime | Written to the operator-chosen output directory |
Adversaries
| Id | Adversary | In-scope mitigations |
|---|---|---|
| ADV-a | Same-uid malware on the operator workstation | Not defended. A process running as the operator can read SPEECH_KEY from the environment or invoke the same credential chain. Workstation hygiene is the controlling defense. |
| ADV-b | Network attacker on the CLI ↔ Azure Speech channel | TLS provided by the Azure Speech SDK with system-trust-store certificate validation. The skill performs no plaintext fallback. |
| ADV-c | Hostile or malformed content.yaml / lexicon | yaml.safe_load (no arbitrary object construction); speaker notes XML-escaped via xml.sax.saxutils.escape; voice/rate/acronym aliases via quoteattr; XML-special acronym keys warned and skipped. |
| ADV-e | Hostile caller process controlling argv / env | Argument paths constrained to declared options; partial WAV files removed on synthesis failure |
Bucket B1: CLI → Azure Speech API
Spoofing
Transport security is delegated to the Azure Speech SDK, which validates the endpoint certificate against the system trust store. The skill constructs no raw HTTP requests and performs no redirect handling of its own.
Tampering
TLS protects the SSML request and the synthesized audio response in transit; the skill performs no plaintext fallback.
Repudiation
The CLI returns deterministic exit codes (
EXIT_SUCCESS/EXIT_FAILURE/EXIT_ERROR) and logs per-slide synthesis outcomes so automation can attribute failures.
Information Disclosure
SPEECH_KEYand the Entra token are passed only to the SDK and are never written to logs. Synthesis failures log onlycancellation.reasonanderror_details, not the credential.Speaker-notes content (A3) leaves the trust boundary to the Azure region for synthesis. There is no data-classification gate (G-INF-1).
Denial of Service
Token-refresh failures are caught and logged; the previous token is retained rather than crashing mid-deck, so a transient credential-service hiccup does not abort a long run.
Elevation of Privilege
Not applicable. The skill requests only synthesis; it performs no privilege transition and the endpoint scope is limited to Cognitive Services.
Risk Rating
| Threat | Likelihood | Impact | Residual Risk | Status |
|---|---|---|---|---|
| Speaker-notes content egress to Azure region | Med | Med | Med | By design (G-INF-1) |
| Credential leakage into logs | Low | High | Low | Mitigated |
Bucket B2: Environment and Entra credentials
Credentials are resolved per invocation. SPEECH_KEY is read from the environment; otherwise SPEECH_RESOURCE_ID triggers DefaultAzureCredential, which mints a short-lived token refreshed roughly five minutes before expiry. Nothing is persisted to disk.
Spoofing
When both
SPEECH_KEYandSPEECH_RESOURCE_IDare set, the skill warns and prefers key auth deterministically rather than silently choosing, so the active credential is unambiguous.
Tampering
Not applicable. Credentials are read into memory per invocation and never written back, so there is no at-rest credential state to tamper with.
Repudiation
Not applicable. Credential acquisition emits no skill-level audit record beyond the Azure SDK's own diagnostics.
Information Disclosure
Short-lived Entra tokens are preferred over long-lived keys where the resource supports a custom domain and role assignment. Nothing is persisted to disk; credentials inherit whatever protection the process environment provides.
Denial of Service
Token-refresh failures are caught and logged; the previously acquired token is retained rather than aborting the run.
Elevation of Privilege
DefaultAzureCredentialwalks a broad credential chain (env, managed identity, Azure CLI, and more). In CI it may bind an unintended identity (G-EOP-1).
Risk Rating
| Threat | Likelihood | Impact | Residual Risk | Status |
|---|---|---|---|---|
| Broad credential chain binds unintended identity | Low | Med | Low | Partially Mitigated (G-EOP-1) |
Bucket B3: Untrusted content inputs
Spoofing
Not applicable. Input content is parsed as data; no identity is derived from it.
Tampering
SSML injection is mitigated: all dynamic values inserted into the SSML document — speaker notes, voice name, prosody rate, and acronym alias/replacement text — are XML-escaped or attribute-quoted before assembly. A single-pass regex prevents acronym substitution from corrupting previously inserted markup.
Repudiation
Not applicable. No attribution is claimed over input content.
Information Disclosure
Not applicable. The skill does not extract or forward secrets from input content; speaker-notes egress is covered under B1.
Denial of Service
YAML inputs are parsed with
yaml.safe_load; malformed slides are skipped with a warning rather than aborting the whole deck.
Elevation of Privilege
YAML is parsed as data through
safe_load; speaker notes and aliases are escaped before SSML assembly. The retired presentation parser introduces no remaining executable or archive-input surface.
Risk Rating
| Threat | Likelihood | Impact | Residual Risk | Status |
|---|---|---|---|---|
| SSML injection via speaker notes / aliases | Low | Med | Low | Mitigated (escape / quoteattr) |
Bucket B4: CLI caller process and filesystem
The caller controls argv, environment, stdin, stdout, and stderr; the CLI treats that process as operator-controlled.
Spoofing
Not applicable. The CLI runs as the invoking OS user and trusts the caller's argv and environment.
Tampering
Argument paths are constrained to declared options. The caller selects the WAV output directory and is responsible for preserving existing outputs before intentional regeneration.
Repudiation
The CLI returns deterministic exit codes so automation can attribute outcomes to the invoking step.
Information Disclosure
Nothing is persisted to disk beyond the requested outputs; no credentials are written.
Denial of Service
Partial WAV files left by a failed synthesis are removed rather than presented as completed narration.
Elevation of Privilege
Output directories are created with default permissions; the skill performs no privileged operation.
Risk Rating
| Threat | Likelihood | Impact | Residual Risk | Status |
|---|---|---|---|---|
| Replacement of existing WAV output | Low | Low | Low | Operator-controlled regeneration |
| Corrupt partial WAV delivered | Low | Low | Low | Mitigated by cleanup on failure |
Enterprise Readiness Gaps
The following are known limitations recorded so operators can make informed deployment decisions. Severity ratings are the project's own assessment and are not equivalent to a CVSS score.
| Id | Gap | Severity | Status |
|---|---|---|---|
| G-INF-1 | Speaker-notes content is transmitted to the configured Azure Speech region for synthesis. There is no data-classification gate; confidential narration leaves the boundary (data-dependent severity). (audit: T-INF-1) | InfoDisc-Med | By design; SPEECH_REGION is required for Azure synthesis (no default), and operators must set it to an approved region and avoid sending regulated content. |
| G-EOP-1 | DefaultAzureCredential walks a broad credential chain (env, managed identity, Azure CLI, and more). In CI it may bind an unintended identity. (audit: T-IAM-1) | EoP-Low | Prefer a scoped SPEECH_KEY or an explicit credential on shared runners. |
| G-TLS-1 | No certificate pinning for the Azure Speech endpoint; TLS validation depends on the SDK and the system trust store. (audit: T-TLS-1) | InfoDisc-Low | Operator-acceptable for a managed Azure endpoint. |
| G-SUP-1 | Speech SDK, identity and YAML dependencies remain floor-pinned and lockfile hash-pinned. (audit: T-SUP-1) | SupplyChain-Med | Monitor retained dependency advisories; presentation-only packages were removed by the resolver. |
| G-TAM-1 | Presentation timing/XML parsing surface was retired with audio embedding. (audit: T-TAM-1) | Tampering-Low | Retired surface; retained for audit history, not an available feature. |
For an active issue tracker entry covering these gaps, see the hve-core issues list.
References
Repository security model
🤖 Crafted with precision by ✨Copilot following brilliant human instruction, then carefully refined by our team of discerning human reviewers.
Requirements and compatibility
Written for: GitHub Copilot
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Needs: Needs Python
Security
What our scanner noticed· Low risk
- Scripts that your agent may run
This skill includes scripts that your agent may run. Read them before you use it.
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- Published by
- Microsoft
- Publisher
- Microsoft (Official)
- Source
- microsoft/hve-core · .github/skills/experimental/tts-voiceover · commit f425e94
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Microsoft.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download tts-voiceover.zip