mcpField
T
TTS

TTS

github.com/answerzhao创意与媒体26112 次安装41 周前更新
代码仓库

关于 TTS

面向使用 z-ai-web-dev-sdk 将文本生成为语音的开发者技能,涵盖命令行与后端 SDK、语音及音频设置、格式限制和集成示例。

主要功能

  1. 01通过 z-ai-web-dev-sdk 进行文本转语音
  2. 02命令行转换及语音、语速选项
  3. 03支持 WAV、MP3 和 PCM 输出
  4. 04流式输出仅支持 PCM
  5. 05输入限制与参数范围说明

适用场景

  1. 01为学习内容生成旁白
  2. 02为书面文本制作音频版本
  3. 03为 API 驱动的应用生成语音
  4. 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({ ... });

常见用例

  1. 有声书与播客:将书面内容转换为音频格式
  2. 在线学习:为教育内容创建旁白
  3. 无障碍:提供文本内容的音频版本
  4. 语音助手:生成动态响应
  5. 公告通知:创建自动化音频通知
  6. IVR 系统:生成电话系统提示词
  7. 内容本地化:用不同语言生成音频

集成示例

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 路由端点或重启开发服务器

性能建议

  1. 复用 SDK 实例:创建一次 ZAI 实例并重复使用
  2. 实现缓存:对重复文本的生成音频进行缓存
  3. 批处理:高效地处理多个文本
  4. 优化文本:生成前移除不必要的内容
  5. 异步处理:使用队列处理多个请求

重要说明

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
  • 保存到文件时妥善处理音频缓冲区
  • 为生产环境应用实现错误处理
  • 考虑对频繁生成的内容进行缓存
  • 定期清理旧的音频文件以管理存储空间

评论

支持 Markdown