# 同声传译 API 文档

# 接口说明

内容 说明
传输方式 ws[s] (为提高安全性,强烈推荐wss)
请求地址 [ws(s)]: //ws-api.xf-yun.com/v1/private/simult_interpretation
注:服务器IP不固定,为保证您的接口稳定,请勿通过指定IP的方式调用接口,使用域名方式调用
请求行 GET /v1/private/simult_interpretation HTTP/1.1
接口鉴权 签名机制,详情请参照下方鉴权说明
字符编码 UTF-8
响应格式 统一采用JSON格式
开发语言 任意,只要可以向讯飞云服务发起WebSocket请求的均可
适用范围 任意操作系统,但因不支持跨域不适用于浏览器
音频属性 采样率16k、位长16、单声道
音频格式 pcm
数据发送 建议音频流每40ms发送1280字节
语言种类 支持中文普通话同声传译为英文发音

# 鉴权认证

在调用业务接口时,请求方需要对请求进行签名,服务端通过签名来校验请求的合法性。
通过在请求地址后面加上鉴权相关参数的方式,请注意影响鉴权结果的值有url、apiSecret、apiKey、date,如果调试鉴权,请务必按照示例中给的值进行调试,具体参数如下:
http示例url:

https://ws-api.xf-yun.com/v1/private/simultaneous_translation?authorization=YXBpX2tleT0iYXBpa2V5WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFgiLCBhbGdvcml0aG09ImhtYWMtc2hhMjU2IiwgaGVhZGVycz0iaG9zdCBkYXRlIHJlcXVlc3QtbGluZSIsIHNpZ25hdHVyZT0iWkIvWXprQnUwTUV5NEhONVMyd0xQRGxPdGVVK01oQjZ3aWRNaCszNnhLMD0i&host=ws-api.xf-yun.com&date=Mon%2C+13+Dec+2021+03%3A37%3A23+GMT&serviceId=simult_interpretation

鉴权参数:

参数 类型 必须 说明 示例
host string 请求主机 itrans.xf-yun.com
date string 当前时间戳,RFC1123格式("EEE, dd MMM yyyy HH:mm:ss z") Mon, 13 Dec 2021 03:37:23 GMT
authorization string 使用base64编码的签名相关信息(签名基于hamc-sha256计算) 参考下方详细生成规则

• date参数生成规则:

date必须是UTC+0或GMT时区,RFC1123格式(Mon, 13 Dec 2021 03:37:23 GMT)。
服务端会对date进行时钟偏移检查,最大允许300秒的偏差,超出偏差的请求都将被拒绝。

• authorization参数生成格式:

1)获取接口密钥APIKey 和 APISecret。
在讯飞开放平台控制台,创建一个应用后打开同声传译页面可以获取,均为32位字符串。
2)参数authorization base64编码前(authorization_origin)的格式如下。

api_key="$api_key",algorithm="hmac-sha256",headers="host date request-line",signature="$signature"

其中 api_key 是在控制台获取的APIKey,algorithm 是加密算法(仅支持hmac-sha256),headers 是参与签名的参数(见下方注释)。
signature 是使用加密算法对参与签名的参数签名后并使用base64编码的字符串,详见下方。

注: headers是参与签名的参数,请注意是固定的参数名("host date request-line"),而非这些参数的值。

3)signature的原始字段(signature_origin)规则如下。

signature原始字段由 host,date,request-line三个参数按照格式拼接成,
拼接的格式为(\n为换行符,’:’后面有一个空格):

host: $host\ndate: $date\n$request-line

假设

请求url = "https://ws-api.xf-yun.com/v1/private/simultaneous_translation"
date = "Mon, 13 Dec 2021 03:37:23 GMT"

那么 signature原始字段(signature_origin)则为:

host: ws-api.xf-yun.com
date: Mon, 13 Dec 2021 03:37:23 GMT
GET /v1/private/simultaneous_translation HTTP/1.1

4)使用hmac-sha256算法结合apiSecret对signature_origin签名,获得签名后的摘要signature_sha。

signature_sha=hmac-sha256(signature_origin,$apiSecret)

其中 apiSecret 是在控制台获取的APISecret

5)使用base64编码对signature_sha进行编码获得最终的signature。

