关于 tts-voiceover
tts-voiceover 从 PowerPoint 内容目录的 YAML speaker_notes 生成逐页 WAV 旁白,并可将音频嵌入 PPTX。它面向需要为幻灯片制作合成讲解音频的团队,支持 SSML 发音控制和无需凭据的预演模式。
主要功能
- 01从 speaker_notes 生成逐页 WAV
- 02使用 Azure AI Speech 神经语音
- 03通过 SSML 别名控制术语发音
- 04支持无凭据 dry-run 验证
- 05将旁白音频嵌入 PPTX
适用场景
- 01为演示文稿生成逐页旁白
- 02预览 SSML 模板而不生成音频
- 03为技术缩写配置发音别名
- 04为 PPTX 添加旁白和播放计时
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ microsoft/ hve-core --skill tts-voiceover安装后在 Claude Code 中输入/tts-voiceover确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
pip-audit-known-vulnerabilities.txtpyproject.tomlSECURITY.mduv.lockscripts/embed_audio.pyembed-audio.shgenerate_voiceover.pygenerate-voiceover.shInvoke-EmbedAudio.ps1Invoke-GenerateVoiceover.ps1scripts/Modules/TtsVoiceoverHelpers.psm1tests/conftest.pyfuzz_harness.pyInvoke-GenerateVoiceover.Tests.ps1test_embed_audio_integrity.pytest_embed_audio.pytest_generate_voiceover_boundaries.pytest_generate_voiceover.pyTtsVoiceoverHelpers.Tests.ps1tests/corpus/0_acronym_text0_empty0_ssml_fragment0_valid_yaml详细介绍
TTS 旁白技能
使用 Azure AI Speech 神经语音,根据 YAML speaker_notes 生成每张幻灯片对应的 WAV 旁白文件,并通过 SSML 控制发音。
概述
此技能会读取 PowerPoint 技能内容目录中的 content.yaml 文件,提取 speaker_notes 字段,为缩略词添加别名以确保技术术语发音正确,并为每张幻灯片生成一个 WAV 文件。支持试运行模式,可在无需 Azure 凭据的情况下验证 SSML 模板。
语音合成使用 Azure AI Speech 神经语音,包括 en-US-Andrew:DragonHDLatestNeural 等 HD 语音。HD 语音仅在部分 Azure 区域提供;选择 SPEECH_REGION 前,请查看语音服务区域。
此技能生成的旁白为合成语音。请告知听众该语音由 AI 生成,并遵循 Microsoft 的合成语音披露设计指南。HVE 演示材料 CI 不会合成语音,而是生成静音视频。
前置条件
- Azure Speech 资源:有关免费层额度和 HD 语音费率,请参阅 Azure AI Speech 定价。
- 身份验证:Microsoft Entra ID(
SPEECH_RESOURCE_ID,推荐)或资源密钥(SPEECH_KEY)。 - 区域:合成时必须设置
SPEECH_REGION,且没有默认值。试运行模式无需设置。 - Python 3.11+,并使用
uv管理虚拟环境。 - 数据处理说明:旁白备注内容会传输到配置的
SPEECH_REGION进行合成。对于预生成的神经语音,Microsoft 声明输入文本和输出音频都不会存储在 Microsoft 日志中;请参阅文本转语音的数据、隐私和安全。运营人员仍须设置获批的区域,并避免发送受监管或机密的旁白内容。
Microsoft Entra ID 身份验证(推荐)
Entra ID 无需存储长期有效的密钥。此方式要求 Speech 资源具有自定义域,并为已登录的身份分配 Cognitive Services Speech User 角色。脚本通过 DefaultAzureCredential 解析身份,因此 az login 会话、托管身份或通过 azure/login 进行的 GitHub Actions OIDC 登录均可使用。
export SPEECH_RESOURCE_ID="/subscriptions/.../Microsoft.CognitiveServices/accounts/your-resource"
export SPEECH_REGION="eastus"
基于密钥的身份验证
export SPEECH_KEY="your-speech-key"
export SPEECH_REGION="eastus"
如果同时设置了 SPEECH_KEY 和 SPEECH_RESOURCE_ID,脚本会发出警告并使用密钥。取消设置 SPEECH_KEY 即可使用 Entra ID。
安装依赖项:
# run from this skill folder
uv sync
快速入门
无需生成音频即可验证 SSML 模板:
uv run scripts/generate_voiceover.py --dry-run --content-dir path/to/content
生成旁白 WAV 文件:
uv run scripts/generate_voiceover.py --content-dir path/to/content --output-dir voice-over
将音频嵌入 PPTX 演示文稿:
uv run scripts/embed_audio.py --input deck.pptx --audio-dir voice-over --output deck-narrated.pptx
参数参考
对于不同的演讲者,可在每次调用时使用重复的 --slide <number> 参数,为一位演讲者合成指定幻灯片的语音。只会读取这些规范的 slide-NNN 文件夹并替换其中的 WAV 文件;其他演讲者的输出不受影响。
省略 --slide 可合成整套演示文稿。
generate_voiceover.py
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--dry-run | 标志 | false | 打印 SSML,不生成音频 |
--voice | 字符串 | en-US-Andrew:DragonHDLatestNeural | Azure AI Speech 语音名称 |
--rate | 字符串 | +10% | 语音韵律速率 |
--content-dir | 路径 | content | 幻灯片内容目录的路径 |
--output-dir | 路径 | voice-over | WAV 输出目录的路径 |
--slide | 整数 | 所有幻灯片 | 可重复指定,仅合成所选的规范幻灯片编号 |
--lexicon | 路径 | (自动检测) | 自定义 acronyms.yaml 路径 |
--collapse-newlines | 标志 | false | 合成前,将演讲者备注中的换行和连续空白折叠为单个空格 |
--verbose / -v | 标志 | false | 启用详细(DEBUG)日志输出 |
embed_audio.py
将 WAV 文件嵌入到对应的 PPTX 幻灯片中,并添加旁白时间 XML, 使 PowerPoint 能够通过 文件 > 导出 > 创建视频 > 使用录制的计时和旁白 识别音频,以便导出视频。
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
--input | 路径 | (必需) | 源 PPTX 文件路径 |
--audio-dir | 路径 | voice-over | 包含 slide-NNN.wav 的目录 |
--output | 路径 | *-narrated.pptx | 输出 PPTX 文件路径 |
--verbose / -v | 标志 | false | 启用详细(DEBUG)日志输出 |
每个 WAV 文件根据其名称中的编号映射到一张幻灯片,因此 slide-1.wav 和 slide-001.wav 都会映射到第 1 张幻灯片。如果两个文件映射到同一张幻灯片,嵌入过程会报错并停止,且不会写入输出文件。编号超过最后一张幻灯片的 WAV 文件会被忽略,并显示警告。如果 WAV 文件无法读取,系统会报告该文件,其对应的幻灯片保持不变,同时继续嵌入其余幻灯片。
脚本参考
使用自定义语音和速率生成:
uv run scripts/generate_voiceover.py \
--content-dir content \
--output-dir voice-over \
--voice "en-US-Jenny:DragonHDLatestNeural" \
--rate "+5%"
使用自定义词典:
uv run scripts/generate_voiceover.py \
--content-dir content \
--lexicon custom-acronyms.yaml
折叠演讲者备注中的换行(建议用于块标量 | 备注,否则换行会被读作停顿):
uv run scripts/generate_voiceover.py \
--content-dir content \
--collapse-newlines
嵌入生成的音频:
uv run scripts/embed_audio.py \
--input slide-deck/presentation.pptx \
--audio-dir voice-over \
--output slide-deck/presentation-narrated.pptx
缩略词词典
词典用于控制缩略词和技术术语的 SSML <sub alias> 替换。创建一个 acronyms.yaml 文件:
acronyms:
HVE-Core: "H V E Core"
OWASP: "Oh wasp"
SBOM: "S Bomb"
SLSA: "Salsa"
CI/CD: "C I C D"
词典解析顺序:
- 通过
--lexicon参数指定的路径。 - 内容目录中的
acronyms.yaml。 - 内置的常见技术缩略词默认值。
SSML 模板
每张幻灯片都会生成一份 SSML 文档:
<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>
与 PowerPoint Skill 集成
此 Skill 会读取 PowerPoint Skill 的内容目录结构:
content/
├── slide-001/
│ └── content.yaml # Must include speaker_notes: field
├── slide-002/
│ └── content.yaml
└── ...
每个 content.yaml 都应包含带有旁白文本的 speaker_notes: 字段。生成的 WAV 文件会命名为 slide-NNN.wav,与目录名称相匹配。
故障排除
| 问题 | 解决方案 |
|---|---|
Set SPEECH_KEY ... or SPEECH_RESOURCE_ID | 设置 SPEECH_KEY(密钥认证)或 SPEECH_RESOURCE_ID(Entra ID),并设置 SPEECH_REGION。 |
SPEECH_REGION must be set | 将 SPEECH_REGION 设置为已获批准的 Azure 区域。语音合成没有默认区域。 |
Multiple WAV files map to slide N | 删除重复的 WAV 文件(例如,与 slide-001.wav 并列的 slide-1.wav),然后重新运行 embed_audio.py。 |
| 使用 Entra ID 认证时出现 401 错误 | 检查 Speech 资源上的自定义域和 Cognitive Services Speech User 角色。RBAC 最多需要 5 分钟才能生效。 |
| WAV 文件为空或幻灯片被跳过 | 确认 content.yaml 中存在非空的 speaker_notes:。 |
| 首字母缩略词发音错误 | 在 acronyms.yaml 中添加带有语音别名的条目。 |
azure-cognitiveservices-speech package is required | 在技能目录中运行 uv sync。 |
| PPTX 中显示音频图标 | 嵌入后,在 PowerPoint 中重新定位或调整音频对象的大小。 |
| 嵌入后原先添加的幻灯片动画消失 | embed_audio.py 会用旁白计时替换现有的 p:timing;嵌入音频后,在 PowerPoint 中重新应用动画。 |
| 嵌入后幻灯片不再响应单击而前进 | embed_audio.py 会将 advClick="0" 设为自动前进。若要重新启用,请在 PowerPoint 中选中所有幻灯片,并在“切换”选项卡中勾选 推进幻灯片 > 单击鼠标时。 |
| 导出视频时显示“未记录计时” | 使用更新后的 embed_audio.py 重新嵌入音频,该脚本会自动添加旁白计时 XML。 |