New:Introducing Socket Scanning for VS Code Marketplace Extensions.Learn more →
Get Started

@xiaozhi-client/tts

Package Overview
Dependencies
Maintainers
1
Versions
7
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@xiaozhi-client/tts

@xiaozhi-client/tts

latest
Source
npmnpm
Version
2.2.0
Version published
Weekly downloads
11
450%
Maintainers
1
Weekly downloads
 
Created
Source

@xiaozhi-client/tts

小智客户端 TTS(文本转语音)库,支持流式和非流式语音合成

npm version License: MIT

简介

@xiaozhi-client/tts 是一个功能完整的 TTS(Text-to-Speech)客户端库,提供:

  • 双模式合成 - 支持流式和非流式两种语音合成方式
  • 事件驱动 - 基于 EventEmitter 的异步事件处理
  • 平台支持 - 目前支持字节跳动 TTS 平台
  • 简洁 API - 提供便捷函数和面向对象两种使用方式
  • 完整类型 - TypeScript 严格模式,完整类型支持
  • WebSocket 连接 - 基于 WebSocket 的实时语音合成

特性

核心功能

  • 流式合成 - 边合成边输出,适合实时播放场景
  • 非流式合成 - 一次性获取完整音频数据
  • 事件监听 - 支持连接、音频块、错误等事件监听
  • 便捷函数 - 无需实例化,直接调用
  • 配置验证 - 内置配置验证和类型检查
  • 自动资源管理 - 支持 close 方法释放资源

支持的平台

平台说明状态
bytedance字节跳动 TTS 服务✅ 稳定

安装

# 使用 npm
npm install @xiaozhi-client/tts

# 使用 pnpm
pnpm add @xiaozhi-client/tts

# 使用 yarn
yarn add @xiaozhi-client/tts

依赖要求

{
  "dependencies": {
    "ws": "^8.16.0",
    "zod": "^3.23.8"
  }
}

快速开始

使用便捷函数(推荐)

便捷函数提供最简单的 API,适合一次性语音合成任务。

非流式合成

import { synthesizeSpeech } from '@xiaozhi-client/tts';
import fs from 'node:fs';

// 非流式合成:一次性获取完整音频
const audio = await synthesizeSpeech({
  appid: 'your-app-id',
  accessToken: 'your-access-token',
  voice_type: 'zh_female_xiaohe_uranus_bigtts',
  text: '你好,这是一段测试语音。',
  encoding: 'wav',
  speed: 1.0,
  pitch: 0,
  volume: 1.0
});

// audio 是 Uint8Array,可直接保存为文件
fs.writeFileSync('output.wav', audio);

流式合成

import { synthesizeSpeechStream } from '@xiaozhi-client/tts';

// 流式合成:边合成边获取音频块
const audioChunks: Uint8Array[] = [];

await synthesizeSpeechStream({
  appid: 'your-app-id',
  accessToken: 'your-access-token',
  voice_type: 'zh_female_xiaohe_uranus_bigtts',
  text: '这是一段流式合成的语音。',
  encoding: 'wav',
  onAudioChunk: async (chunk, isLast) => {
    console.log(`收到音频块,大小: ${chunk.length} 字节,最终块: ${isLast}`);
    audioChunks.push(chunk);

    // 可以在这里实时播放音频块
  }
});

// 合并所有音频块
const totalLength = audioChunks.reduce((sum, chunk) => sum + chunk.length, 0);
const mergedAudio = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of audioChunks) {
  mergedAudio.set(chunk, offset);
  offset += chunk.length;
}

使用 TTS 类

TTS 类提供事件驱动的 API,适合需要持续监听和处理的场景。

import { TTS } from '@xiaozhi-client/tts';
import fs from 'node:fs';

// 创建 TTS 客户端
const client = new TTS({
  platform: 'bytedance',
  config: {
    app: {
      appid: 'your-app-id',
      accessToken: 'your-access-token'
    },
    audio: {
      voice_type: 'zh_female_xiaohe_uranus_bigtts',
      encoding: 'wav'
    }
  }
});

// 监听事件
client.on('open', () => {
  console.log('[事件] 连接已打开');
});

client.on('audio_chunk', async (chunk: Uint8Array, isLast: boolean) => {
  console.log(`[事件] 收到音频块,大小: ${chunk.length} 字节`);
  // 处理音频块
});

client.on('result', (audio?: Uint8Array) => {
  console.log('[事件] 合成完成');
  if (audio) {
    console.log(`音频大小: ${audio.length} 字节`);
  }
});

client.on('error', (error: Error) => {
  console.error('[事件] 错误:', error.message);
});

client.on('close', () => {
  console.log('[事件] 连接已关闭');
});

// 非流式合成
const audio = await client.synthesize('你好,这是测试语音。');
// 保存音频
fs.writeFileSync('output.wav', audio);

// 关闭连接
client.close();

流式合成(TTS 类)

import { TTS } from '@xiaozhi-client/tts';