signature=base64(signature_sha)

假设

APISecret = "apisecretXXXXXXXXXXXXXXXXXXXXXXX"	
date = "Mon, 13 Dec 2021 03:37:23 GMT"

则signature为

signature="ZB/YzkBu0MEy4HN5S2wLPDlOteU+MhB6widMh+36xK0="

6)根据以上信息拼接authorization base64编码前(authorization_origin)的字符串,示例如下。

api_key="apikeyXXXXXXXXXXXXXXXXXXXXXXXXXX", algorithm="hmac-sha256", headers="host date request-line", signature="ZB/YzkBu0MEy4HN5S2wLPDlOteU+MhB6widMh+36xK0="

注: headers是参与签名的参数,请注意是固定的参数名("host date request-line"),而非这些参数的值。

7)最后再对authorization_origin进行base64编码获得最终的authorization参数。

authorization = base64(authorization_origin)
示例结果为:
authorization=YXBpX2tleT0iYXBpa2V5WFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFgiLCBhbGdvcml0aG09ImhtYWMtc2hhMjU2IiwgaGVhZGVycz0iaG9zdCBkYXRlIHJlcXVlc3QtbGluZSIsIHNpZ25hdHVyZT0iWkIvWXprQnUwTUV5NEhONVMyd0xQRGxPdGVVK01oQjZ3aWRNaCszNnhLMD0i

# 鉴权结果

如果鉴权失败,则根据不同错误类型返回不同HTTP Code状态码,同时携带错误描述信息,详细错误说明如下:

HTTP Code 说明 错误描述信息 解决方法
401 缺少authorization参数 {"message":"Unauthorized"} 检查是否有authorization参数,详情见authorization参数详细生成规则
401 签名参数解析失败 {“message”:”HMAC signature cannot be verified”} 检查签名的各个参数是否有缺失是否正确,特别确认下复制的api_key是否正确
401 签名校验失败 {“message”:”HMAC signature does not match”} 签名验证失败,可能原因有很多。 1. 检查api_key,api_secret 是否正确。 2.检查计算签名的参数host,date,request-line是否按照协议要求拼接。 3. 检查signature签名的base64长度是否正常(正常44个字节)。
403 时钟偏移校验失败 {“message”:”HMAC signature cannot be verified, a valid date or x-date header is required for HMAC Authentication”} 检查服务器时间是否标准,相差5分钟以上会报此错误

时钟偏移校验失败示例:

HTTP/1.1 403 Forbidden
Date: Mon, 30 Nov 2020 02:34:33 GMT
Content-Length: 116
Content-Type: text/plain; charset=utf-8
{
    "message": "HMAC signature does not match, a valid date or x-date header is required for HMAC Authentication"
}

# 请求参数

在调用业务接口时,都需要在 Http Request Body 中配置以下参数,请求数据均为json字符串。
请注意status的第一次请求取值为0,中间请求取值为1,最后一次请求取值为2。
请求参数示例:

{
  "header": {
    "app_id":"your_app_id",
    "status": 0,
    
  },
  "parameter": {
    "ist": {
      "accent": "mandarin",
      "domain": "ist_ed_open",
      "language": "zh_cn",
      "vto": 15000,
      "eos": 150000
    },
    "streamtrans": {
      "from": "cn",
      "to": "en"
    },
    "tts": {
      "vcn": "x2_john",
      "tts_results": {
        "encoding": "raw",
        "sample_rate": 16000,
        "channels": 1,
        "bit_depth": 16,
        "frame_size": 0
      }
    }
  },
  "payload": {
    "data": {
      "audio": "JiuY3iK9AAB...",
      "encoding": "raw",
      "sample_rate": 16000,
      "seq": 1,
      "status": 0
    }
  }
}

请求参数说明:

