实时文字转语音 v2
通过公开的 Realtime TTS WebSocket v2 协议流式生成语音。
Realtime TTS v2 仅为现有客户端保留;新接入请使用上级 v3 页面。默认简单模式只需发送一次输入;需要显式顺序、幂等和断线恢复时再启用可靠模式。
接口与子协议
| 品牌 | 接口地址 |
|---|---|
| Fish Audio | wss://realtime.fishaudio.org/v2/tts/live |
连接必须声明 WebSocket 子协议 realtime.tts.msgpack.v2。客户端和服务端的应用层消息均为 MessagePack 二进制帧,不是 JSON 文本帧。
现有 /v1/tts/live 接入仍继续提供兼容服务,但 v1 已冻结。新接入应使用上级 v3 协议;本页仅用于维护 v2 客户端。
站点能力
| 站点 | 供应商 | 标准模型 | 格式 |
|---|---|---|---|
| Fish Audio | FishAudio | fishaudio-s21pro-flash | mp3, wav, opus |
公开 voice_id 必须支持所选 model。建连前可通过 GET /v2/tts/capabilities 获取当前站点支持的供应商、模型、格式、参数及 PCM 元数据。超出当前站点能力矩阵的组合会被明确拒绝。
鉴权
服务端应用应在 WebSocket 握手阶段传递 API 密钥:
Authorization: Bearer API_KEY
Sec-WebSocket-Protocol: realtime.tts.msgpack.v2浏览器原生 WebSocket API 无法设置 Authorization 请求头。可信服务端可用 API Key 换取绑定 Origin 的一次性票据:
POST /v2/tts/browser-tickets
Authorization: Bearer API_KEY
Content-Type: application/json
{"origin":"https://your-app.example"}浏览器再使用 v2 子协议连接,并把返回票据编码成首个 MessagePack 帧:
{ event: 'auth', token: 'rtv2_ticket_...' }必须在鉴权超时前发送。票据只能使用一次、短期有效,并绑定站点、Host 与 Origin。不要把凭证放入 URL,服务端会拒绝 token 和 api_key 查询参数。正式浏览器产品不得把长期 API 密钥下发给浏览器。
快速接入:简单模式
连接 → authenticated → start → ready → input → audio(一个或多个)→ finish省略 start.mode 即使用简单模式。服务端生成请求、事件、顺序和分段 ID。收到 ready 后发送一次 { event: 'input', text };默认 commit: true,生成完成后自动结束。客户端只需处理 ready、audio、error、finish,确认和用量事件可以忽略。
{ event: 'start', request: { voice_id: '公开音色 UUID', model: 'fishaudio-s21pro-flash', format: 'mp3' } }
{ event: 'input', text: '你好,这是 Realtime TTS v2。' }需要分多帧提交文本时,可先发送多个 commit: false 的 input,最后发送一次 commit: true。一个 WebSocket 连接只承载一次生成请求。
客户端事件包括 auth、start、input、text、flush、stop、ping。简单模式通常只使用 start 和 input;text、flush、stop 属于可靠模式。
客户端事件入参
所有客户端事件都是 MessagePack Map。未知字段会被拒绝;字段名统一使用 snake_case,文本值必须是有效的 UTF-8 字符串。
| 事件 | 适用模式 | 必填字段 | 可选字段与默认值 | 约束与行为 |
|---|---|---|---|---|
auth | 选择模式前 | event、token | event_id | 仅在握手没有携带 Authorization 时使用;token 是一次性浏览器票据。 |
start | 简单、可靠 | event、request | mode: "simple"、event_id、request_id、retry_failed | 可靠模式必须提供 event_id 和 request_id;retry_failed: true 仅限可靠模式。 |
input | 简单 | event、text | event_id、commit: true | text 不得为空;整个缓冲请求最多 10,000 个字符。 |
text | 可靠 | event、event_id、sequence、text | 无 | sequence 是正整数且必须等于服务端期待的下一个值;每帧最多 2,000 个字符。 |
flush | 可靠 | event、event_id、segment_id | 无 | 提交非空缓冲区;segment_id 是该分段唯一的 1–128 字符幂等键。 |
stop | 可靠 | event、event_id | 无 | 等所有已接受分段完成后结束本次请求。 |
ping | 简单、可靠 | event;可靠模式还需 event_id | 简单模式可带 event_id,并可带 timestamp | 鉴权完成后可发送,服务端返回 pong。 |
event 必须等于第一列中的事件名。可靠模式从 start 开始的每个客户端事件都必须使用唯一 event_id。简单模式中的请求、事件、顺序和分段 ID 由服务端生成,客户端无需回传。
可靠模式
需要显式多分段控制和断线恢复时,发送 { event: 'start', mode: 'reliable', event_id, request_id, request }。可靠模式从 start 开始要求每个客户端事件携带唯一 event_id,每个 text 携带严格递增的 sequence,每个 flush 携带唯一 segment_id。
start → ready → text → input_ack → flush → segment_accepted
→ audio → usage → segment_completed → stop → finishrequest_id 长度为 1–128 个字符,是账号、站点和域名范围内的幂等键。重复提交相同请求返回 request_status,不会再次调用供应商或重复扣费。
start.request 入参
| 字段 | 必填 | 类型与约束 |
|---|---|---|
parameter_profile | 否 | 省略时保持旧 v2;设为 product_v1 启用统一产品参数 |
voice_id | 是 | 音色目录返回的公开音色 UUID |
model | 是 | 公共模型 ID;当前默认模型为 fishaudio-s21pro-flash |
format | 否 | GET /v2/tts/capabilities 返回的格式 |
speed | 否 | 0.5 到 2 的数字 |
volume | 否 | 旧 v2 为 0 到 10;product_v1 为 -20 到 20 |
stability | 否 | product_v1 稳定性;数值越高越稳定 |
similarity | 否 | product_v1 音色相似度 |
pitch | 否 | product_v1 音高;仅模型支持时生效 |
language | 否 | 1–32 个字符的语言提示 |
chunk_length | 否 | 50 到 1000 的整数 |
latency | 否 | 仅在所选供应商和模型的能力列表中存在时可用 |
emotion | 否 | 仅在所选供应商和模型的能力列表中存在时可用 |
instructions | 否 | 仅在所选供应商和模型的能力列表中存在时可用 |
optimize_instructions | 否 | 仅在所选供应商和模型的能力列表中存在时可用 |
text_normalization | 否 | product_v1 文本归一化开关;仅模型支持时生效 |
默认 profile 保持旧 v2 的严格行为。使用 parameter_profile: "product_v1" 时,服务端会把支持的参数限制到模型范围,忽略模型不支持的参数,并通过 ready.effective_request 返回最终生效值。
示例:
{
event: 'start',
request: {
voice_id: '00000000-0000-0000-0000-000000000000',
model: 'fishaudio-s21pro-flash',
parameter_profile: 'product_v1',
format: 'mp3',
speed: 1,
volume: 0,
stability: 1,
similarity: 1,
text_normalization: true,
language: 'zh',
chunk_length: 200,
latency: 'normal'
}
}服务端事件
每个服务端事件都包含唯一 event_id、链路 trace_id 和 Unix 毫秒 timestamp。直接响应客户端事件时还包含 in_reply_to。
| 事件 | 主要字段 |
|---|---|
authenticated | session_id、site、protocol_version、quota |
ready | mode、request_id、session_id、provider、model、voice_id、format、PCM 元数据 |
input_ack | request_id、已接收的 sequence 和字符数、当前缓冲字符数、剩余容量 |
segment_accepted | request_id、segment_id、分段字符数和预留计费单位 |
audio | request_id、session_id、segment_id、递增的 sequence、二进制 audio、format、PCM 元数据 |
usage | request_id、segment_id、segment_units、session_units、quota_remaining、audio_bytes |
segment_completed | request_id、segment_id、分段计费单位、音频字节数 |
request_status | 重复请求的 state、retryable、units、result_available;不会重新生成 |
warning | request_id?、code、message |
error | request_id?、session_id?、稳定的 code、message、retryable、terminal、stage?、path? |
finish | request_id、session_id、reason、units;终态原因为 completed、error、timeout 或 server_draining |
pong | session_id、request_id?、timestamp |
audio.audio 是 MessagePack 帧内的二进制字节数组,不是 Base64。客户端应按照 sequence 顺序拼接所选输出格式的音频帧。当 format 为 pcm 时,ready 和 audio 都会携带必需的 sample_rate、channels 和 bit_depth,客户端可以据此生成 WAV 文件头或初始化 PCM 播放器。
错误码
每个 error 事件都有稳定的 code、安全的 message、retryable 和 terminal。terminal: false 表示客户端可修正后继续当前连接;terminal: true 表示本会话终止。stage 和 path 用于定位失败阶段与字段。当前错误码如下:
| 类别 | 错误码 |
|---|---|
| 鉴权 | authentication_failed、authentication_required、already_authenticated、permission_denied |
| 消息状态 | invalid_message、invalid_messagepack、invalid_state、idempotency_conflict、empty_segment、segment_too_large |
| 限制 | session_limit_exceeded、quota_exceeded、client_backpressure |
| 能力 | unsupported_parameter、voice_not_found、model_not_found |
| 供应商 | provider_unavailable、provider_timeout、generation_failed |
| 生命周期 | billing_failed、connection_closed、internal_error |
能力与请求状态
客户端可以在建连前读取 GET /v2/tts/capabilities,按站点返回可用供应商、模型、格式、参数、限制以及 simple、reliable 两种模式。
可靠模式客户端在网络中断后可查询 GET /v2/tts/requests/{request_id}。返回状态为 running、completed、failed_retryable 或 failed_terminal。result_available=true 时响应包含 segments 清单。
要重新执行 failed_retryable 请求,重新连接后在 start 中复用同一 request_id 并设置 retry_failed: true。每个重试分段必须复用相同 segment_id、顺序和完整文本,否则返回 idempotency_conflict。
Node.js 最小示例
安装 ws 和 @msgpack/msgpack:
import { writeFileSync } from 'node:fs';
import { decode, encode } from '@msgpack/msgpack';
import WebSocket from 'ws';
const audio = [];
const ws = new WebSocket('wss://realtime.fishaudio.org/v2/tts/live', 'realtime.tts.msgpack.v2', {
headers: { Authorization: `Bearer ${process.env.API_KEY}` },
});
ws.on('message', (frame) => {
const event = decode(frame);
if (event.event === 'authenticated') {
ws.send(
encode({
event: 'start',
request: {
voice_id: process.env.VOICE_ID,
model: 'fishaudio-s21pro-flash',
format: 'mp3',
},
}),
);
} else if (event.event === 'ready') {
ws.send(encode({ event: 'input', text: '你好,这是 Realtime TTS v2。' }));
} else if (event.event === 'audio') {
audio.push(event.audio);
} else if (event.event === 'error') {
console.error(event.code, event.message, event.retryable, event.terminal, event.trace_id);
} else if (event.event === 'finish') {
writeFileSync('realtime-output.mp3', Buffer.concat(audio.map((chunk) => Buffer.from(chunk))));
console.log({ requestId: event.request_id, units: event.units });
ws.close(1000);
}
});限制、计费与重试
简单模式中,commit: true 的 input 形成一个计费分段并自动结束。可靠模式中,每个非空 flush 形成独立计费分段,客户端应等待 input_ack 和 segment_completed 后再继续。服务端在调用供应商前预留额度,在第一块有效音频交付时提交计费。
协议默认限制为:简单模式每次输入最多 10,000 字符;可靠模式每个 text 最多 2,000 字符、每个分段最多 10,000 字符;每个会话最多 100,000 计费单位。
只有 retryable: true 的错误或 WebSocket 关闭码 1012、1013 可以自动重试,并应使用带随机抖动的指数退避。只有用户明确发起一次新生成时才创建新的 request_id,不要通过更换 ID 绕过重复保护来重放状态不确定的请求。
握手失败会在 101 Switching Protocols 之前以 HTTP 响应返回;会话内失败通过 MessagePack error 事件返回,随后关闭连接。