用户指南 API 参考

实时语音合成 (TTS)

流式传输文本,并即时获取原始 PCM 或 MP3 音频数据。

WSS /api/v1/tts/ws/

建立双向 WebSocket 连接,实现实时流式文本转语音。支持分段发送文本(非常适合 LLM 输出)并持续接收二进制音频流。额度计算方式与 HTTP 接口相同。

⚡ 延迟优化 (TTFB)

若要实现超低延迟(首字节时间约为 200 毫秒),请确保文本片段以硬标点符号(句号 .、感叹号 ! 或问号 ?)结尾。AI 会在检测到句子边界之前自动缓冲文本,以确保呈现出完美自然的语调。

身份验证

在 WebSocket 握手期间使用 Authorization 标头进行身份验证:

Authorization: Bearer YOUR_API_KEY

连接设置

连接后,发送一条包含所需配置的初始化消息:

{
  "type": "init",
  "voice_id": 1710,
  "prerecording": 1,
  "output_format": "pcm_24000",
  "voice_settings": {
    "temperature": 25,
    "similarity": 100,
    "expressiveness": 0,
    "speed": 1.0,
    "fast_mode": 2
  }
}

初始化参数

参数 必填 类型 描述
type 必需的 字符串 初始化消息必须为 "init"。
voice_id 必需的 整数 来自……的语音识别 语音列表.
output_format 可选的 字符串 所需的音频格式。默认为 pcm_24000。选项包括:
  • MP3: mp3_22050_32, mp3_44100_32, mp3_44100_64, mp3_44100_96, mp3_44100_128, mp3_44100_192
  • PCM: pcm_8000, pcm_16000, pcm_22050, pcm_24000, pcm_44100
若需实现零延迟的应用流式传输,请使用 PCM;若需保存文件,请请求 MP3 格式。
prerecording 可选的 整数 1(普通,默认)或 2(AI 增强)。会影响所需点数。
voice_settings 可选的 物体 语音自定义选项。请参阅下方的“语音设置”部分。

语音设置对象

参数 类型 范围 默认 描述
temperature 整数 0 – 100 25 较低 = 稳定且可预测;较高 = 富有创意且多样化。
similarity 整数 0 – 100 100 语音与原始克隆声音的匹配程度。
speed 漂浮 0.5 – 2.0 1.0 播放速度倍率
fast_mode 整数 0, 1, or 2 0 显著降低延迟。
  • 0: 已禁用(最高质量)
  • 1: Flash 模式(中等延迟降低)
  • 2: Turbo 模式(最大程度降低延迟)

流式处理流水线

1. 发送文本片段

在 LLM 生成文本时逐步发送:

{"type": "text", "data": "مرحبا بكم."}

服务器将立即处理该数据片段,扣除对应时长的点数,并开始流式传输音频。

2. 信号完成(停止)

当文本生成完全结束后,发送停止信号:

{"type": "stop"}

服务器将完成缓冲区内剩余文本的合成,将最终的音频数据发送给您,并发送流结束信号。

接收消息

服务器通过 WebSocket 推送两种类型的帧:

1. 二进制音频数据块 (PCM / MP3)

原始音频字节在生成的同时即时流式传输。具体格式取决于您请求的 `output_format`(默认为 24kHz 16-bit PCM)。如果您请求的是 MP3 格式,则可以直接将这些字节写入文件。

2. JSON 控制消息

初始化就绪:

{"status": "ready", "message": "已初始化,准备接收文本。"}

流结束(安全关闭信号):

{"type": "end_of_stream"}

当您收到此消息时,即从数学上保证 100% 的音频分块已送达。此时您可以安全地在客户端关闭 WebSocket 连接。

流处理中途错误:

{"error": "直播期间积分不足"}

生产就绪示例

# pip install websockets
import asyncio
import websockets
import json

API_KEY = "YOUR_API_KEY"
url = "wss://api.moknah.io/api/v1/tts/ws/"

