# 使用集成接入文字转语音｜Creght

> 在 Creght 用 Azure 语音集成做 TTS：ctx.tts.azure.speak 合成语音，支持音色、语速、音高与情绪，upload 直接存文件返回地址，密钥不进沙箱，文本自动转义防 SSML 注入。

[![Creght](https://ugc.talizen.com/_assets/site/2061660904709165056/1780797461299__creght_logo.png)API for AI](/)

[查看 llms.txt](/llms.txt)

概览

- [Creght AI 编程指南](/api.md)

AI 可发现性

- [如何优化 llms.txt](/api/optimize-llms-txt.md)

站点配置

- [配置 talizen.config.ts](/api/talizen-config.md)
- [实现基于域名的多语言路由](/api/domain-locale-routing.md)

后端

- [在服务端调用外部 API 并管理缓存](/api/ssr-external-api-cache.md)
- [使用 Func 构建站点后端能力](/api/func-backend.md)
- [JSON 表：定义、读写与查询](/api/func-json-tables.md)
- [上传文件：直传与 Func 内生成](/api/func-assets-upload.md)
- [超时配置与流式响应](/api/func-timeout-streaming.md)
- [使用 Func 接入支付宝电脑网站支付](/api/func-alipay-payment.md)

集成

- [使用集成发送邮件与验证码](/api/func-email-integration.md)
- [使用集成接入支付宝支付](/api/func-alipay-integration.md)
- [使用集成接入 Stripe 支付](/api/func-stripe-integration.md)
- [使用集成调用 OpenAI 模型](/api/func-ai-integration.md)
- [使用集成接入文字转语音](/api/func-tts-integration.md)

登录与用户

- [注册时验证邮箱](/api/auth-verified-registration.md)
- [实现找回密码与修改密码](/api/auth-password-reset.md)
- [在 Func 里实现登录](/api/auth-func-login.md)
- [在 Func 里查询用户](/api/func-user-directory.md)

本页目录

- [连接 Azure 语音](#connect)
- [speak：合成一段语音](#speak)
- [upload: true 通常是你要的那条路](#upload)
- [为什么只收纯文本，不收 SSML](#ssml)
- [音频格式](#format)
- [常用音色](#voices)
- [多渠道：via(tag)](#via)
- [缓存与去重要自己做](#cache)
- [报错怎么看](#errors)
- [不做的事](#scope)

集成/使用集成接入文字转语音

# 使用集成接入文字转语音

连接 Azure 语音集成后在 Func 里用 ctx.tts.azure.speak 把文字变成语音：可调音色、语速、音高与说话风格，upload 直接存进项目空间返回永久地址。订阅密钥只保存在服务端，文本由平台转义后拼成 SSML。

复制 Markdown 链接

连接 Azure 语音集成之后，Func 里用 `ctx.tts.azure.speak()` 把文字变成语音：朗读文章、生成播客、做有声书、给页面加语音提示。订阅密钥只保存在服务端，代码里不会出现。

**一句话用法**

`ctx.tts.azure.speak({ text: '你好世界', upload: true })`，返回一个可以直接放进 `<audio>` 的地址。

## 连接 Azure 语音

先在 Azure 门户创建一个 **语音服务** 资源：

1. 打开 [创建语音服务](https://portal.azure.com/#create/Microsoft.CognitiveServicesSpeechServices)，选订阅、资源组和区域。免费层 F0 就够试用。
2. 创建完成后进「密钥和终结点」，复制 **密钥 1**。
3. 记下 **位置/区域**，形如 `eastus`、 `eastasia`、 `southeastasia`。

然后打开编辑器的 **后端 → 集成**，点 **Azure 语音**：

| 字段 | 填什么 | 必填 |
| --- | --- | --- |
| 密钥 | 「密钥和终结点」页面里的密钥 1 | 是 |
| 区域 | 短区域名，如 `eastus`。 **不要填整条地址** | 是 |
| 默认音色 | 调用不指定时用它，默认 `zh-CN-XiaoxiaoNeural` | 否 |
| 音频格式 | 留空就是 mp3 | 否 |
| 渠道标签 | 同一项目可以接多份配置（比如中文一份英文一份），用标签区分 | 否 |

**区域为什么最容易填错**

区域会被拼进请求域名（ `<区域>.tts.speech.microsoft.com`）。把「终结点」那一整条 URL 粘进来，或者填成「East US」这种带空格的显示名，都会让整份配置不可用。面板和后端都会拦，但记住它要的是 `eastus` 这种短名。

保存时平台会真的向 Azure 请求一次音色列表，密钥或区域不对当场报错。

## speak：合成一段语音

最常见的写法是只传文字：

```ts
export function read(input, ctx) {
  const audio = ctx.tts.azure.speak(input.text)
  return { audioBase64: audio.audioBase64, mimeType: audio.mimeType }
}
```

要调音色、语速、情绪，传一个对象：

```ts
export function read(input, ctx) {
  const audio = ctx.tts.azure.speak({
    text: input.text,
    voice: 'zh-CN-YunxiNeural',
    rate: '+10%',
    pitch: '-2st',
    style: 'cheerful',
    styleDegree: 1.5,
    format: 'audio-24khz-48kbitrate-mono-mp3',
    upload: true,
  })
  return { url: audio.fileUrl }
}
```

| 参数 | 说明 |
| --- | --- |
| `text` | 必填。 **纯文本**，不是 SSML，见 [为什么不收 SSML](#ssml) |
| `voice` | 音色名，留空取集成里配的默认值 |
| `rate` | 语速。 `+20%`、 `-10%`、 `1.2`，或 `slow` / `fast` 这类词 |
| `pitch` | 音高。 `+10%`、 `-2st`（半音）、 `high` / `low` |
| `style` | 说话风格，如 `cheerful`、 `sad`、 `newscast-casual`。音色要支持才有效 |
| `styleDegree` | 风格强度，常用 `0.01` 到 `2` |
| `language` | 语言标签，如 `zh-CN`。一般不用填，音色自带 |
| `format` | 输出格式，留空是 mp3，见 [音频格式](#format) |
| `upload` | `true` 时直接存进项目空间并返回 `fileUrl` |
| `filename` | 只在 `upload` 时有用。默认按内容哈希命名 |

返回：

| 字段 | 说明 |
| --- | --- |
| `fileUrl` | 只有 `upload: true` 时有。可以直接放进 `<audio src>` |
| `audioBase64` | 不传 `upload` 时有。音频原文的 base64 |
| `mimeType` | 如 `audio/mpeg` |
| `bytes` | 音频字节数 |
| `voice` | 实际用到的音色 |
| `format` | 实际用到的格式 |

## upload: true 通常是你要的那条路

不传 `upload` 时音频以 base64 回到你的代码里。一分钟的语音大约是 500KB，base64 之后接近 700KB 的字符串，要占 Func 的内存预算，再随返回值传给前端又要占一次带宽。

带上 `upload: true`，音频在服务端直接进项目自己的文件空间，你只拿到一个永久地址：

```ts
const audio = ctx.tts.azure.speak({ text: paragraph, upload: true })
ctx.db.insert('chapters', { text: paragraph, audioUrl: audio.fileUrl })
```

什么时候不用 `upload`：一次性的短提示音、马上就播不需要保存的内容。

## 为什么只收纯文本，不收 SSML

Azure 接口本身吃的是 SSML，平台会替你拼，并把你的文字转义之后放进去。 **你不能自己写 SSML 标签**。

**这是一条安全边界，不是偷懒**

要合成的文本常常来自用户输入（评论、书稿、表单）。不转义的后果不是报错而是 **注入**：文本里带一段 `</voice><voice name='...'>` 就能改掉整段音色，带 `<break time='9s'/>` 就能让你的播客中间静音九秒。

同理， `rate`、 `pitch`、 `style` 这些值也会被拼进 SSML 属性里，所以平台按白名单校验它们，填了奇怪的值会直接报错而不是被拼进去。

确实需要整段自己写 SSML 的话，用 `ctx.ai.openai.call()` 那样的思路：这个能力目前不开 SSML 直通，需要的话可以在 Func 里自己配环境变量走 `fetch`。

## 音频格式

留空就是 mp3，绝大多数场景都够用。常用取值：

| 格式 | 用途 |
| --- | --- |
| `audio-24khz-48kbitrate-mono-mp3` | 默认。体积小，网页播放够用 |
| `audio-48khz-192kbitrate-mono-mp3` | 音质更好，文件更大，适合有声书 |
| `riff-24khz-16bit-mono-pcm` | WAV，适合再做后期处理 |
| `ogg-24khz-16bit-mono-opus` | Opus，同码率下音质更好，浏览器支持要自己确认 |

## 常用音色

| 音色 | 说明 |
| --- | --- |
| `zh-CN-XiaoxiaoNeural` | 中文女声，默认。支持多种 `style` |
| `zh-CN-YunxiNeural` | 中文男声，偏年轻 |
| `zh-CN-YunjianNeural` | 中文男声，适合解说与体育播报 |
| `zh-CN-XiaoyiNeural` | 中文女声，情感表现更强 |
| `en-US-JennyNeural` | 英文女声 |
| `en-US-GuyNeural` | 英文男声 |

完整列表在 [Azure 语音的语言与音色支持](https://learn.microsoft.com/azure/ai-services/speech-service/language-support)，注意不是每个音色都支持 `style`。

## 多渠道：via(tag)

一个项目可以接多份配置，比如中文一份、英文一份，或者试用层和付费层各一份。给每份写上渠道标签，代码里用 `via()` 选：

```ts
ctx.tts.azure.via('zh').speak({ text: '你好' })
ctx.tts.azure.via('en').speak({ text: 'hello' })
```

不写 `via()` 时用标签为 `default` 的那份。

## 缓存与去重要自己做

平台 **不** 缓存：同一句话调两次就是两次钱。要省钱自己建一张表存「文本哈希 → 地址」。

```ts
export function speakCached(input, ctx) {
  const key = input.text.trim()
  const hit = ctx.db.query('tts_cache', { where: { text: key }, limit: 1 })
  if (hit.list.length > 0) return { url: hit.list[0].url }

  const audio = ctx.tts.azure.speak({ text: key, upload: true })
  ctx.db.insert('tts_cache', { text: key, url: audio.fileUrl })
  return { url: audio.fileUrl }
}
```

不内置是因为「什么算同一句」只有你知道：换了音色算不算？语速呢？把这个判断做进平台，只会做出一个你不同意的版本。

## 报错怎么看

| 报错 | 该改哪一项 |
| --- | --- |
| `tts integration has no api key` | 集成没连，或密钥被清空了 |
| `azure_tts_region is required` | 区域没填 |
| `invalid azure_tts_region` | 区域填成了整条地址或带空格的显示名，要 `eastus` 这种短名 |
| `text is required` | 传进来的文字是空的 |
| `invalid rate` / `invalid pitch` / `invalid style` | 值没通过白名单。参考上面各参数的取值说明 |
| `invalid voice` | 音色名格式不对，形如 `zh-CN-XiaoxiaoNeural` |

Azure 自己的报错（配额用尽、音色不存在、区域与密钥不匹配）会带上文案转成 400 或 502，不会把上游的 401 直接抛给前端。

## 不做的事

| 不做 | 为什么 |
| --- | --- |
| SSML 直通 | 文本常常来自用户输入，不转义就是注入。见 [上文](#ssml) |
| 缓存与去重 | 「什么算同一句」只有站点知道 |
| 长文本自动分段 | 怎么断句、要不要按章节分文件，是内容的事不是接口的事 |
| 其他 TTS 服务商 | 目前只有 Azure。真有人要再按同一套接口加 |

相关文档： [更多集成](/docs/ai/more-integrations.md)、 [OpenAI 模型集成](/api/func-ai-integration.md)、 [Func 后端能力](/api/func-backend.md)。

![Creght](https://ugc.talizen.com/_assets/site/2061660904709165056/1780797461299__creght_logo.png)

此网站使用 [Creght](/) 创建

![微信客服](https://fsu.creght.com/site/2066727200882692096/1785119134612__image.png?w=3072&fmt=webp)

微信客服

## 链接

- [SaaS 官网生成器](/saas-website-builder.md)
- [价格](/price.md)
- [解决方案](/solution.md)
- [客户案例](/customers.md)
- [AI 模型对比](/ai-models.md)
- [帮助中心](/help.md)
- [联系我们](/contact.md)
- [更新记录 & 博客](/blogs.md)
- [退款说明](/tuikuan.md)

## 资源

- [全部资源](/resources.md)
- [模板](/templates.md)
- [组件库](https://creghtlib.site.creght.com)
- [动效库](/design/effects.md)
- [Figma to Creght](/figma2creght.md)
- [API](/api.md)

## 产品对比

- [对比上线了](/creght-vs-sxl.md)
- [对比凡科建站](/creght-vs-fkw.md)
- [Creght vs 自己写代码](/compare/self-coding.md)
- [Creght vs 外包](/compare/outsourcing.md)
- [Creght vs Framer](/compare/framer.md)
- [Creght vs WordPress](/compare/wordpress.md)

## 协议

- [用户协议](/legal/terms.md)
- [隐私政策](/legal/privacy.md)
- [可接受使用政策](/legal/acceptable-use.md)

## 社交媒体

- [小红书](https://www.xiaohongshu.com/user/profile/5a38606811be10715f4895b6)
- [哔哩哔哩](https://space.bilibili.com/513308095)

[蜀ICP备2023038192号-2](https://beian.miit.gov.cn)

免费方案评估

> 全站页面清单：[/llms.txt](/llms.txt)
