logo
电话400 898 2016
预约试用
开放平台
En
尊敬的开发者,您好! 标贝科技的开发文档已更换新的地址,旧版文档地址将会在2023年12月31日下线, 给您带来不便,敬请谅解! 立即查看
产品简介 产品简介
基本概念 基本概念
平台新手指引 平台新手指引
计价模式 计价模式
开发者文档下拉
开放平台计价
定制服务计价
获取访问令牌 获取访问令牌
语音合成 语音合成
开发者文档下拉
接口说明
发音人列表
短文本合成 开发者文档下拉
Android SDK
iOS SDK
C++(Linux) SDK
RESTful API
Websocket API
长文本合成 开发者文档下拉
长文本合成RESTful API
长文本合成Websocket API
离线合成 开发者文档下拉
模型文件说明
离线合成(普通版) Android SDK
离线合成(普通版) iOS SDK
离线合成(精品版) Android SDK
离线合成(精品版) iOS SDK
XML标签
SSML标签
语音合成时间戳功能
语音识别 语音识别
开发者文档下拉
音频格式说明
一句话识别 开发者文档下拉
RESTful API
Websocket API
Android SDK
iOS SDK
实时长语音识别 开发者文档下拉
Websocket API
Android SDK
iOS SDK
录音文件识别 开发者文档下拉
RESTful API
自学习工具 开发者文档下拉
热词
个性化模型
声音复刻 声音复刻
开发者文档下拉
定制模型 开发者文档下拉
RESTful API
Android SDK
iOS SDK
定制声音合成 开发者文档下拉
短文本合成RESTful API
长文本合成Websocket API
声音转换 声音转换
开发者文档下拉
发音人列表
Websocket API
Android SDK
iOS SDK
离线声音转换 离线声音转换
开发者文档下拉
发音人列表
Android SDK
iOS SDK
声纹识别 声纹识别
开发者文档下拉
RESTful API
声音理解 声音理解
开发者文档下拉
RESTful API
协议规则 协议规则
开发者文档下拉
平台服务协议
平台通用规则
法律声明及隐私政策
服务等级协议SLA
常见问题 常见问题
开发者文档下拉
语音合成
语音识别

语音合成时间戳功能介绍

功能介绍

标贝语音合成服务提供时间戳功能,在输出音频流的同时,可输出每个汉字或英文单词在音频中的时间位置,即时间戳功能。针对使用场景不同,时间戳功能分为两种:
1. 音子级别时间戳
音子级别时间戳主要用于驱动虚拟人口型。
2. 字级别时间戳
字级别时间戳主要用于视频配音字幕,有声书朗读等。
注意:
  • ● 只有支持音子级别时间戳的音色可以使用该功能,详见发音人列表
  • ● 在长文本任务中,使用多个发音人时,所选音色需要都支持音子/字级别时间戳,音色是否支持音子/字级别时间戳可查询语音合成-发音人列表。
  • ● 开启时间戳功能后,响应结果数据格式发生变化,具体参考下文

短文本合成RESTful API

RESTful参数设置

参数名称 是否必填 名称 说明
interval 音子边界时间戳 音子级别时间戳功能:
'0':关闭音子级别时间戳功能
'1':开启音子级别时间戳功能
enable_subtitles 字级别时间戳 字级别时间戳功能,同interval=1一起使用:
'0':关闭字级别时间戳功能
'1':开启字级别时间戳功能

RESTful示例代码

#!/usr/bin/env python
#coding: utf-8
import requests
import json
import argparse
import base64


# 获取access_token用于鉴权
def get_access_token(client_secret, client_id):
    grant_type = "client_credentials"
    url = "https://openapi.data-baker.com/oauth/2.0/token?grant_type={}&client_secret={}&client_id={}".format(grant_type, client_secret, client_id)
    response = requests.post(url)
    access_token = json.loads(response.text).get('access_token')

    return access_token


# 获取转换后音频
def get_audio(data):
    url = "https://openapi.data-baker.com/tts?access_token={}&domain={}&language={}&voice_name={}&text={}&audiotype={}" \
          "&enable_subtitles={}&interval={}".format(data['access_domain'], data['domain'], data['language'],
           data['voice_name'], data['text'], data['audiotype'], data['enable_subtitles'], data['interval'])
    response = requests.post(url)
    content_type = response.headers['Content-Type']
    if 'audio' not in content_type:
        raise Exception(response.text)

    length = int.from_bytes(response.content[:4], byteorder='big', signed=False)
    json_data = json.loads((response.content[4: length + 4]).decode(encoding='gbk'))
    subtitles_list = json.loads((base64.b64decode(json_data.get('data')['interval-subtitles'])).decode()).get('subtitles_list')
    for subtitles in subtitles_list:
        print(subtitles)
    return response.content[length + 4:]