async def stream_tts():
    headers = {"Authorization": f"Bearer {API_KEY}"}
    async with websockets.connect(url, additional_headers=headers) as ws:
        # 1. Initialize session (Requesting MP3 so we can save to file)
        await ws.send(json.dumps({
            "type": "init",
            "voice_id": 1710,
            "output_format": "mp3_44100_128",
            "voice_settings": {"fast_mode": 2}
        }))
        print(f"Server: {await ws.recv()}")

        # 2. Receiver Task
        async def receive_audio():
            with open("audio.mp3", "wb") as f:
                async for msg in ws:
                    if isinstance(msg, bytes):
                        f.write(msg)
                    else:
                        data = json.loads(msg)
                        if data.get("type") == "end_of_stream":
                            print("✅ Stream finished safely.")
                            await ws.close()
                            break

        receiver = asyncio.create_task(receive_audio())

        # 3. Send Text
        for chunk in ["مرحبا بكم.", "في منصة مُكنة."]:
            await ws.send(json.dumps({"type": "text", "data": chunk}))
            await asyncio.sleep(0.5)

        # 4. Signal Stop
        await ws.send(json.dumps({"type": "stop"}))
        await receiver

asyncio.run(stream_tts())
// npm install ws
const WebSocket = require('ws');
const fs = require('fs');

const API_KEY = "YOUR_API_KEY";
const ws = new WebSocket('wss://api.moknah.io/api/v1/tts/ws/', {
    headers: { "Authorization": `Bearer ${API_KEY}` }
});

const audioStream = fs.createWriteStream('audio.mp3');

ws.on('open', function open() {
    // Requesting MP3 to safely write to an MP3 file
    ws.send(JSON.stringify({
        type: "init",
        voice_id: 1710,
        output_format: "mp3_44100_128",
        voice_settings: { fast_mode: 2 }
    }));
});

ws.on('message', function incoming(data, isBinary) {
    if (isBinary) {
        audioStream.write(data); // Save MP3 chunks
    } else {
        const msg = JSON.parse(data.toString());
        if (msg.status === 'ready') {
            console.log("Ready! Sending text...");
            ws.send(JSON.stringify({ type: "text", data: "مرحبا بكم." }));
            setTimeout(() => ws.send(JSON.stringify({ type: "text", data: "في منصة مُكة." })), 500);
            setTimeout(() => ws.send(JSON.stringify({ type: "stop" })), 1000);
        } else if (msg.type === 'end_of_stream') {
            console.log("✅ All audio received. Closing connection.");
            ws.close();
        }
    }
});
<?php
// composer require textalk/websocket
require 'vendor/autoload.php';
use WebSocket\Client;

$apiKey = "YOUR_API_KEY";
$client = new Client("wss://api.moknah.io/api/v1/tts/ws/", [
    'headers' => ['Authorization' => "Bearer $apiKey"],
    'timeout' => 60
]);

// 1. Initialize (Requesting MP3 format)
$client->text(json_encode([
    "type" => "init",
    "voice_id" => 1710,
    "output_format" => "mp3_44100_128",
    "voice_settings" => ["fast_mode" => 2]
]));
echo "Server: " . $client->receive() . "\n";

// 2. Send Text Chunks
$client->text(json_encode(["type" => "text", "data" => "مرحبا بكم."]));
$client->text(json_encode(["type" => "text", "data" => "في منصة مُكنة."]));
$client->text(json_encode(["type" => "stop"]));

// 3. Receive Audio & Control Messages
$audioFile = fopen("audio.mp3", "wb");
while (true) {
    $message = $client->receive();
    $decoded = json_decode($message, true);

    if ($decoded && isset($decoded['type']) && $decoded['type'] == 'end_of_stream') {
        echo "✅ Stream finished successfully.\n";
        break;
    } elseif (!$decoded) {
        // If it can't be decoded as JSON, it's our binary MP3 audio chunk
        fwrite($audioFile, $message);
    }
}
fclose($audioFile);
$client->close();
?>
# Standard cURL does not support bidirectional WebSockets easily.
# Use 'websocat' to test WebSockets from the command line:

websocat wss://api.moknah.io/api/v1/tts/ws/ \
  -H "Authorization: Bearer YOUR_API_KEY"

# Once connected, paste the JSON payloads interactively:
{"type": "init", "voice_id": 1710, "output_format": "mp3_44100_128"}

{"type": "text", "data": "مرحبا بكم."}

{"type": "stop"}

关闭代码与错误

WebSocket 连接可能会以以下标准代码之一关闭:

代码 类型 描述
1000 成功 流自然结束。
3011 生成错误 上游 API 音频生成失败。请重试请求。
4001 缺少 API 密钥 Authorization 标头中未提供 API 密钥。
4002 认证失败 密钥无效、账户未激活、初始额度不足或受 IP 限制。
4290 并发限制 该用户已有一个活跃的 TTS WebSocket 连接。每个用户仅限建立一个连接。
API 支持

如有关于 API 的问题或疑问,请联系我们: api@moknah.io.