参数名 类型 必传 描述
header object 用于上传平台参数
header.app_id string 在讯飞开放平台申请的appid信息
header.status int 流式接口请求状态,可选值为:0,1,2。
第一次请求值为0
中间请求值为1
最后一次请求值为2
parameter object 用于上传服务特性参数
parameter.ist object 用于上传功能参数
parameter.ist.language string 转写语种,可选值:zh_cn
parameter.ist.language_type int 语言过滤筛选
1:中英文模式,中文英文均可识别(默认)
2:中文模式,可识别出简单英文
3:英文模式,只识别出英文
4:纯中文模式,只识别出中文
注意:中文引擎支持该参数,其他语言不支持。
parameter.ist.domain string 应用领域,可选值:ist_ed_open
parameter.ist.accent srting 口音取值范围,目前固定为mandarin
parameter.ist.eos int 用于设置端点检测的静默时间,单位是毫秒。
即静默多长时间后引擎认为音频结束,取值范围0~99999999
parameter.ist.vto int vad强切控制,单位毫秒,默认15000
parameter.ist.nunum int 将返回结果的数字格式规则为阿拉伯数字格式,默认开启
0:关闭
1:开启
parameter.streamtrans object 功能参数
parameter.streamtrans.from string 源语种
parameter.streamtrans.to string 目标语种
parameter.tts object tts功能参数
parameter.tts.vcn string 对应同传发音人,有以下可选值:
英文女性:x2_catherine
英文男性:x2_john
成年女性:x2_xiaoguo
成年男性:x2_xiaozhong
儿童女声:x2_xiaofang_cts
童声开心:x2_mengmenghappy
童声自然:x2_mengmengnetural
parameter.tts.tts_results object 合成响应数据
parameter.tts.tts_results.encoding string 音频编码,注意更改生成文件的后缀(如.pcm或.mp3),可选值:
raw:合成pcm音频
lame:合成mp3音频
parameter.tts.tts_results.sample_rate int 采样率,可选值:16000
parameter.tts.tts_results.channels int 声道数,可选值:1
parameter.tts.tts_results.bit_depth int 位深,可选值:16
payload object 数据段,携带请求的数据
payload.data object 输入的音频数据格式
payload.data.audio string base64编码后输入的音频数据,数据大小建议保持在:0~10M
payload.data.encoding string 音频编码,可选值:raw (代表pcm音频)
payload.data.sample_rate int 音频采样率,可选值:16000
payload.data.seq int 标明数据为第几块,取值范围:0~9999999
payload.data.status int 数据状态:
0:开始
1:继续
2:结束

# 返回结果

如出现错误码,可到 这里 (opens new window) 查询。
返回参数示例(识别响应):

{
  "header": {
    "code": 0,
    "message": "success",
    "sid": "aso000e287f@hu17db2d3721205c3882",
    "status": 1
  },
  "payload": {
    "recognition_results": {
      "format": "json",
      "status": 1,
      "text": "eyJiZyI6MjAw...",
      "encoding": "utf8"
    }
  }
}

text字段Base64解码后示例(识别响应):

{
  "bg": 200,
  "ed": 800,
  "ls": false,
  "pgs": "rpl",
  "rg": [
    1,
    1
  ],
  "sn": 2,
  "sub_end": false,
  "ws": [
    {
      "bg": 20,
      "cw": [
        {
          "rl": 0,
          "sc": 0,
          "w": "科大讯飞",
          "wb": 20,
          "wc": 0,
          "we": 40,
          "wp": "n"
        }
      ]
    },
    {
      "bg": 40,
      "cw": [
        {
          "rl": 0,
          "sc": 0,
          "w": "是",
          "wb": 40,
          "wc": 0,
          "we": 60,
          "wp": "n"
        }
      ]
    }
  ]
}

返回参数示例(翻译响应):

{
  "header": {
    "code": 0,
    "message": "success",
    "sid": "aso000de6a1@hu17b90e965460212882",
    "status": 1
  },
  "payload": {
    "streamtrans_results": {
      "text": "eyJzcmMiOiLkuID...",
      "seq": "5",
      "status": 1,
      "encoding": "utf8",
      "format": "json",
      "compress": "raw"
    }
  }
}

text字段Base64解码后示例(翻译响应):

{
  "src": "一个面向全球的中文学习爱好者的一个",
  "dst": " A global Chinese learningenthusiasts for a",
  "wb": 10,
  "we": 2480,
  "is_final": 0
}

返回参数示例(合成响应):

