Generated banner for Tts Voiceover
Generated logo for Tts Voiceover

Tts Voiceover

MicrosoftOfficialPublished by Microsoft

  • Skill
  • Tool integration
  • Automation and integrations
  • MIT
  • Low risk
  • Has scripts

From the author

Text-to-speech voice-over generation from YAML speaker notes using Azure AI Speech neural voices with SSML pronunciation control
— the description by Microsoft

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

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/microsoft/tts-voiceover/download
  2. Unzip 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_REGION is required for synthesis and has no default. Dry-run mode does not need it.

  • Python 3.11+ with uv for virtual environment management.

  • Data handling note: Speaker-notes content is transmitted to the configured SPEECH_REGION for 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.

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 sync

Quick Start

Verify SSML templates without generating audio:

uv run scripts/generate_voiceover.py --dry-run --content-dir path/to/content

Generate voice-over WAV files:

uv run scripts/generate_voiceover.py --content-dir path/to/content --output-dir voice-over

Use 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
ParameterTypeDefaultDescription
--dry-runflagfalsePrint SSML without generating audio
--voicestringen-US-Andrew:DragonHDLatestNeuralAzure AI Speech voice name
--ratestring+10%Speech prosody rate
--content-dirpathcontentPath to slide content directory
--output-dirpathvoice-overPath to WAV output directory
--slideintegerall slidesRepeat to synthesize only the selected canonical slide numbers
--lexiconpath(auto-detect)Custom acronyms.yaml path
--collapse-newlinesflagfalseCollapse newlines and whitespace runs in speaker notes into single spaces before synthesis
--verbose / -vflagfalseEnable 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.yaml

Collapse 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-newlines

Acronym 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:

  1. Path specified via --lexicon argument.

  2. acronyms.yaml in the content directory.

  3. 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

IssueSolution
Set SPEECH_KEY ... or SPEECH_RESOURCE_IDExport SPEECH_KEY (key auth) or SPEECH_RESOURCE_ID (Entra ID) with SPEECH_REGION.
SPEECH_REGION must be setExport SPEECH_REGION with an approved Azure region. Synthesis has no default region.
401 with Entra ID authVerify custom domain on the Speech resource and Cognitive Services Speech User role. RBAC propagation takes up to 5 minutes.
Empty WAV files or skipped slidesVerify speaker_notes: is present and non-empty in content.yaml.
Mispronounced acronymsAdd entries to acronyms.yaml with phonetic aliases.
azure-cognitiveservices-speech package is requiredRun uv sync in the skill directory.

Files

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.md and 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
DimensionValue
Runtime surfacePython CLI; Azure Speech SDK (TLS); YAML-to-SSML and WAV output; no local listener
Trust bucketsB1 CLI→Azure Speech, B2 env/Entra credentials, B3 untrusted inputs, B4 caller
CredentialsEntra token via DefaultAzureCredential or SPEECH_KEY; never persisted to disk
Network egressHTTPS to the configured Azure Speech region endpoint
Open residual gaps4 (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
  1. scripts/generate_voiceover.py — reads content.yaml, escapes speaker notes into SSML, and synthesizes one WAV per slide via the Azure Speech SDK.

  2. Bash/PowerShell wrappers manage the existing skill environment and invoke synthesis.

  3. Credential resolution — SPEECH_KEY from the environment, or an Entra token minted by DefaultAzureCredential.

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"| OUTW

Trust 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
BoundaryAssets ProtectedControls Enforced
Operator Workstation / RunnerCredentials, output filesPer-invocation credential resolution; no disk persistence; partial WAV cleanup
Azure SpeechSynthesis request integrity, bearer tokenTLS via SDK (system trust store); credentials sent only to the SDK
InputsHost process integrityyaml.safe_load; SSML XML-escaping and quoteattr

Assets

IdAssetLifetimeNotes
A1SPEECH_KEY subscription keyOperator-managedRead from SPEECH_KEY env at invocation. Passed to the Speech SDK and sent to the Azure region endpoint over TLS.
A2Entra ID access tokenCommand lifetimeMinted by DefaultAzureCredential for https://cognitiveservices.azure.com/.default; embedded as aad#{resource_id}#{token} and refreshed near expiry.
A3Speaker-notes contentCommand lifetimeRead from content.yaml; leaves the trust boundary to the Azure Speech endpoint for synthesis. May contain confidential narration.
A4Lexicon YAMLCommand lifetimeOperator-supplied data parsed through PyYAML
A5Output WAV filesCommand lifetimeWritten to the operator-chosen output directory

Adversaries

IdAdversaryIn-scope mitigations
ADV-aSame-uid malware on the operator workstationNot 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-bNetwork attacker on the CLI ↔ Azure Speech channelTLS provided by the Azure Speech SDK with system-trust-store certificate validation. The skill performs no plaintext fallback.
ADV-cHostile or malformed content.yaml / lexiconyaml.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-eHostile caller process controlling argv / envArgument 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_KEY and the Entra token are passed only to the SDK and are never written to logs. Synthesis failures log only cancellation.reason and error_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
ThreatLikelihoodImpactResidual RiskStatus
Speaker-notes content egress to Azure regionMedMedMedBy design (G-INF-1)
Credential leakage into logsLowHighLowMitigated

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_KEY and SPEECH_RESOURCE_ID are 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
  • DefaultAzureCredential walks a broad credential chain (env, managed identity, Azure CLI, and more). In CI it may bind an unintended identity (G-EOP-1).

Risk Rating
ThreatLikelihoodImpactResidual RiskStatus
Broad credential chain binds unintended identityLowMedLowPartially 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
ThreatLikelihoodImpactResidual RiskStatus
SSML injection via speaker notes / aliasesLowMedLowMitigated (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
ThreatLikelihoodImpactResidual RiskStatus
Replacement of existing WAV outputLowLowLowOperator-controlled regeneration
Corrupt partial WAV deliveredLowLowLowMitigated 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.

IdGapSeverityStatus
G-INF-1Speaker-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-MedBy 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-1DefaultAzureCredential 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-LowPrefer a scoped SPEECH_KEY or an explicit credential on shared runners.
G-TLS-1No certificate pinning for the Azure Speech endpoint; TLS validation depends on the SDK and the system trust store. (audit: T-TLS-1)InfoDisc-LowOperator-acceptable for a managed Azure endpoint.
G-SUP-1Speech SDK, identity and YAML dependencies remain floor-pinned and lockfile hash-pinned. (audit: T-SUP-1)SupplyChain-MedMonitor retained dependency advisories; presentation-only packages were removed by the resolver.
G-TAM-1Presentation timing/XML parsing surface was retired with audio embedding. (audit: T-TAM-1)Tampering-LowRetired 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

🤖 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 or Microsoft.

Download ZIP

The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download tts-voiceover.zip