# 获取命令行输入参数
def get_args():
    text = '标贝科技'
    parser = argparse.ArgumentParser(description='ASR')
    parser.add_argument('-client_secret', type=str, required=True)
    parser.add_argument('-client_id', type=str, required=True)
    parser.add_argument('-file_save_path', type=str, required=True)
    parser.add_argument('--text', type=str, default=text)
    parser.add_argument('--audiotype', type=str, default='6')
    parser.add_argument('--domain', type=str, default='1')
    parser.add_argument('--language', type=str, default='zh')
    parser.add_argument('--voice_name', type=str, default='Jiaojiao')
    args = parser.parse_args()

    return args


if __name__ == '__main__':
    try:
        args = get_args()

        # 获取access_token
        client_secret = args.client_secret
        client_id = args.client_id
        access_token = get_access_token(client_secret, client_id)

        # 读取参数
        audiotype = args.audiotype
        domain = args.domain
        language = args.language
        voice_name = args.voice_name
        text = args.text
        data = {'access_domain': access_token, 'audiotype': audiotype, 'domain': domain, 'language': language,
                'voice_name': voice_name, 'text': text, 'enable_subtitles': "1", 'interval': '1'}
        content = get_audio(data)

        #保存音频文件
        with open('test.wav', 'wb') as audio:
            audio.write(content)
        print("task finished successfully")
    except Exception as e:
        print(e)

RESTful响应结果

检查http 响应头Content-Type判断合成请求是否成功,如返回Content-Type以audio开头说明合成成功,响应结构如下。

如返回Content-Type:application/json说明合成出现错误,响应体为json格式

RESTful响应参数

字段名 类型 描述
data object 时间戳信息
err_msg string 错误信息
err_no int 错误码
log_id int 日志id
sn string 系统内部使用信息

data内部结构:

字段名 类型 描述
interval-info string 音子级别时间戳
interval-info-x string interval-info-x: L=1&T=1,L=1&T=2,L=1&T=1,L=1&T=2,L=1&T=5
L:表示语言种类,目前支持1:纯中文,5:中英混
T:表示interval类型,0:默认值,1:声母,2:韵母,3:儿化韵母,4:英文,5:#3静音
interval-subtitles string 字级别时间戳,返回结果是base64编码,需解码后使用

interval-subtitles内部结构:

字段名 类型 描述
subtitles_list list 时间戳信息list

subtitles_list内部结构:

字段名 类型 描述
subtitles list 音子级别时间戳
text string 句子级别文本

subtitles内部结构:

字段名 类型 描述
begin_index int 文本对应当前句中起始索引
begin_time int 文本对应音频中起始时间戳,单位毫秒
end_index int 文本对应当前句中结束索引
end_time int 文本对应音频中结束时间戳,单位毫秒
prosody string 停顿类型:
SP0
SP1
SP2
SP3
text string 文本信息

RESTful响应示例

{
  'data': {
'interval-info': 'b=0.072346&iao=0.238304&b=0.295952&ei=0.409770&k=0.519053&e=0.630216&j=0.775269&i=0.936000&SIL=1.236000',
'interval-info-x': 'L=1&T=1,L=1&T=2,L=1&T=1,L=1&T=2,L=1&T=1,L=1&T=2,L=1&T=1,L=1&T=2,L=1&T=5',
'interval-subtitles': 'eyJzdWJ0aXRsZXNfbGlzdCI6W3sic3VidGl0bGVzIjpbeyJiZWdpbl9pbmRleCI6MCwiYmVnaW5fdGltZSI6MCwiZW5kX2luZGV4IjoyLCJlbmRfdGltZSI6MjM4LCJwcm9zb2R5IjoiU1AwIiwidGV4dCI6IuaghyJ9LHsiYmVnaW5faW5kZXgiOjMsImJlZ2luX3RpbWUiOjIzOCwiZW5kX2luZGV4Ijo1LCJlbmRfdGltZSI6NDA5LCJwcm9zb2R5IjoiU1AwIiwidGV4dCI6Iui0nSJ9LHsiYmVnaW5faW5kZXgiOjYsImJlZ2luX3RpbWUiOjQwOSwiZW5kX2luZGV4Ijo4LCJlbmRfdGltZSI6NjMwLCJwcm9zb2R5IjoiU1AwIiwidGV4dCI6IuenkSJ9LHsiYmVnaW5faW5kZXgiOjksImJlZ2luX3RpbWUiOjYzMCwiZW5kX2luZGV4IjoxMSwiZW5kX3RpbWUiOjkzNiwicHJvc29keSI6IlNQMyIsInRleHQiOiLmioAifV0sInRleHQiOiLmoIfotJ3np5HmioAifV19Cg=='
	},
	'err_msg': 'succ',
	'err_no': 0,
	'log_id': 1233745494,
	'sn': '60bb11ab-c8ea-4305-b689-aa912248a4fc'
}