const client = new TTS({
  platform: 'bytedance',
  config: {
    app: {
      appid: 'your-app-id',
      accessToken: 'your-access-token'
    },
    audio: {
      voice_type: 'zh_female_xiaohe_uranus_bigtts',
      encoding: 'wav'
    }
  }
});

// 监听音频块事件
client.on('audio_chunk', (chunk: Uint8Array, isLast: boolean) => {
  console.log(`收到音频块: ${chunk.length} 字节, 最终: ${isLast}`);
  // 可以在这里实时播放音频
});

// 流式合成
await client.synthesizeStream('这是一段流式语音。');

client.close();

使用异步迭代器(V1 API)

import { TTS } from '@xiaozhi-client/tts';

const client = new TTS({
  platform: 'bytedance',
  config: {
    app: {
      appid: 'your-app-id',
      accessToken: 'your-access-token'
    },
    audio: {
      voice_type: 'S_70000',
      encoding: 'wav'
    }
  }
});

// 使用异步迭代器
for await (const { chunk, isFinal } of client.bytedance.v1.speak('使用异步迭代器进行语音合成。')) {
  console.log(`收到音频块: ${chunk.length} 字节, 最终: ${isFinal}`);
  // 处理音频块
}

client.close();

API 参考

便捷函数

synthesizeSpeech()

非流式语音合成。

function synthesizeSpeech(options: SynthesizeOptions): Promise<Uint8Array>

参数:

参数类型必需说明
appidstring✅应用 ID
accessTokenstring✅访问令牌
voice_typestring✅声音类型
textstring✅要合成的文本
encodingstring❌编码格式(默认 wav)
speednumber❌语速(默认 1.0)
pitchnumber❌音调(默认 0)
volumenumber❌音量(默认 1.0)
clusterstring❌集群类型
endpointstring❌自定义端点

返回: Promise<Uint8Array> - 音频二进制数据

synthesizeSpeechStream()

流式语音合成。

function synthesizeSpeechStream(options: SynthesizeStreamOptions): Promise<void>

参数: 包含 synthesizeSpeech() 的所有参数,额外增加:

参数类型必需说明
onAudioChunkAudioChunkCallback✅音频块回调函数

音频块回调:

type AudioChunkCallback = (chunk: Uint8Array, isLast: boolean) => Promise<void>;

validateConfig()

验证 TTS 配置。

function validateConfig(config: unknown): ByteDanceTTSConfig

TTS 类

事件驱动的 TTS 客户端类。

构造函数

constructor(options: TTSClientOptions)

配置选项:

interface TTSClientOptions {
  platform?: 'bytedance';
  config?: ByteDanceTTSConfig;
  bytedance?: {
    v1?: ByteDanceTTSConfig;
  };
}

方法

synthesize()

非流式合成语音。

async synthesize(text: string): Promise<Uint8Array>
synthesizeStream()

流式合成语音。

async synthesizeStream(text: string): Promise<void>
close()

关闭连接并释放资源。

close(): void
updateConfig()

更新配置。

updateConfig(config: Partial<ByteDanceTTSConfig>): void
getConfig()

获取当前配置。

getConfig(): ByteDanceTTSConfig
validateConfig()(静态)

验证配置。

static validateConfig(config: unknown): ByteDanceTTSConfig

事件

TTS 类继承自 EventEmitter,支持以下事件:

// 连接打开事件
client.on('open', () => {
  console.log('连接已打开');
});

// 音频块事件(流式合成)
client.on('audio_chunk', (chunk: Uint8Array, isLast: boolean) => {
  console.log(`收到音频块: ${chunk.length} 字节`);
});

// 合成完成事件
client.on('result', (audio?: Uint8Array) => {
  console.log('合成完成');
});

// 错误事件
client.on('error', (error: Error) => {
  console.error('错误:', error.message);
});

// 连接关闭事件
client.on('close', () => {
  console.log('连接已关闭');
});

配置说明

ByteDanceTTSConfig

字节跳动 TTS 平台配置。

interface ByteDanceTTSConfig {
  // 应用配置
  app: {
    appid: string;        // 应用 ID
    accessToken: string;  // 访问令牌
  };

  // 音频配置
  audio: {
    voice_type: string;   // 声音类型
    encoding: string;     // 编码格式(wav, mp3, ogg_opus)
    speed?: number;       // 语速(0.2 - 3.0,默认 1.0)
    pitch?: number;       // 音调(-12.0 - 12.0,默认 0)
    volume?: number;      // 音量(0.1 - 10.0,默认 1.0)
  };

  // 可选配置
  cluster?: string;       // 集群类型
  endpoint?: string;      // 自定义端点
}

常用声音类型

声音类型说明
S_70000女声·小何
zh_female_xiaohe_uranus_bigtts女声·小何(完整标识)
更多声音类型请参考 字节跳动 TTS 文档

完整示例

带完整错误处理的 TTS 客户端

import { TTS, type TTSClientOptions } from '@xiaozhi-client/tts';
import fs from 'node:fs';

