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

使用集成接入文字转语音

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

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

连接 Azure 语音

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

  1. 打开 创建语音服务,选订阅、资源组和区域。免费层 F0 就够试用。
  2. 创建完成后进「密钥和终结点」,复制密钥 1
  3. 记下位置/区域,形如 eastuseastasiasoutheastasia

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

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

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

speak:合成一段语音

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

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

要调音色、语速、情绪,传一个对象:

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
voice音色名,留空取集成里配的默认值
rate语速。+20%-10%1.2,或 slow / fast 这类词
pitch音高。+10%-2st(半音)、high / low
style说话风格,如 cheerfulsadnewscast-casual。音色要支持才有效
styleDegree风格强度,常用 0.012
language语言标签,如 zh-CN。一般不用填,音色自带
format输出格式,留空是 mp3,见音频格式
uploadtrue 时直接存进项目空间并返回 fileUrl
filename只在 upload 时有用。默认按内容哈希命名

返回:

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

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

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

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

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

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

为什么只收纯文本,不收 SSML

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

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

音频格式

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

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

常用音色

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

完整列表在 Azure 语音的语言与音色支持,注意不是每个音色都支持 style

多渠道:via(tag)

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

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

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

缓存与去重要自己做

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

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

相关文档:更多集成OpenAI 模型集成Func 后端能力

Render diagnostics