interval-subtitles解码后结构:

'interval-subtitles': {
	'subtitles_list': [{
		'subtitles': [{
			'begin_index': 0,
			'begin_time': 0,
			'end_index': 2,
			'end_time': 238,
			'prosody': 'SP0',
			'text': '标'
		}, {
			'begin_index': 3,
			'begin_time': 238,
			'end_index': 5,
			'end_time': 409,
			'prosody': 'SP0',
			'text': '贝'
		}, {
			'begin_index': 6,
			'begin_time': 409,
			'end_index': 8,
			'end_time': 630,
			'prosody': 'SP0',
			'text': '科'
		}, {
			'begin_index': 9,
			'begin_time': 630,
			'end_index': 11,
			'end_time': 936,
			'prosody': 'SP3',
			'text': '技'
		}],
		'text': '标贝科技'
	}]
}

短文本合成Websocket API

注意:短文本合成Websocket API开启时间戳功能后,时间戳信息会保存在subtitles_list参数中。由于时间戳信息会在整句音频生成完毕后给出,故并非所有数据包包含时间戳信息,需要客户端校验subtitles_list参数,存在该参数后方可获取时间戳信息。

webSocket参数设置

参数名称 是否必填 名称 说明
interval 音子边界时间戳 音子级别时间戳功能:
'0':关闭音子级别时间戳功能
'1':开启音子级别时间戳功能
enable_subtitles 字级别时间戳 字级别时间戳功能,同interval=1一起使用:
'0':关闭字级别时间戳功能
'1':开启字级别时间戳功能

webSocket示例代码

import argparse
import json
import base64
from threading import Thread
import requests
import websocket
import wave


#websocket客户端
class Client:
    def __init__(self, data, uri):
        self.data = data
        self.uri = uri
        self.audio_data = b""

    #建立连接
    def connect(self):
        ws_app = websocket.WebSocketApp(uri,
                                        on_open=self.on_open,
                                        on_message=self.on_message,
                                        on_error=self.on_error,
                                        on_close=self.on_close)
        ws_app.run_forever()

    # 建立连接后发送消息
    def on_open(self, ws):
        print("sending..")
        def run(*args):
            ws.send(self.data)

        Thread(target=run).start()

    # 接收消息
    def on_message(self, ws, message):
        message = json.loads(message)
        code = message["code"]
        if 'subtitles_list' in message["data"]:
            print(message["data"]['subtitles_list'])
        if code != 90000:
            # 打印接口错误
            print(message)
        else:
            self.audio_data += base64.b64decode(bytes(message["data"]["audio_data"], encoding='utf-8'))
            if message.get("data")["end_flag"] == 1:
                with wave.open('test.wav', 'wb') as wavfile:
                    wavfile.setparams((1, 2, 16000, 0, 'NONE', 'NONE'))
                    wavfile.writeframes(self.audio_data)
                ws.close()
                print("task finished successfully")

    # 打印错误
    def on_error(self, ws, error):
        print("error: ", str(error))

    # 关闭连接
    def on_close(ws):
        print("client closed.")


# 准备数据
def prepare_data(args, access_token):

    # 填写Header信息
    audiotype= args.audiotype
    voice_name = args.voice_name

    text = args.text
    # 单次调用最长300个汉字
    if len(text) > 300:
        raise Exception("Text is too long. The maximum length of chinese character is 300")
    text_bytes = text.encode(encoding='UTF-8')
    text = str(base64.b64encode(text_bytes), encoding='UTF-8')
    tts_params = {"language": "ZH", "voice_name": voice_name, "audiotype": audiotype, "domain": "1", "text": text, 'interval': '1', 'enable_subtitles': '1'}

    data = {"access_token": access_token, "version": "1.0", "tts_params": tts_params}
    data = json.dumps(data)

    return data