class TTSClient {
  private client: TTS;

  constructor(appid: string, accessToken: string) {
    const options: TTSClientOptions = {
      platform: 'bytedance',
      config: {
        app: {
          appid,
          accessToken
        },
        audio: {
          voice_type: 'zh_female_xiaohe_uranus_bigtts',
          encoding: 'wav',
          speed: 1.0,
          pitch: 0,
          volume: 1.0
        }
      }
    };

    this.client = new TTS(options);
    this.setupEventHandlers();
  }

  private setupEventHandlers() {
    this.client.on('open', () => {
      console.log('✅ TTS 连接已建立');
    });

    this.client.on('error', (error: Error) => {
      console.error('❌ TTS 错误:', error.message);
    });

    this.client.on('close', () => {
      console.log('🔌 TTS 连接已关闭');
    });
  }

  async synthesizeToFile(text: string, outputPath: string): Promise<void> {
    try {
      console.log(`开始合成: ${text}`);

      const audio = await this.client.synthesize(text);

      console.log(`合成完成,音频大小: ${audio.length} 字节`);
      fs.writeFileSync(outputPath, audio);
      console.log(`音频已保存到: ${outputPath}`);
    } catch (error) {
      console.error('合成失败:', error);
      throw error;
    }
  }

  async synthesizeStream(text: string, outputPath: string): Promise<void> {
    try {
      console.log(`开始流式合成: ${text}`);

      const chunks: Uint8Array[] = [];

      this.client.on('audio_chunk', (chunk: Uint8Array, isLast: boolean) => {
        console.log(`收到音频块: ${chunk.length} 字节, 最终: ${isLast}`);
        chunks.push(chunk);
      });

      await this.client.synthesizeStream(text);

      // 合并音频块
      const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
      const mergedAudio = new Uint8Array(totalLength);
      let offset = 0;
      for (const chunk of chunks) {
        mergedAudio.set(chunk, offset);
        offset += chunk.length;
      }

      fs.writeFileSync(outputPath, mergedAudio);
      console.log(`音频已保存到: ${outputPath}`);
    } catch (error) {
      console.error('流式合成失败:', error);
      throw error;
    }
  }

  close(): void {
    this.client.close();
  }
}

// 使用示例
async function main() {
  const ttsClient = new TTSClient(
    'your-app-id',
    'your-access-token'
  );

  try {
    // 非流式合成
    await ttsClient.synthesizeToFile('你好,世界!', 'output.wav');

    // 流式合成
    await ttsClient.synthesizeStream('这是一段流式语音。', 'output-stream.wav');
  } finally {
    ttsClient.close();
  }
}

main();

最佳实践

1. 资源管理

// ✅ 推荐:使用 try-finally 确保释放资源
const client = new TTS(config);
try {
  await client.synthesize('测试文本');
} finally {
  client.close();
}

// ❌ 避免:忘记释放资源
const client = new TTS(config);
await client.synthesize('测试文本');
// 忘记调用 close()

2. 错误处理

// ✅ 推荐:捕获并处理错误
try {
  await client.synthesize('测试文本');
} catch (error) {
  if (error instanceof Error) {
    console.error('合成失败:', error.message);
    // 实现重试逻辑或其他错误处理
  }
}

// ❌ 避免:忽略错误
await client.synthesize('测试文本'); // 可能抛出异常

3. 流式合成音频合并

// ✅ 推荐:高效合并音频块
const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0);
const merged = new Uint8Array(totalLength);
let offset = 0;
for (const chunk of chunks) {
  merged.set(chunk, offset);
  offset += chunk.length;
}

// ❌ 避免:低效的拼接方式
let merged = new Uint8Array(0);
for (const chunk of chunks) {
  const temp = new Uint8Array(merged.length + chunk.length);
  temp.set(merged);
  temp.set(chunk, merged.length);
  merged = temp;
}

常见问题

Q: 如何获取字节跳动 TTS 的 App ID 和 Access Token?

A: 请访问字节跳动火山引擎注册账号并创建应用,获取相关凭证。

Q: 流式合成和非流式合成有什么区别?

A:

  • 非流式合成:等待完整音频生成后返回,适合短文本或需要完整音频的场景
  • 流式合成:边合成边返回音频块,适合长文本或需要实时播放的场景

Q: 支持哪些音频编码格式?

A: 支持 wav、mp3、ogg_opus 等格式,具体取决于 TTS 平台支持。

Q: 如何调整语音的语速、音调和音量?

A: 在配置中设置 speed(语速)、pitch(音调)、volume(音量)参数:

config: {
  audio: {
    speed: 1.2,    // 语速 1.2 倍
    pitch: 2.0,    // 音调 +2
    volume: 1.5    // 音量 1.5 倍
  }
}

许可证

MIT

相关资源

作者: xiaozhi-client 版本: 0.0.1

Keywords

tts

FAQs

Package last updated on 16 Mar 2026

Related posts