同声传译

简介

同声传译通过一个 WebSocket 持续接收语音,并在同一个连接上返回双语字幕和译文语音。麦克风音频以二进制帧上行;字幕是 JSON 事件,译文语音以二进制帧下行。

WebSocket 地址

接口调用连接 wss://api.anycap.io/v1/audio/ast/stream,在握手请求头里带 Authorization: Bearer <接入密钥>。公开调用不得带 Cookie 或 Origin。使用同一密钥调用 GET /v1/audio/ast/sessions 查看会话列表、GET /v1/audio/ast/sessions/{session_id} 读取会话详情。控制台页面连接 wss://<控制台 API 主机>/api/v1/capabilities/audio.ast/stream,使用会话 Cookie:浏览器 WebSocket 无法设置请求头,服务端只放行 allowlist 中的 Origin。

JS
import WebSocket from "ws";import { readFileSync, writeFileSync, appendFileSync } from "node:fs"; const base = process.env.ANYCAP_API_BASE || "https://api.anycap.io";const headers = { Authorization: `Bearer ${process.env.ANYCAP_API_KEY}` };const response = await fetch(`${base}/v1/audio/ast/models`, { headers });if (!response.ok) throw new Error(`Discovery failed: ${response.status}`);const { items } = await response.json();const source = items.find((item) => item.resource_id === process.env.ANYCAP_AST_SOURCE_ID) || items[0];if (!source) throw new Error("No authorized interpretation source"); // Raw 16 kHz mono s16le PCM, without a WAV header. Install: npm install wsconst pcm = readFileSync(process.env.ANYCAP_PCM_FILE || "speech.pcm");const seconds = Math.min(60, source.max_session_seconds);if (!pcm.length || pcm.length % 2 || pcm.length > seconds * 32000) {  throw new Error("PCM must contain whole samples within the session limit");}const url = new URL("/v1/audio/ast/stream", base);url.protocol = url.protocol === "https:" ? "wss:" : "ws:";url.search = new URLSearchParams({  resource_id: source.resource_id, mode: "s2s", source: "zh", target: "en",  seconds: String(seconds), audio_format: "pcm", audio_rate: "24000",}).toString();const socket = new WebSocket(url, { headers }); // No Cookie or Origin.let timer;let terminal = false;writeFileSync("translated.pcm", Buffer.alloc(0));socket.on("message", (data, binary) => {  if (binary) return appendFileSync("translated.pcm", data);  const event = JSON.parse(data.toString());  if (event.type === "status" && !timer) {    // Start audio only after status, then send at approximately real-time speed.    let offset = 0;    timer = setInterval(() => {      if (offset >= pcm.length) {        clearInterval(timer);        socket.send(JSON.stringify({ type: "finish" }));        return;      }      socket.send(pcm.subarray(offset, offset + 2560));      offset += 2560;    }, 80);  }  if (event.type === "subtitle") console.log(event.channel, event.phase, event.text || "");  if (event.type === "done" || event.type === "error") {    terminal = true;    clearInterval(timer);    console.log(event);    socket.close();  }});socket.on("unexpected-response", (_request, response) => {  console.error("Handshake rejected:", response.statusCode);  response.setEncoding("utf8");  response.on("data", (body) => console.error(body));});socket.on("error", (error) => console.error(error.message));socket.on("close", () => {  clearInterval(timer);  if (!terminal) console.error("Connection ended without a terminal event; query the session before retrying.");});

查询参数

参数说明
resource_id
从 GET /v1/audio/ast/models 获取的已授权来源 UUID,必需。
mode
s2s 同时返回译文语音和字幕;s2t 只返回字幕。
source
必填,源语言代码,见下方语种表。
target
必填,目标语言代码,见下方语种表。
speaker_id
可选 zh_female_vv_uranus_bigtts 或 zh_male_jingqiangkanye_emo_mars_bigtts,仅 s2s;不传则复刻源说话人的音色。
seconds
输入音频时长额度。省略或传 0 使用来源默认值;正数小于 10 按 10 秒,大于来源上限按该上限;小于 0 或大于 600 拒绝。输入结束后发送 finish。
audio_format
pcm(默认):24 kHz 为 float32、16 kHz 为有符号 int16,均为单声道小端;ogg_opus 固定返回 48 kHz Ogg Opus。
audio_rate
PCM 选 16000 或 24000(默认);Ogg Opus 固定 48000,忽略传入值。