{
  "header": {
    "code": 0,
    "message": "success",
    "sid": "aso000de6a1@hu17b90e965460212882",
    "status": 1
  },
  "payload": {
    "tts_results": {
      "encoding": "raw",
      "channels": "1",
      "id": "2",
      "seq": "3",
      "audio": "wRUal1l021bJlzu1wI...",
      "sample_rate": "16000",
      "status": "1",
      "bit_depth": "16",
      "type": "0",
      "ced": "68"
    }
  }
}

audio字段为合成后的音频片段,采用base64编码,base64解码后写到文件即可。

返回参数说明(识别):

参数名 类型 描述
header object 用于描述平台特性的参数
header.code int 0表示会话调用成功(并不一定表示服务调用成功,服务是否调用成功以text字段为准)
其它表示会话调用异常,详情请参考错误码
header.message string 描述信息
header.sid string 本次会话唯一标识id
header.status int 流式接口响应状态,可选值为:0,1,2。
第一次响应值为0
中间响应值为1
最后一次响应值为2
payload object 数据段,用于携带响应的数据
payload.recognition_results object 识别响应数据块
payload.recognition_results.text string 响应结果,采用base64编码。长度范围:0~1000000
payload.recognition_results.format string 文本格式
payload.recognition_results.encoding string 文本编码
payload.recognition_results.status int 数据状态
0:开始
1:继续
2:结束

text字段base64解码后信息如下(识别),请重点关注:

参数名 类型 描述
bg int 本次输入对应的开始时间戳
ed int 本次输入对应的结束时间戳
sn int 返回结果的序号
pgs string 取值为"apd"时表示该片结果是追加到前面的最终结果,取值 为"rpl" 时表示替换前面的部分结果,替换范围为rg字段。默认值为0
rg array 替换范围
sub_end bool 字句是否结果
ls bool 是否是最后一片结果
wb int 词在本句中的开始时间,单位是帧,中间结果的wb为0
we int 词在本句中的结束时间,单位是帧,中间结果的we为0
wp string 词标识
n-普通词
s-顺滑词(语气词)
p-标点
w string 词识别结果

返回参数说明(翻译):

参数名 类型 描述
header object 用于描述平台特性的参数
header.code int 0表示会话调用成功(并不一定表示服务调用成功,服务是否调用成功以text字段为准)
其它表示会话调用异常,详情请参考错误码
header.message string 描述信息
header.sid string 本次会话唯一标识id
header.status int 流式接口响应状态,可选值为:0,1,2。
第一次响应值为0
中间响应值为1
最后一次响应值为2
payload object 数据段,用于携带响应的数据
payload.streamtrans_results object 翻译结果
payload.streamtrans_results.text string 文本数据,响应结果,采用base64编码
payload.streamtrans_results.encoding string 文本编码
payload.streamtrans_results.format string 文本格式
payload.streamtrans_results.status int 数据状态
0:开始
1:继续
2:结束

text字段base64解码后信息如下(翻译),请重点关注:

参数名 类型 描述
src string 原文本
dst string 目标文本
wb int 起始偏移量,本次结果对应的开始时间戳,识别结果第一个字符相对原始音频绝对时间戳的偏移时间,单位是毫秒
we int 结束偏移量,本次结果对应的结束时间戳,识别结果第一个字符相对原始音频绝对时间戳的偏移时间,单位是毫秒
is_final int 本次结果是否是最终结果
中间结果:0
确定结果:1

返回参数说明(合成):

参数名 类型 描述
header string 用于描述平台特性的参数
header.code int 0表示会话调用成功(并不一定表示服务调用成功,服务是否调用成功以text字段为准)
其它表示会话调用异常,详情请参考错误码
header.message string 描述信息
header.sid Object 本次会话唯一标识id
header.status int 流式接口响应状态,可选值为:0,1,2。
第一次响应值为0
中间响应值为1
最后一次响应值为2
payload obejct 数据段,用于携带响应的数据
payload.tts_results object 合成结果
payload.tts_results.encoding string 音频编码,注意更改生成文件的后缀(如.pcm或.mp3),可选值:
raw:合成pcm音频
lame:合成mp3音频
payload.tts_results.sample_rate int 采样率,可选值:16000
payload.tts_results.channels int 声道数,可选值:1
payload.tts_results.bit_depth int 位深,可选值:16
payload.tts_results.status int 数据状态
0:开始
1:继续
2:结束
payload.tts_results.seq int 数据序号,标明数据为第几块 。取值范围:0~9999999
payload.tts_results.audio string 响应结果,采用base64编码。长度范围:0~1000000
payload.tts_results.frame_size int 帧大小,取值范围0~1024
# python完整示例代码
# -*- coding:utf-8 -*-
import _thread as thread
import base64
import datetime
import hashlib
import hmac
import ssl
import time
from datetime import datetime
from time import mktime
from urllib.parse import urlencode
from wsgiref.handlers import format_date_time
import websocket
import json
from tool import pcm2wav

