定制模型RESTful API
功能介绍
声音复刻API接收到用户上传的音频列表(须由同一人录制且满足总有效时长要求)后,先对音频数据进行预校验,通过后即返回与训练相关的modelId。预校验成功后自动开启与modelId关联的训练任务。需要注意的是,用户调用该接口上传音频的同时需要额外提供一个回调url,当模型训练完成后,通过事先提供的回调url通知模型训练结果(成功或失败)。 训练结束且成功后用户就可使用modelId直接调用语音合成接口合成音频。
参数设置
- 支持音频文件的编码格式及文件名的后缀: wav,pcm
- 支持音频文件的采样率: 16000Hz
- 支持音频文件的位深:16bits
- 支持的语言:中文
- 音频有效时长:不小于3分钟
- 音频的音质、音量均对模型效果有直接影响,请保证音频的录音环境相对安静、音频人声音量不能太小。
使用方法
1. 创建账号和应用,详见平台新手指引,通过标贝开放平台/应用/服务获取client_id,client_secret
2. 发送请求获取access_token,详见获取访问令牌
3. 调用定制模型接口,具体参数详见请求说明。
4. 接收模型ID,具体参数详见响应结果。
服务地址
访问类型 | 说明 | URL | Host |
---|---|---|---|
外网访问 | 提交复刻任务接口,只支持POST调用 | https://openapi.data-baker.com/gramophone/v1/submit | openapi.data-baker.com |
交互流程
请求说明
参数名称 | 参数格式 | 是否必填 | 说明 |
---|---|---|---|
access_token | String | 是 | 通过client_id,client_secret调用授权服务获得见获取访问令牌 |
originFiles | MultipartFile[] | 是 |
音频列表,需满足一定条件: 1.格式为pcm或wav 2. 采样率为16000hz,位深为16bit,单声道 3. 有效时长最好不小于3分钟(接口实际是以识别出的字数作为判定标准) |
mobile | String | 否 | 手机号,用于接收短信提醒 |
notifyUrl | String | 是 | 该地址用于接收合成结果,该地址必须为外网可访问的url,不能携带参数。 |
请求示例
curl -L -X POST 'https://openapi.data-baker.com/gramophone/v1/submit' \ -F 'access_token=64c16f14-ac37-4dd8-9d56-a82e32b2d8d1' \ -F 'notifyUrl=https://openapi.data-baker.com/gramophone/v1/api/notify' \ -F 'originFiles=@/C:/Users/xxx/Desktop/0baf0cb3fdc14a049c9f615c69c1c50b/origin/1597222394369.wav' \ -F 'originFiles=@/C:/Users/xxx/Desktop/0baf0cb3fdc14a049c9f615c69c1c50b/origin/1597222451867.wav' \ -F 'originFiles=@/C:/Users/xxx/Desktop/0baf0cb3fdc14a049c9f615c69c1c50b/origin/1597222475643.wav' \ -F 'originFiles=@/C:/Users/xxx/Desktop/0baf0cb3fdc14a049c9f615c69c1c50b/origin/1597222497181.wav' \
Python示例代码
代码地址:Github
Python3示例如下:
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 create_model(data, files): url = "https://openapi.data-baker.com/gramophone/v1/submit" response = requests.post(url, data=data, files=files) code = json.loads(response.text).get("code") if code != "20000": raise Exception(response.text) else: model = json.loads(response.text).get("data")["modelId"] print(model) # 获取命令行输入参数 def get_args(): 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_path', type=str, required=True) parser.add_argument('-notifyUrl', type=str, required=True) parser.add_argument('--mobile', type=str) 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) files = [ ('originFiles', ( '101.wav', open('101.wav', 'rb'), 'audio/wav')), ('originFiles', ( '201.wav', open('201.wav', 'rb'), 'audio/wav')), ('originFiles', ( '301.wav', open('301.wav', 'rb'), 'audio/wav')), ('originFiles', ( '401.wav', open('401.wav', 'rb'), 'audio/wav')) ] # 读取参数 notifyUrl = args.notifyUrl mobile = args.mobile data = {'access_token': access_token, 'notifyUrl': notifyUrl, 'mobile': mobile} create_model(data, files) print("task finished successfully") except Exception as e: print(e)
命令行执行
python audio_file_recognition.py -client_secret=您的client_secret -client_id=您的client_id -notifyUrl=您的回调地址
JAVA示例代码
package com.databaker.web.tts; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import okhttp3.*; import org.apache.commons.lang3.StringUtils; import java.io.File; import java.util.ArrayList; import java.util.List; /** * 声音复刻RESTFUL API接口调用示例 * 附:声音复刻RESTFUL API文档 【https://www.data-baker.com/specs/file/reprint_api_restful】 * * 注意:仅作为demo示例,失败重试、token过期重新获取、日志打印等优化工作需要开发者自行完成 * * @author data-baker */ public class TtsSoundReproductionApiDemo { /** * 授权:需要在开放平台获取【https://ai.data-baker.com/】 */ private static final String clientId = "YOUR_CLIENT_ID"; private static final String clientSecret = "YOUR_CLIENT_SECRET"; /** * 获取token的地址信息 */ public static String tokenUrl = "https://openapi.data-baker.com/oauth/2.0/token?grant_type=client_credentials&client_secret=%s&client_id=%s"; /** * 声音复刻API地址 */ public static String soundReproductionUrl = "https://openapi.data-baker.com/gramophone/v1/submit"; /** * 音频列表,需满足一定条件: * 1.格式为pcm或wav * 2.采样率为16000hz,位深为16bit,单声道 * 3.有效时长最好不小于3分钟(接口实际是以识别出的字数作为判定标准) */ public static List<File> originFiles = new ArrayList<>(); public static void main(String[] args) { String accessToken = getAccessToken(); if (StringUtils.isNotEmpty(accessToken)) { doSoundReproduction(accessToken, originFiles, "mobile", "https://openapi.data-baker.com/gramophone/v1/api/notify"); } } /** * 提交复刻任务 * * 开发者需开发一个回调接口,接口地址作为参数notifyUrl,用来接收模型训练的结果,具体写法可参考接口文档【https://www.data-baker.com/specs/file/reprint_api_restful】中的回调部分 */ private static void doSoundReproduction(String accessToken, List<File> originFiles, String mobile, String notifyUrl) { //创建连接 OkHttpClient client = new OkHttpClient(); //构建requestBody,传入参数 MultipartBody.Builder requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM); for (File file : originFiles) { RequestBody body = RequestBody.create(file, MediaType.parse("multipart/form-data")); String filename = file.getName(); requestBody.addFormDataPart("originFiles", filename, body); } requestBody.addFormDataPart("access_token", accessToken); requestBody.addFormDataPart("mobile", mobile); requestBody.addFormDataPart("notifyUrl", notifyUrl); //构造request Request request = new Request.Builder() .url(soundReproductionUrl) .method("POST", requestBody.build()) .build(); try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { System.out.println("调用成功,返回结果:" + response.body().string()); } else { System.out.println("调用失败,返回结果:" + response.body().string()); } } 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; } }
响应结果
正确结果示例:modelId可以唯一标志一个模型,通过notifyUrl回调推送合成结果时,会同时返回这个modelId和训练的结果及原因。如果训练成功,可以使用该modelId作为voice_name进一步进行tts功能的调用(参考: https://www.data-baker.com/specs/file/tts_api_restful)。
{ "code": 20000, "message": "成功", "data": { "modelId": "714bef4b24****************8e68ballpx3" } }
错误结果示例
{ "code": 30000, "message": "鉴权错误(access_token值不正确或已经失效)", "data": null }
回调说明
- 声音复刻接口收到请求后,首先对请求参数进行初步校验,并马上返回。如果成功,返回的结果“code”是0,会同时返回此次模型对应的modelId,然后进入异步训练和回调逻辑;如果校验失败,返回的错误码提示错误,丢弃当前音频数据放弃训练。
请求的body中会包含modelId、modelStatus和reason字段。modelStatus = “success”表示成功,modelStatus = “fail”表示失败,reason说明具体原因。回调的请求方式示例如下
curl -X POST --header 'Content-Type: application/json' --header 'Accept: application/json' -d '{ "modelStatus": "fail", "modelId": "714bef4b24****************8e68ballpx3", "reason":"音频有效时长不足(识别出的字数不足500字)" }' 'https://openapi.data-baker.com/gramophone/v1/api/notify'
建议处理方式:收到回调请求后,提取需要的信息,并在body中返回固定字符串
<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>
- 回调如果不成功,目前每隔5秒重试一次,重试4次(该策略可能会有变动)
Python回调示例代码
from flask import Flask, request import json app = Flask(__name__) @app.route("/", methods=['GET', 'POST']) def get_audio(): data = str(json.loads(request.get_data())) with open('clong.log', 'w') as f: f.write(data) return "finished"
JAVA回调示例代码
@ApiOperation(value = "测试用回调接口", notes = "该链接由参数notifyUrl设置,如果链接无法访问,将无法接收到回调的push信息。") @PostMapping("/notify") public void synthesisNotify(HttpServletRequest request, HttpServletResponse response) { String resXml = ""; InputStream inStream; try { inStream = request.getInputStream(); ByteArrayOutputStream outSteam = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = inStream.read(buffer)) != -1) { outSteam.write(buffer, 0, len); } // 获取声音复刻api调用notifyUrl的返回信息 String result = new String(outSteam.toByteArray(), "utf-8"); log.info("dataBaker:声音复刻api返回结果 ----result---- =" + result); // 关闭流 outSteam.close(); inStream.close(); // String转换为json对象,然后业务处理 JSONObject jsonObject = JSON.parseObject(result); //该参数表示模型id String modelId = jsonObject == null ? "" : jsonObject.getString("modelId"); //该参数表示模型当前状态:可能的值为success/fail, 其中,success=训练成功 fail=训练失败 String modelStatus = jsonObject == null ? "" : jsonObject.getString("modelStatus"); //该参数表示原因(文案可能会有变动),成功时为空字符串,失败时为失败原因说明 String reason = jsonObject == null ? "" : jsonObject.getString("reason"); //具体业务处理,例如尝试tts调用等(可先返回结果,然后异步去完成业务逻辑) //todo //根据情况,向结果中赋值:正常情况下,选择下面的resSuccess;如果选择resFail,将会视为推送失败,标贝服务端将重新推送一次相同的内容 // * 返回成功xml // */ // String resSuccess = "<xml><return_code>SUCCESS</return_code><return_msg>OK</return_msg></xml>"; // /** // * 返回失败xml // */ // String resFail = "<xml><return_code>FAIL</return_code><return_msg>报文为空</return_msg></xml>";resXml = Constant.resSuccess; //记录日志 log.info("dataBaker:声音复刻api回调返回模型 {} 的状态为:--->{}", modelId , modelStatus); } catch (Exception e) { log.error("dataBaker:声音复刻api回调异常:", e); } finally { try { // 处理业务完毕 BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); out.write(resXml.getBytes()); out.flush(); out.close(); } catch (IOException e) { log.error("dataBaker:声音复刻api回调异常:out:", e); } } }
错误码
错误码 | 含义 | |
---|---|---|
20000 | 请求成功 | |
30000 | 鉴权错误(access_token值不正确或已经失效) | 授权相关错误(鉴权未通过、qps超限、授权到期等) |
30008 | 授权禁用 | |
30009 | 授权到期 | |
30010 | 调用次数超过限制 | |
30013 | 授权不支持声音复刻 | |
40000 | 参数获取失败或未传输 | 客户端参数错误 |
40002 | 提交次数已达到最大限制 | |
40003 | 接口请在有效期内使用 | |
40006 | 文件大小或音频时长不符合要求 | |
50000 | 系统内部错误 | 服务端错误信息 |
50001 | 操作redis错误 | |
50002 | 引擎下载失败 |