客户端发给服务端

  • 二进制帧为 16 kHz 单声道 s16le 麦克风音频。建议每帧 80 ms(2,560 字节),上限 64 KB。
  • {"type":"finish"} 表示说完了,服务端把剩余字幕和语音发完后再发 done。
  • {"type":"cancel"} 立即结束本次会话。
  • 收到 status 后再发送音频。额度按上传 PCM 字节数计算,不是连接的墙钟时长;超出额度的帧会停止输入并返回 budget_exhausted 终态错误。

服务端发给客户端

文本帧是 JSON 事件;二进制帧是按 target_format 编码的译文语音,用 status 里的采样率解码。数值为 0 或布尔值为 false 时字段可能省略,按 0/false 处理。

事件字段说明
statusstate、session_id、mode、max_seconds、reserved_credits、target_format、target_rate连接建立后的第一个事件,收到后再发送音频。reserved_credits 是本次预留积分;用 target_format 和 target_rate 解码音频。
subtitlechannel(source/translation)、phase(start/partial/final)、text、start_ms、end_ms、speaker_changed、detected_languagestart 开始一句,partial 覆盖当前句文本,final 定稿并追加该行。
audiophase(start/end)、start_ms、end_ms标记一句译文语音的起止;音频本身以二进制帧下发。
mutedmuted_ms这段输入是静音或未被识别,没有产生译文。
doneaudio_seconds、charged_credits,可选 usage终态事件,给出实际语音秒数与扣费;随后连接关闭。
errorcode、message终态事件。以 code 为准;已经开始计费的会话不要静默重试。

语种约束

模式源语言目标语言
s2s,不传 speaker_idzh、en、de、fr、es、id、ja、ptzh、en、de、fr、es、id、ja、pt
s2s,传 speaker_id下列 s2t 的二十种源语言及两个方言zh、en
s2tzh、en、de、fr、es、id、ja、pt、ko、tr、ms、nl、ro、pl、cs、ar、th、vi、ru、it,以及 yue-CN、sh-CN上述二十种语言,不含两个方言

源语言和目标语言同时传 zhen 时支持中英反转互译;其他组合要求中文或英文至少在一侧。方言源语言不算这一侧,所以 yue-CN 或 sh-CN 的目标语言必须是中文或英文。不支持的语种组合返回 invalid_input。

错误

按 token 定价的来源使用上报的 input_audio_tokens、output_text_tokens、output_audio_tokens 结算,数量以十进制字符串返回,可含小数;三类金额精确合计后仅向上取整一次到整积分。audio_seconds 只表示时长,不决定 token 费用。status.token_allowance(若有)是预留额度;用量缺失、异常或超过额度时返回待核对错误,不应重新提交该会话。原有按秒来源保持原结算方式。

WebSocket 建立前的拒绝是普通 HTTP 响应,正文为 {"error":{"code":...}};建立后的失败以 error 事件下发,code 可能为 budget_exhausted、provider_failed、transport_error、settlement_error、missing_usage、invalid_usage 或 usage_exceeds_limit。

错误码状态码说明
temporarily_unavailable503实时连接未能建立。
price_unavailable503没有可调用的已发布价格。
not_found404来源不存在。
insufficient_credits402余额不足以覆盖预留。
rate_limited429并发会话过多。
capability_unavailable503同传服务暂时不可用,请稍后重试。
forbidden403密钥、账号、来源或 Origin 不被允许。
unauthorized401密钥或控制台会话缺失或已过期。
invalid_input400查询参数、来源或语种组合不被支持。
下一篇视频生成