关于 TTS
面向使用 z-ai-web-dev-sdk 将文本生成为语音的开发者技能,涵盖命令行与后端 SDK、语音及音频设置、格式限制和集成示例。
主要功能
- 01通过 z-ai-web-dev-sdk 进行文本转语音
- 02命令行转换及语音、语速选项
- 03支持 WAV、MP3 和 PCM 输出
- 04流式输出仅支持 PCM
- 05输入限制与参数范围说明
适用场景
- 01为学习内容生成旁白
- 02为书面文本制作音频版本
- 03为 API 驱动的应用生成语音
- 04通过命令行测试音色和输出格式
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ answerzhao/ agent-skills --skill tts安装后在 Claude Code 中输入/tts确认已加载。
附带文件tts.tsLICENSE.txt
详细介绍
TTS(文本转语音)技能
本技能指导如何使用 z-ai-web-dev-sdk 包实现文本转语音(TTS)功能,将文本转换为音质自然的语音音频。
技能路径
技能位置:{project_path}/skills/TTS
本技能位于你项目中的上述路径。
参考脚本:{Skill Location}/scripts/ 目录中提供了示例测试脚本,便于快速测试和参考。可查看 {Skill Location}/scripts/tts.ts 获取一个可运行的示例。
概述
文本转语音让你能够构建从文本输入生成语音音频的应用,支持多种音色、语速和输出格式,满足多样化用例。
重要:z-ai-web-dev-sdk 必须仅在后端代码中使用。切勿在客户端代码中使用。
API 限制与约束
在实现 TTS 功能之前,请了解以下重要限制:
输入文本约束
- 最大长度:每次请求 1024 个字符
- 超出此限制的文本必须拆分为较小的分块
音频参数
- 语速范围:0.5 至 2.0
- 0.5 = 半速(较慢)
- 1.0 = 正常语速(默认)
- 2.0 = 双倍速(较快)
- 音量范围:大于 0,最大为 10
- 默认值:1.0
- 取值必须大于 0(不含)且最大为 10(含)
格式与流式传输
- 流式传输限制:启用
stream: true时,仅支持pcm格式 - 非流式传输:支持
wav、pcm和mp3格式 - 采样率:24000 Hz(推荐)
长文本最佳实践
function splitTextIntoChunks(text, maxLength = 1000) {
const chunks = [];
const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
let currentChunk = '';
for (const sentence of sentences) {
if ((currentChunk + sentence).length <= maxLength) {
currentChunk += sentence;
} else {
if (currentChunk) chunks.push(currentChunk.trim());
currentChunk = sentence;
}
}
if (currentChunk) chunks.push(currentChunk.trim());
return chunks;
}
前提条件
z-ai-web-dev-sdk 包已安装。按下述示例所示导入它。
命令行用法(适用于简单任务)
对于简单的文本转语音转换,你可以使用 z-ai 命令行,而无需编写代码。这非常适合快速生成音频、测试音色或简单自动化。
基本 TTS
# Convert text to speech (default WAV format)
z-ai tts --input "Hello, world" --output ./hello.wav
# Using short options
z-ai tts -i "Hello, world" -o ./hello.wav
不同音色和语速
# Use specific voice
z-ai tts -i "Welcome to our service" -o ./welcome.wav --voice tongtong
# Adjust speech speed (0.5-2.0)
z-ai tts -i "This is faster speech" -o ./fast.wav --speed 1.5
# Slower speech
z-ai tts -i "This is slower speech" -o ./slow.wav --speed 0.8
不同输出格式
# MP3 format
z-ai tts -i "Hello World" -o ./hello.mp3 --format mp3
# WAV format (default)
z-ai tts -i "Hello World" -o ./hello.wav --format wav
# PCM format
z-ai tts -i "Hello World" -o ./hello.pcm --format pcm
流式输出
# Stream audio generation
z-ai tts -i "This is a longer text that will be streamed" -o ./stream.wav --stream
命令行参数
--input, -i <text>:必填 - 要转换为语音的文本(最多 1024 个字符)--output, -o <path>:必填 - 输出音频文件路径--voice, -v <voice>:可选 - 音色类型(默认:tongtong)--speed, -s <number>:可选 - 语速,0.5-2.0(默认:1.0)--format, -f <format>:可选 - 输出格式:wav、mp3、pcm(默认:wav)--stream:可选 - 启用流式输出(仅支持 pcm 格式)
何时使用命令行与 SDK
使用命令行的情况:
- 快速文本转语音转换
- 测试不同音色和语速
- 简单的批量音频生成
- 命令行自动化脚本
使用 SDK 的情况:
- 在应用中动态生成音频
- 与 Web 服务集成
- 自定义音频处理流水线
- 具有复杂需求的生产应用
基本 TTS 实现
简单文本转语音
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
async function textToSpeech(text, outputPath) {
const zai = await ZAI.create();
const response = await zai.audio.tts.create({
input: text,
voice: 'tongtong',
speed: 1.0,
response_format: 'wav',
stream: false
});
// Get array buffer from Response object
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer));
fs.writeFileSync(outputPath, buffer);
console.log(`Audio saved to ${outputPath}`);
return outputPath;
}
// Usage
await textToSpeech('Hello, world!', './output.wav');
多种音色选项
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
async function generateWithVoice(text, voice, outputPath) {
const zai = await ZAI.create();
const response = await zai.audio.tts.create({
input: text,
voice: voice, // Available voices: tongtong, chuichui, xiaochen, jam, kazi, douji, luodo
speed: 1.0,
response_format: 'wav',
stream: false
});
// Get array buffer from Response object
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer));
fs.writeFileSync(outputPath, buffer);
return outputPath;
}
// Usage
await generateWithVoice('Welcome to our service', 'tongtong', './welcome.wav');
可调语速
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
async function generateWithSpeed(text, speed, outputPath) {
const zai = await ZAI.create();
// Speed range: 0.5 to 2.0 (API constraint)
// 0.5 = half speed (slower)
// 1.0 = normal speed (default)
// 2.0 = double speed (faster)
// Values outside this range will cause API errors
const response = await zai.audio.tts.create({
input: text,
voice: 'tongtong',
speed: speed,
response_format: 'wav',
stream: false
});
// Get array buffer from Response object
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer));
fs.writeFileSync(outputPath, buffer);
return outputPath;
}
// Usage - slower narration
await generateWithSpeed('This is an important announcement', 0.8, './slow.wav');
// Usage - faster narration
await generateWithSpeed('Quick update', 1.3, './fast.wav');
可调音量
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
async function generateWithVolume(text, volume, outputPath) {
const zai = await ZAI.create();
// Volume range: greater than 0, up to 10 (API constraint)
// Values must be > 0 (exclusive) and <= 10 (inclusive)
// Default: 1.0 (normal volume)
const response = await zai.audio.tts.create({
input: text,
voice: 'tongtong',
speed: 1.0,
volume: volume, // Optional parameter
response_format: 'wav',
stream: false
});
// Get array buffer from Response object
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer));
fs.writeFileSync(outputPath, buffer);
return outputPath;
}
// Usage - louder audio
await generateWithVolume('This is an announcement', 5.0, './loud.wav');
// Usage - quieter audio
await generateWithVolume('Whispered message', 0.5, './quiet.wav');
高级用例
批处理
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
import path from 'path';
async function batchTextToSpeech(textArray, outputDir) {
const zai = await ZAI.create();
const results = [];
// Ensure output directory exists
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
for (let i = 0; i < textArray.length; i++) {
try {
const text = textArray[i];
const outputPath = path.join(outputDir, `audio_${i + 1}.wav`);
const response = await zai.audio.tts.create({
input: text,
voice: 'tongtong',
speed: 1.0,
response_format: 'wav',
stream: false
});
// Get array buffer from Response object
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer));
fs.writeFileSync(outputPath, buffer);
results.push({
success: true,
text,
path: outputPath
});
} catch (error) {
results.push({
success: false,
text: textArray[i],
error: error.message
});
}
}
return results;
}
// Usage
const texts = [
'Welcome to chapter one',
'Welcome to chapter two',
'Welcome to chapter three'
];
const results = await batchTextToSpeech(texts, './audio-output');
console.log('Generated:', results.length, 'audio files');
动态内容生成
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
class TTSGenerator {
constructor() {
this.zai = null;
}
async initialize() {
this.zai = await ZAI.create();
}
async generateAudio(text, options = {}) {
const {
voice = 'tongtong',
speed = 1.0,
format = 'wav'
} = options;
const response = await this.zai.audio.tts.create({
input: text,
voice: voice,
speed: speed,
response_format: format,
stream: false
});
// Get array buffer from Response object
const arrayBuffer = await response.arrayBuffer();
return Buffer.from(new Uint8Array(arrayBuffer));
}
async saveAudio(text, outputPath, options = {}) {
const buffer = await this.generateAudio(text, options);
if (buffer) {
fs.writeFileSync(outputPath, buffer);
return outputPath;
}
return null;
}
}
// Usage
const generator = new TTSGenerator();
await generator.initialize();
await generator.saveAudio(
'Hello, this is a test',
'./output.wav',
{ speed: 1.2 }
);
Next.js API 路由示例
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
try {
const { text, voice = 'tongtong', speed = 1.0 } = await req.json();
// Import ZAI SDK
const ZAI = (await import('z-ai-web-dev-sdk')).default;
// Create SDK instance
const zai = await ZAI.create();
// Generate TTS audio
const response = await zai.audio.tts.create({
input: text.trim(),
voice: voice,
speed: speed,
response_format: 'wav',
stream: false,
});
// Get array buffer from Response object
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer));
// Return audio as response
return new NextResponse(buffer, {
status: 200,
headers: {
'Content-Type': 'audio/wav',
'Content-Length': buffer.length.toString(),
'Cache-Control': 'no-cache',
},
});
} catch (error) {
console.error('TTS API Error:', error);
return NextResponse.json(
{
error: error instanceof Error ? error.message : '生成语音失败,请稍后重试',
},
{ status: 500 }
);
}
}
最佳实践
1. 文本准备
function prepareTextForTTS(text) {
// Remove excessive whitespace
text = text.replace(/\s+/g, ' ').trim();
// Expand common abbreviations for better pronunciation
const abbreviations = {
'Dr.': 'Doctor',
'Mr.': 'Mister',
'Mrs.': 'Misses',
'etc.': 'et cetera'
};
for (const [abbr, full] of Object.entries(abbreviations)) {
text = text.replace(new RegExp(abbr, 'g'), full);
}
return text;
}
2. 错误处理
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
async function safeTTS(text, outputPath) {
try {
// Validate input
if (!text || text.trim().length === 0) {
throw new Error('Text input cannot be empty');
}
if (text.length > 1024) {
throw new Error('Text input exceeds maximum length of 1024 characters');
}
const zai = await ZAI.create();
const response = await zai.audio.tts.create({
input: text,
voice: 'tongtong',
speed: 1.0,
response_format: 'wav',
stream: false
});
// Get array buffer from Response object
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer));
fs.writeFileSync(outputPath, buffer);
return {
success: true,
path: outputPath,
size: buffer.length
};
} catch (error) {
console.error('TTS Error:', error);
return {
success: false,
error: error.message
};
}
}
3. SDK 实例复用
import ZAI from 'z-ai-web-dev-sdk';
// Create a singleton instance
let zaiInstance = null;
async function getZAIInstance() {
if (!zaiInstance) {
zaiInstance = await ZAI.create();
}
return zaiInstance;
}
// Usage
const zai = await getZAIInstance();
const response = await zai.audio.tts.create({ ... });
常见用例
- 有声书与播客:将书面内容转换为音频格式
- 在线学习:为教育内容创建旁白
- 无障碍:提供文本内容的音频版本
- 语音助手:生成动态响应
- 公告通知:创建自动化音频通知
- IVR 系统:生成电话系统提示词
- 内容本地化:用不同语言生成音频
集成示例
Express.js API 端点
import express from 'express';
import ZAI from 'z-ai-web-dev-sdk';
import fs from 'fs';
import path from 'path';
const app = express();
app.use(express.json());
let zaiInstance;
const outputDir = './audio-output';
async function initZAI() {
zaiInstance = await ZAI.create();
if (!fs.existsSync(outputDir)) {
fs.mkdirSync(outputDir, { recursive: true });
}
}
app.post('/api/tts', async (req, res) => {
try {
const { text, voice = 'tongtong', speed = 1.0 } = req.body;
if (!text) {
return res.status(400).json({ error: 'Text is required' });
}
const filename = `tts_${Date.now()}.wav`;
const outputPath = path.join(outputDir, filename);
const response = await zaiInstance.audio.tts.create({
input: text,
voice: voice,
speed: speed,
response_format: 'wav',
stream: false
});
// Get array buffer from Response object
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer));
fs.writeFileSync(outputPath, buffer);
res.json({
success: true,
audioUrl: `/audio/${filename}`,
size: buffer.length
});
} catch (error) {
res.status(500).json({ error: error.message });
}
});
app.use('/audio', express.static('audio-output'));
initZAI().then(() => {
app.listen(3000, () => {
console.log('TTS API running on port 3000');
});
});
故障排查
问题:"Input text exceeds maximum length"(输入文本超出最大长度)
- 解决方案:文本输入上限为 1024 个字符。请使用 API Limitations 部分中展示的
splitTextIntoChunks函数将较长的文本拆分成多个块
问题:"Invalid speed parameter"(无效的速度参数)或速度行为异常
- 解决方案:速度必须介于 0.5 和 2.0 之间。请检查你的速度值是否在此范围内
问题:"Invalid volume parameter"(无效的音量参数)
- 解决方案:音量必须大于 0 且不超过 10。请确保音量值在范围 (0, 10] 内
问题:使用 WAV/MP3 时出现 "Stream format not supported"(不支持流式格式)
- 解决方案:流式模式仅支持 PCM 格式。请在使用流式时搭配
response_format: 'pcm',或为 WAV/MP3 输出禁用流式(stream: false)
问题:"SDK must be used in backend"(SDK 必须在后端使用)
- 解决方案:确保 z-ai-web-dev-sdk 仅在服务端代码中导入
问题:"TypeError: response.audio is undefined"
- 解决方案:SDK 返回标准的 Response 对象,请使用
await response.arrayBuffer(),而不是访问response.audio
问题:生成的音频文件为空或已损坏
- 解决方案:确保调用
await response.arrayBuffer()并正确转换为 Buffer:Buffer.from(new Uint8Array(arrayBuffer))
问题:音频听起来不自然
- 解决方案:妥善准备文本(移除特殊字符,展开缩写)
问题:处理时间过长
- 解决方案:将长文本拆分成较小的块并并行处理
问题:Next.js 缓存了旧的 API 路由
- 解决方案:创建新的 API 路由端点或重启开发服务器
性能建议
- 复用 SDK 实例:创建一次 ZAI 实例并重复使用
- 实现缓存:对重复文本的生成音频进行缓存
- 批处理:高效地处理多个文本
- 优化文本:生成前移除不必要的内容
- 异步处理:使用队列处理多个请求
重要说明
API 约束
输入文本长度:每次请求最多 1024 个字符。对于较长的文本:
// Split long text into chunks
const longText = "..."; // Your long text here
const chunks = splitTextIntoChunks(longText, 1000);
for (const chunk of chunks) {
const response = await zai.audio.tts.create({
input: chunk,
voice: 'tongtong',
speed: 1.0,
response_format: 'wav',
stream: false
});
// Process each chunk...
}
流式格式限制:使用 stream: true 时,仅支持 pcm 格式。如需输出 wav 或 mp3,请使用 stream: false。
采样率:音频以 24000 Hz 采样率生成(推荐的播放设置)。
响应对象格式
zai.audio.tts.create() 方法返回标准的 Response 对象(不是带有 audio 属性的自定义对象)。请始终使用:
// ✅ CORRECT
const response = await zai.audio.tts.create({ ... });
const arrayBuffer = await response.arrayBuffer();
const buffer = Buffer.from(new Uint8Array(arrayBuffer));
// ❌ WRONG - This will not work
const response = await zai.audio.tts.create({ ... });
const buffer = Buffer.from(response.audio); // response.audio is undefined
可用音色
tongtong- 温暖亲切chuichui- 活泼可爱xiaochen- 沉稳专业jam- 英音绅士kazi- 清晰标准douji- 自然流畅luodo- 富有感染力
速度范围
- 最小值:
0.5(半速) - 默认值:
1.0(正常速度) - 最大值:
2.0(双倍速度)
重要:速度值超出 [0.5, 2.0] 范围将导致 API 错误。
音量范围
- 最小值:大于
0(不含) - 默认值:
1.0(正常音量) - 最大值:
10(含)
注意:音量参数是可选的。未指定时,默认为 1.0。
请记住
- 始终仅在后端代码中使用 z-ai-web-dev-sdk
- 输入文本最多限制为 1024 个字符 - 将较长的文本拆分成多个块
- 速度必须介于 0.5 和 2.0 之间 - 超出此范围的值将导致错误
- 音量必须大于 0 且不超过 10 - 可选参数,默认值为 1.0
- 流式仅支持 PCM 格式 - 如需 WAV 或 MP3 输出,请使用非流式
- SDK 返回标准的 Response 对象 - 请使用
await response.arrayBuffer() - 使用
Buffer.from(new Uint8Array(arrayBuffer))将 ArrayBuffer 转换为 Buffer - 保存到文件时妥善处理音频缓冲区
- 为生产环境应用实现错误处理
- 考虑对频繁生成的内容进行缓存
- 定期清理旧的音频文件以管理存储空间