短文本合成RESTful API
功能介绍
语音合成RESTful API支持HTTPS GET和POST两种方法的请求,将待合成的文本上传到服务端,服务端返回语音合成结果。随着TTS合成效果的不断提升,算法的复杂度也越来越高,对用户而言,可能会遇到合成耗时变长的可能。因此我们建议您使用流式合成机制。
参数设置
- 支持设置合成音频的格式:pcm,wav,mp3,alaw,ulaw
- 支持设置合成音频的采样率: 8000Hz,16000Hz,24000Hz
- 支持设置多种发音人
- 支持设置音量、语速、语调
- 支持返回时间戳信息
- 支持设置多种语言:中文(zh),英文(eng),粤语(cat),四川话(sch),天津话(tjh),台湾话(tai),韩语(kr),巴葡语(bra),日语(jp),西班牙西语(ESP),墨西哥西语(MEX),维吾尔语(UYG)
使用方法
1. 创建账号和应用,详见平台新手指引,通过标贝开放平台/应用/服务获取client_id,client_secret
2. 发送请求获取access_token,详见获取访问令牌
3. 发送请求:发送语音合成请求,具体参数详见 请求参数。
4. 接收返回结果:检查http 响应头Content-Type判断合成请求是否成功,如返回Content-Type以audio开头说明合成成功,响应体为音频数据。如返回Content-Type: application/json说明合成出现错误,响应体为json,具体参数定义详见 响应结果说明。
服务地址
访问类型 | 说明 | URL | Host |
---|---|---|---|
外网访问 | 所有音色 | https://openapi.data-baker.com/tts | openapi.data-baker.com |
交互流程
请求参数
参数名称 | 是否必填 | 名称 | 说明 |
---|---|---|---|
text | 是 | 合成文本 |
合成的文本,使用UTF-8编码,如果用户在非浏览器上操作,则需要把合成文本转化为utf-8格式。
GET 方法一次请求最多不超过300个汉字 POST 方法一次请求最多不超过300个汉字 |
access_token | 是 | 用户访问token | 通过client_id,client_secret调用授权服务获得见获取访问令牌 |
domain | 是 | 领域 | 应用所属领域如导航、客服等,以数字进行编码,目前值固定为1,必填 |
language | 是 | 文本语言 |
合成请求文本的语言: ZH(中文和中英混) ENG(纯英文,中文部分不会合成) CAT(粤语) SCH(四川话) TJH(天津话) TAI(台湾话) KR(韩语) BRA(巴葡语) JP(日语) ESP(西班牙西语) MEX(墨西哥西语) UYG(维吾尔语) |
speed | 否 | 语速 | 设置播放的语速,在0~9之间(支持浮点值),不传时默认为5 |
volume | 否 | 音量 | 设置语音的音量,在0~9之间(只支持整型值),不传时默认为5 |
pitch | 否 | 音调 | 设置语音的音调,取值0-9,不传时默认为5中语调 |
audiotype | 否 | 音频种类 |
可不填,不填时默认为3,表示mp3格式
audiotype=3 :返回16K采样率的mp3格式 audiotype=4 :返回16K采样率的pcm格式 audiotype=5 :返回8K采样率的pcm格式 audiotype=6 :返回16K采样率的wav格式 audiotype=6&rate=1 :返回8K的wav格式 audiotype=7 :返回8k8bit的alaw格式文件 audiotype=8 :返回8k8bit的ulaw格式文件 audiotype=9&rate=1 返回8K采样率的mp3 audiotype=9&rate=3,返回24k采样率的mp3格式 audiotype=6&rate=3,返回24k采样率的wav格式 audiotype=5&rate=3,返回24k采样率的pcm格式 |
rate | 否 | 码率 |
可不填,不填时默认为2,取值范围1-8,2以上的值仅针对返回MP3格式,对应的码率为:
1 —— 8kbps 2 —— 16kbps 3 —— 24kbps 4 —— 32kbps 5 —— 40kbps 6 —— 48kbps 7 —— 56kbps 8 —— 64kbps |
voice_name | 是 | 发音人 | 发音人选择,直接输入英文名称 例如:Jiaojiao。详见发音人列表 |
spectrum | 否 | 频谱 |
取值范围0~20;不传时默认为不调整频谱;
值为0代表使用配置文件tts_attention.conf中spec_adjust_d的值; 1代表不调整频谱; 1以上的值代表高频能量增加幅度,值越大声音的高频部分增强越多,听起来更亮和尖细 |
spectrum_8k | 否 | 低频部分频谱 | 取值范围0~20;不传时默认为0,仅针对8K音频频谱的调整。 |
interval | 否 | 音子边界时间戳 |
音子级别时间戳功能: '0':关闭音子级别时间戳功能 '1':开启音子级别时间戳功能 |
enable_subtitles | 否 | 字级别时间戳 |
字级别时间戳功能,同interval=”1”一起使用: '0':关闭字级别时间戳功能 '1':开启字级别时间戳功能 详细使用方法参考语音合成时间戳 |
silence | 否 | 标点符号静音时长 |
设置标点符号静音时长: '0':默认值 '1':句中标点停顿较短,适合直播、配音解说等场景 '2':句中标点停顿较长,适合朗诵、教学等场景 |
emo_type | string | 是(请求情感音色时) | 在请求情感音色时,该参数是必填项。请求情感音色不存在的情感会报错。可选值参考 发音人列表-多情感/风格音色,如:sad |
emo_intensity | string | 否 | 设置情感强度,在1~5之间(只支持整型值),不传时默认为3。 |
请求示例
curl –get --data-urlencode "text=标贝科技" "https://openapi.data-baker.com/tts?access_token=your_access_token&domain=1&language=zh&voice_name=Jiaojiao"2. POST方法
curl -X POST https://openapi.data-baker.com/tts -d "access_token=default&domain=1&language=zh&voice_name=Jiaojiao&text=标贝科技"
Python示例代码
代码地址:Github
Python3示例:
#!/usr/bin/env python # coding: utf-8 import requests import json import argparse # 获取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={}".format(data['access_domain'], data['domain'], data['language'], data['voice_name'], data['text'], data['audiotype']) response = requests.post(url) content_type = response.headers['Content-Type'] if 'audio' not in content_type: raise Exception(response.text) return response.content # 获取命令行输入参数 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} content = get_audio(data) #保存音频文件 with open('test.wav', 'wb') as audio: audio.write(content) print("task finished successfully") except Exception as e: print(e)
命令行执行
python online_tts.py -client_secret=你的client_secret -client_id=你的client_secret -file_save_path=test.wav --text=今天天气不错哦
如有需要可自行修改参数
python online_tts.py -client_secret=你的client_secret -client_id=你的client_secret -file_save_path=test.wav --text=今天天气不错哦 --audiotype=6
JAVA示例代码
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import org.apache.commons.lang3.StringUtils; import java.io.File; import java.io.IOException; import java.net.URLEncoder; import java.nio.file.Files; public class JavaTtsRestDemo { /** * 合成使用的地址信息,rate、language等参数在本demo固定,开发者如需调整,参考https://www.data-baker.com/specs/file/tts_api_restful */ public static String ttsUrl = "https://openapi.data-baker.com/tts?access_token=%s&domain=1&rate=2&audiotype=%s&language=zh&voice_name=%s&speed=%s&volume=%s&text=%s"; /** * 获取token的地址信息 */ public static String tokenUrl = "https://openapi.data-baker.com/oauth/2.0/token?grant_type=client_credentials&client_secret=%s&client_id=%s"; public static String clientId = "YOUR_CLIENT_ID"; public static String clientSecret = "YOUR_CLIENT_SECRET"; /** * 仅作为demo示例 * 失败重试、token过期重新获取、日志打印等优化工作需要开发者自行完成 **/ public static void main(String[] args) { String accessToken = getAccessToken(); if (StringUtils.isNotEmpty(accessToken)) { doSynthesis(accessToken, "Nannan", "测试文本", 6, 5.0, 5.0, "/home/tts/test.wav"); } } public static void doSynthesis(String accessToken, String voiceName, String originText, Integer audioType, Double speed, Double volume, String filePath) { //在非浏览器上操作,需要把合成文本转化为utf-8格式 try { originText = URLEncoder.encode(originText, "utf-8"); String synthesisUrl = String.format(ttsUrl, accessToken, audioType, voiceName, speed, volume, originText); fetchTtsResponse(synthesisUrl, filePath); } catch (Exception e) { e.printStackTrace(); } } /** * 请求并获取音频流保存至本地文件:这里filePath为全路径 * * @param url * @param filePath * @throws IOException */ public static void fetchTtsResponse(String url, String filePath) throws IOException { OkHttpClient client = new OkHttpClient(); //request 默认是get请求 Request request = new Request.Builder().url(url).build(); try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { if (response.body() != null && response.body().contentType().toString().startsWith("audio")) { //写入文件 File targetFile = new File(filePath); Files.write(targetFile.toPath(), response.body().bytes()); } } } catch (Exception e) { e.printStackTrace(); } } public static String getAccessToken() { String accessToken = ""; OkHttpClient client = new OkHttpClient(); //request 默认是get请求 String url = String.format(tokenUrl, clientSecret, clientId); Request request = new Request.Builder().url(url).build(); JSONObject jsonObject; try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { //解析 String resultJson = response.body().string(); jsonObject = JSON.parseObject(resultJson); accessToken = jsonObject.getString("access_token"); } } catch (Exception e) { e.printStackTrace(); } return accessToken; } }
PHP示例代码
<?php //获取访问令牌 $client_secret = '***'; //应用secret $client_id = '***'; //应用id $grant_type = 'client_credentials'; //固定格式 //1.获取token $url = 'https://openapi.data-baker.com/oauth/2.0/token?grant_type='.$grant_type.'&client_id='.$client_id.'&client_secret='.$client_secret; //curl get请求 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); //信任任何证书 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0); // 检查证书中是否设置域名,0不验证 $token_res = curl_exec($ch); //如果错误 查看错误信息 if(curl_errno($ch)) { print curl_error($ch); } curl_close($ch); //进行token信息解析 $token_info = json_decode($token_res,1); $access_token = $token_info['access_token']; //获取到的token信息 //var_dump($access_token);die; //$access_token = '***'; //2.短文本合成 说明(支持get post方式,本demo使用get方式) $url = 'https://openapi.data-baker.com/tts?access_token='.$access_token.'&text="PHP是世界上最好的语言,不接受任何反驳"&domain=1&language=zh&voice_name=Jiaojiao'; //curl get请求 $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $audio_info = curl_exec($ch); //获取请求链接响应头信息 $response_header = curl_getinfo($ch); if(stripos($response_header['content_type'],'audio') ===false){ var_dump('合成内容失败');die; } //进行合成内容保存 $file_name = './test.mp3'; $res = file_put_contents($file_name,$audio_info); if($res){ var_dump('文件保存成功');die; }
GO示例代码
package main import ( "bytes" "encoding/json" "errors" "flag" "fmt" "io/ioutil" "net/http" "net/url" "os" "strconv" "strings" "time" ) type AuthInfo struct { AccessToken string `json:"access_token"` ExpiresIn int `json:"expires_in"` Scope string `json:"scope"` } type ReqTtsParams struct { Domain string `json:"domain"` Interval string `json:"interval,omitempty"` Language string `json:"language"` VoiceName string `json:"voice_name"` Text string `json:"text"` Audiotype string `json:"audiotype"` Version string `json:"version"` Rate string `json:"rate,omitempty"` AccessToken string `json:"access_token"` } const grantType string = "client_credentials" func GetToken(reqUrl, clientId, clientSecret string) (string, error) { // 超时时间:60秒 client := &http.Client{Timeout: 60 * time.Second} urlParams := url.Values{} urlParams.Add("grant_type", grantType) urlParams.Add("client_id", clientId) urlParams.Add("client_secret", clientSecret) httpUrl := reqUrl httpUrl += "?" httpUrl += urlParams.Encode() fmt.Printf("httpUrl: %s\n\n", httpUrl) resp, err := client.Get(httpUrl) if err != nil { fmt.Printf("send http get token request failed, err: %s\n", err) return "", err } defer resp.Body.Close() result, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("http get token request, readall body failed, err: %s\n", err) return "", err } authInfo := AuthInfo{} err = json.Unmarshal(result, &authInfo) if err != nil { fmt.Printf("auth info json.Unmarshal err: %s \n", err) return "", err } if len(authInfo.AccessToken) <= 0 { return "", errors.New("access token is null") } fmt.Printf("get token success. info: %#v \n", authInfo) return authInfo.AccessToken, nil } func SendTts(URL string, reqParam ReqTtsParams) (string, error) { // 超时时间:60秒 client := &http.Client{Timeout: 60 * time.Second} reqUrl := URL fmt.Printf("reqUrl: %s\n\n", reqUrl) var resp *http.Response var err error if 0 == strings.Compare("2", reqParam.Version) { jsonData, _ := json.Marshal(&reqParam) resp, err = client.Post(reqUrl, "application/json", bytes.NewReader(jsonData)) } else { urls := url.Values{} urls.Add("text", reqParam.Text) urls.Add("voice_name", reqParam.VoiceName) urls.Add("access_token", reqParam.AccessToken) urls.Add("domain", "1") urls.Add("language", "zh") urls.Add("interval", reqParam.Interval) if strings.Contains(reqParam.VoiceName, "cc") { urls.Add("audiotype", "6") urls.Add("rate", "1") } else { if len(reqParam.Audiotype) > 0 { urls.Add("audiotype", reqParam.Audiotype) } if len(reqParam.Rate) > 0 { urls.Add("rate", reqParam.Rate) } } reqUrl += "?" reqUrl += urls.Encode() resp, err = client.Get(reqUrl) } if err != nil { fmt.Printf("send http tts request failed, err: %s \n", err) return "", err } defer resp.Body.Close() if resp.StatusCode != 200 { fmt.Printf("send http tts request return status code != 200\n") return "", errors.New("status code is not 200") } result, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Printf("readall http body failed, err: %s \n", err) return "", err } contentType := resp.Header.Get("Content-Type") if strings.Contains(contentType, "audio/") { logid := resp.Header.Get("logid") intervalInfo := resp.Header.Get("interval-info") intervalInfoX := resp.Header.Get("interval-info-x") fmt.Printf("interval:\t%s\ninterval_x:\t%s\n", intervalInfo, intervalInfoX) filename := reqParam.VoiceName + "_" filename += logid filename += "." filename += strings.TrimLeft(contentType, "audio/") fmt.Printf("output:\t\t%s\n", filename) _ = ioutil.WriteFile(filename, result, 0755) return "", nil } return string(result), fmt.Errorf("tts work failed") } func main() { var ( clientId, clientSecret, text, voiceName string audioType, rate int interval bool ) flag.StringVar(&clientId, "cid", "", "client id") flag.StringVar(&clientSecret, "cs", "", "client secret") flag.StringVar(&text, "t", "标贝科技", "合成文本") flag.StringVar(&voiceName, "v", "Jingjing", "发音人") flag.IntVar(&audioType, "audiotype", 6, "audiotype") flag.IntVar(&rate, "rate", 0, "rate") flag.BoolVar(&interval, "interval", false, "interval") flag.Parse() if len(os.Args) < 2 { flag.Usage() return } if len(clientId) <= 0 || len(clientSecret) <= 0 || len(text) <= 0 || len(voiceName) <= 0 { fmt.Println("parameter error!!!") return } accessToken, err := GetToken("https://openapi.data-baker.com/oauth/2.0/token", clientId, clientSecret, ) if err != nil { fmt.Println("get access token failed!!!! please check your client_id and client_secret.") return } param := ReqTtsParams{ AccessToken: accessToken, Text: text, Language: "ZH", Domain: "1", VoiceName: voiceName, Audiotype: strconv.Itoa(audioType), Rate: strconv.Itoa(rate), } if interval { param.Interval = "1" } body, err := SendTts("https://openapi.data-baker.com/tts", param) if err != nil { fmt.Printf("send tts http request failed. result: %s \n", body) return } fmt.Println("send http request success.") }
C示例代码
代码地址:Github
响应结果
检查http 响应头Content-Type判断合成请求是否成功,如返回Content-Type以audio开头说明合成成功,响应体为音频数据。如返回Content-Type:application/json说明合成出现错误,响应体为json。
1. 成功请求Content-Type示例a) audiotype=3,返回16K采样率的mp3格式的音频字符串,可根据参数表中的rate参数设置返回的码率,具体header信息 Content-Type: audio/mp3;
b) audiotype=4,返回16K采样率的PCM格式音频,Content-Type: audio/pcm;
c) audiotype=5,返回8K采样率的PCM格式音频,Content-Type: audio/pcm;
d) audiotype=6,返回16K采样率的wav格式音频,Content-Type: audio/wav;
e) audiotype=6&rate=1,返回为8K采样率的wav格式的音频,Content-Type: audio/wav;
f) audiotype=7,返回8K采样率的alaw格式音频,Content-Type: audio/alaw;
g) audiotype=8,返回8K采样率的ulaw格式音频, Content-Type: audio/ulaw;
h) audiotype=9&rate=1,返回8K采样率的mp3格式音频, Content-Type: audio/mp3;
2. interval=1时返回响应头增加interval-info 返回边界音子时间戳音素类型:
Content-Type: application/json { "err_msg": "parameter error", "err_no": -6101, "log_id": 955891560, "sn": "914313d3-c9cf-493d-820d-b0e81fdb8bd4" }
错误码
错误码 | 含义 |
---|---|
10001 | access_token参数获取失败或未传输 |
10002 | domain参数值错误 |
10003 | language参数错误 |
10004 | voice_name参数错误 |
10005 | audiotype参数错误 |
10006 | rate参数错误 |
10007 | idx错误 |
10008 | single错误 |
10009 | text参数错误 |
10010 | 文本太长 |
20000 | 获取资源错误 |
20001 | 断句失败 |
20002 | 分段数错误 |
20003 | 分段后的文本长度错误 |
20004 | 获取引擎链接错误 |
20005 | RPC链接失败错误 |
20006 | 引擎内部错误 |
20007 | 操作redis错误 |
20008 | 音频编码错误 |
30000 | 鉴权错误(access_token值不正确或已经失效) |
30001 | 并发错误 |
30002 | 内部配置错误 |
30003 | json串解析错误 |
30004 | 获取url失败 |
30005 | 获取客户IP地址失败 |
30006 | 任务队列错误 |