'''
 1、同声传译接口,可以将音频流实时翻译为不同语种的文本,并输对应的音频内容,广泛应用于国际论坛、智能会议、智慧教育、跨国交流等场景。
 '''

STATUS_FIRST_FRAME = 0  # 第一帧的标识
STATUS_CONTINUE_FRAME = 1  # 中间帧标识
STATUS_LAST_FRAME = 2  # 最后一帧的标识


class Ws_Param(object):
    # 初始化
    encoding = 'raw'

    def __init__(self, AudioFile):
        # 控制台鉴权信息
        self.APPID = APPID
        self.APISecret = APISecret
        self.APIKey = APIKey
        self.Host = "ws-api.xf-yun.com"
        self.HttpProto = "HTTP/1.1"
        self.HttpMethod = "GET"
        self.RequestUri = "/v1/private/simult_interpretation"
        self.Algorithm = "hmac-sha256"
        self.url = "ws://" + self.Host + self.RequestUri
        # self.encoding = 'raw'

        # 设置测试音频文件
        self.AudioFile = audio_path

    # 生成url
    def create_url(self):
        url = self.url
        # 生成RFC1123格式的时间戳
        now = datetime.now()
        date = format_date_time(mktime(now.timetuple()))
        signature_origin = "host: " + self.Host + "\n"
        signature_origin += "date: " + date + "\n"
        signature_origin += "GET " + self.RequestUri + " HTTP/1.1"
        signature_sha = hmac.new(self.APISecret.encode('utf-8'), signature_origin.encode('utf-8'),
                                 digestmod=hashlib.sha256).digest()
        signature_sha = base64.b64encode(signature_sha).decode(encoding='utf-8')
        authorization_origin = "api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"" % (
            self.APIKey, "hmac-sha256", "host date request-line", signature_sha)
        authorization = base64.b64encode(authorization_origin.encode('utf-8')).decode(encoding='utf-8')
        v = {
            "authorization": authorization,
            "date": date,
            "host": self.Host,
            "serviceId": "simult_interpretation"
        }
        url = url + '?' + urlencode(v)
        return url

    @staticmethod
    def create_params(appid, status, audio):
        param = {
            "header": {
                "app_id": appid,
                "status": status,
            },
            "parameter": {
                "ist": {
                    "accent": "mandarin",
                    "domain": "ist_ed_open",
                    "language": "zh_cn",
                    "vto": 15000,
                    "eos": 150000
                },
                "streamtrans": {
                    "from": "cn",
                    "to": "en"
                },
                "tts": {
                    "vcn": "x2_catherine",
                    "tts_results": {
                        "encoding": "raw",
                        "sample_rate": 16000,
                        "channels": 1,
                        "bit_depth": 16,
                        "frame_size": 0
                    }
                }
            },

            "payload": {
                "data": {
                    "audio": str(base64.b64encode(audio).decode('utf-8')),
                    "encoding": "raw",
                    "sample_rate": 16000,
                    "seq": 1,
                    "status": status
                }
            }
        }
        return param


    # 收到websocket消息的处理
    def on_message(self,ws, message):
        try:
            print(f"收到的消息:{message}")

        except Exception as e:
            print("receive msg,but parse exception:", e)

        # 对结果进行解析
        message = json.loads(message)
        status = message["header"]["status"]
        sid = message["header"]["sid"]
        # 接收到的识别结果写到文本
        if "recognition_results" in message['payload']:
            result = message['payload']['recognition_results']['text']
            asrresult = base64.b64decode(result).decode()
            file1 = open('output\\text\\asr.txt', "a", encoding="UTF-8")
            file1.write(asrresult)
            file1.close()

        # 接收到的翻译结果写到文本
        if "streamtrans_results" in message['payload']:
            result = message['payload']['streamtrans_results']['text']
            transresult = base64.b64decode(result).decode()
            file1 = open('output\\text\\trans.txt', "a", encoding="UTF-8")
            file1.write(transresult)
            file1.close()

        # 把接收到的音频流合成PCM
        if "tts_results" in message['payload']:
            audio = message['payload']['tts_results']['audio']
            audio = base64.b64decode(audio)
            with open('output\\audio\\trans.pcm', 'ab') as f:
                f.write(audio)

        if status == 2:
            print("session end ")
            print("本次请求的sid==》 " + sid)
            print("数据处理完毕,等待实时转译结束!同传后的音频文件请到output/audio/目录查看...")
            time.sleep(1)
            ws.close()


    # 收到websocket错误的处理
    def on_error(self,ws, error):
        #print("### error:", error)
        pass


    # 收到websocket关闭的处理
    def on_close(self,ws):
        print("### closed ###")


    # 收到websocket连接建立的处理
    def on_open(self,ws):
        def run(*args):
            frameSize = 1280  # 每一帧的音频大小
            intervel = 0.04  # 发送音频间隔(单位:s)
            status = STATUS_FIRST_FRAME  # 音频的状态信息,标识音频是第一帧,还是中间帧、最后一帧
            with open(self.AudioFile, "rb") as fp:
                while True:
                    buf = fp.read(frameSize)
                    # 文件结束
                    if not buf:
                        status = STATUS_LAST_FRAME
                    # 第一帧处理
                    # 发送第一帧音频,带business 参数
                    # appid 必须带上,只需第一帧发送
                    if status == STATUS_FIRST_FRAME:
                        # print(wsParam.create_params(ws.appid, status, buf))
                        ws.send(json.dumps(self.create_params(ws.appid, status, buf)))
                        print('第一帧已发送...')
                        status = STATUS_CONTINUE_FRAME
                    # 中间帧处理
                    elif status == STATUS_CONTINUE_FRAME:
                        ws.send(json.dumps(self.create_params(ws.appid, status, buf)))
                        # print('中间帧已发送...')
                    # 最后一帧处理
                    elif status == STATUS_LAST_FRAME:
                        print('最后一帧已发送...')
                        ws.send(json.dumps(self.create_params(ws.appid, status, buf)))
                        break

                    # 模拟音频采样间隔
                    time.sleep(intervel)
            # ws.close()

        thread.start_new_thread(run, ())

    def get_audio_text(self):
        websocket.enableTrace(False)
        wsUrl = self.create_url()
        # 创建转写、转译的text文本和传译的音频
        open('output\\text\\asr.txt', 'w')
        open('output\\text\\trans.txt', 'w')
        open('output\\audio\\trans.pcm', 'w')
        ws = websocket.WebSocketApp(wsUrl, on_message=self.on_message, on_error=self.on_error, on_close=self.on_close)
        ws.on_open = self.on_open
        ws.appid = self.APPID
        ws.run_forever(sslopt={"cert_reqs": ssl.CERT_NONE})
        # 输出wav格式音频
        pcm2wav.pcm_2_wav("output\\audio\\trans.pcm", "output\\audio\\trans.wav")

if __name__ == "__main__":
    # 在控制台获取appid等信息
    APPID = ''
    APISecret = ''
    APIKey = ''
    # 音频路径 
    audio_path = 'input/audio/original.pcm'

    demo = Ws_Param(audio_path)
    demo.get_audio_text()

# 常见问题

# 同声传译的主要功能是什么?

答:可将不限时长的音频流实时识别并转译成指定的语种和发音。

# 同声传译支持什么语言?

答:目前支持中文-英文的互译,其他语种后续会开放,敬请关注平台动态。

# 同声传译支持什么应用平台?

答:目前支持WebAPI应用平台。

# 同声传译对音频有什么要求吗?

答:采样率为16k、采样深度为16bit、单声道的pcm格式的音频。

# 是否支持源语种的自动识别?

答:目前暂不支持。

在线咨询
体验中心