# 获取命令行输入参数
def get_args():
    text = '标贝科技'
    parser = argparse.ArgumentParser(description='tts')
    parser.add_argument('-client_secret', type=str, required=True)
    parser.add_argument('-client_id', type=str, required=True)
    parser.add_argument('-file_save_path', type=str, required=True)
    parser.add_argument('--text', type=str, default=text)
    parser.add_argument('--audiotype', type=str, default='4')
    parser.add_argument('--voice_name', type=str, default='beixi')
    args = parser.parse_args()

    return args


# 获取access_token用于鉴权
def get_access_token(client_secret, client_id):
    grant_type = "client_credentials"
    url = "https://openapi.data-baker.com/oauth/2.0/token?grant_type={}&client_secret={}&client_id={}" \
        .format(grant_type, client_secret, client_id)

    try:
        response = requests.post(url)
        response.raise_for_status()
    except Exception as e:
        print(response.text)
        raise Exception
    else:
        access_token = json.loads(response.text).get('access_token')
        return access_token


if __name__ == '__main__':
    try:
        args = get_args()

        # 获取access_token
        client_secret = args.client_secret
        client_id = args.client_id
        access_token = get_access_token(client_secret, client_id)

        # 准备数据
        data = prepare_data(args, access_token)

        uri = "wss://openapi.data-baker.com/tts/wsapi"
        # 建立Websocket连接
        client = Client(data, uri)
        client.connect()

        print('end')
    except Exception as e:
        print(e)

webSocket响应结果

参数名称 类型 描述
code int 错误码4xxxx表示客户端参数错误,5xxxx表示服务端内部错误
message string 错误描述
trace_id string 任务id
data object 合成音频数据

data内部结构:

参数名称 类型 描述
idx int 包序号
audio_data string base64编码后的音频数据,需要解码后使用
audio_type string 音频格式
interval string 音子边界信息
end_flag int 结束标志:
0:未结束
1:结束
interval_x string interval-info-x: L=1&T=1,L=1&T=2,L=1&T=1,L=1&T=2,L=1&T=5
L:表示语言种类,目前支持1:纯中文,5:中英混
T:表示interval类型,0:默认值,1:声母,2:韵母,3:儿化韵母,4:英文,5:#3静音
subtitles_list list 时间戳信息list

subtitles_list内部结构:

字段名 类型 描述
subtitles list 音子级别时间戳
text string 句子级别文本

subtitles内部结构:

字段名 类型 描述
begin_index int 文本对应当前句中起始索引
begin_time int 文本对应音频中起始时间戳,单位毫秒
end_index int 文本对应当前句中结束索引
end_time int 文本对应音频中结束时间戳,单位毫秒
prosody string 停顿类型:
SP0
SP1
SP2
SP3
text string 文本信息

webSocket响应示例

{
	'code': 90000,
	'data': {
		'audio_data': '音频数据',
		'audio_type': 'audio/pcm',
		'end_flag': 1,
		'idx': 1,
		'interval': 'b=0.072346&iao=0.238304&b=0.295952&ei=0.409770&k=0.519053&e=0.630216&j=0.775269&i=0.936000&SIL=1.236000',
		'interval_x': 'L=1&T=1,L=1&T=2,L=1&T=1,L=1&T=2,L=1&T=1,L=1&T=2,L=1&T=1,L=1&T=2,L=1&T=5',
		'subtitles_list': [{
			'subtitles': [{
				'begin_index': 0,
				'begin_time': 0,
				'end_index': 2,
				'end_time': 238,
				'prosody': 'SP0',
				'text': '标'
			}, {
				'begin_index': 3,
				'begin_time': 238,
				'end_index': 5,
				'end_time': 409,
				'prosody': 'SP0',
				'text': '贝'
			}, {
				'begin_index': 6,
				'begin_time': 409,
				'end_index': 8,
				'end_time': 630,
				'prosody': 'SP0',
				'text': '科'
			}, {
				'begin_index': 9,
				'begin_time': 630,
				'end_index': 11,
				'end_time': 936,
				'prosody': 'SP3',
				'text': '技'
			}],
			'text': '标贝科技'
		}]
	},
	'message': 'Success',
	'trace_id': '1656986976642904'
}