CosyVoice声音复刻服务,仅需提供较短的样本音频,即可迅速生成高度相似且听感自然的定制声音。通过OpenAPI进行CosyVoice声音复刻,要求开发者自行编写代码完成身份验证和请求逻辑,如果缺少对应编程语言的SDK,或者需要控制依赖组件,可以通过OpenAPI进行音色复刻和查询。本文为您介绍相关接口协议说明。
请求报文
客户端发送的请求支持使用HTTPS协议,请求方法支持POST方法。服务端提供了基于阿里云POP协议的接口,因此客户端需要实现阿里云POP的签名机制。
URL
协议
URL
方法
HTTPS
https://nls-slp.cn-shanghai.aliyuncs.com
POST
请求参数
名称
类型
是否必选
说明
AccessKeyId
String
是
阿里云账号AccessKey ID
Action
String
是
POP API名称:
音色复刻:
CosyVoiceClone
查询音色:
ListCosyVoice
Version
String
是
POP API版本:2019-08-19
Format
String
是
响应返回的类型:
JSON
RegionId
String
是
服务所在的地域ID:
cn-shanghai
Timestamp
String
是
请求的时间戳。日期格式按照ISO 8601标准表示,且使用UTC时间,时区:+0。格式:YYYY-MM-DDThh:mm:ssZ。如2019-04-03T06:15:03Z为UTC时间2019年4月3日6点15分03秒。
SignatureMethod
String
是
签名算法:
HMAC-SHA1
SignatureVersion
String
是
签名算法版本:1.0
SignatureNonce
String
是
唯一随机数uuid,用于请求的防重放攻击,每次请求唯一,不能重复使用。格式为A-B-C-D-E(A、B、C、D、E的字符位数分别为8、4、4、4、12)。例如,
8d1e6a7a-f44e-40d5-aedb-fe4a1c80f434
。Signature
String
是
由所有请求参数计算出的签名结果,生成方法参见下文签名机制。
VoicePrefix
String
是
复刻音色的前缀,仅包含小写英文字母及数字,且长度不超过10个字符。
Url
String
仅在CosyVoiceClone中使用,必选。
用于音色复刻的音频文件URL。
PageIndex
Integer
仅在ListCosyVoice中使用,非必选,默认为1。
查询音色列表页码。
PageSize
Integer
仅在ListCosyVoice中使用,非必选,默认为10。
一页音色数量。
HTTPS请求头部
HTTPS请求头部由“关键字-值”对组成,每行一对,关键字和值用英文冒号“:”分隔,内容如下:
名称
类型
是否必选
描述
Host
String
否
HTTPS请求的服务器域名:
nls-meta.cn-shanghai.aliyuncs.com
,一般根据请求链接自动解析。Accept
String
否
指定客户端能够接收的内容类型:
application/json
,不设置默认为 */* 。Content-type
String
POST方法必须设置
指定POST方法请求体数据格式:
application/x-www-form-urlencoded
。
报文示例
HTTPS POST请求报文
CosyVoiceClone
POST&%2F&AccessKeyId%3DALIYUN_AK_ID%26Action%3DCosyVoiceClone%26Format%3DJSON%26RegionId%3Dcn-shanghai%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D80bf00d8-3f4d-11ef-941b-72ec8d600bed%26SignatureVersion%3D1.0%26Timestamp%3D2024-07-11T06%253A19%253A17Z%26Url%3Dyour-audio-file-url%26Version%3D2019-08-19%26VoicePrefix%3Dyour-voice-prefix
ListCosyVoice
POST&%2F&AccessKeyId%3DALIYUN_AK_ID%26Action%3DListCosyVoice%26Format%3DJSON%26PageIndex%3D1%26PageSize%3D10%26RegionId%3Dcn-shanghai%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D823730ac-3f4d-11ef-941b-72ec8d600bed%26SignatureVersion%3D1.0%26Timestamp%3D2024-07-11T06%253A19%253A20Z%26Version%3D2019-08-19%26VoicePrefix%3Dyour-voice-prefix
响应结果
发送HTTPS请求后,会受到服务端响应,结果以JSON字符串的形式保存在响应中。
成功响应
HTTPS状态码为200,响应字段如下表所示。
参数
类型
说明
RequestId
String
requestId
Message
String
SUCCESS
VoiceName
String
复刻成功声音的voice名字
CosyVoiceClone
{ "RequestId": "0D6FF6C9-7D90-57C0-9931-xxxxxxx", "Message": "SUCCESS", "Code": 20000000, "VoiceName": "cosyvoice-xxxxx" }
ListCosyVoice
{ "TotalCount": 0, "RequestId": "C4E8622C-B365-55B3-899A-xxxxxxxxxxx", "Message": "SUCCESS", "PageSize": 10, "PageIndex": 1, "Code": 20000000, "Voices": [ ] }
失败响应
HTTPS状态码为非200,响应字段说明如下表。
参数
类型
说明
RequestId
String
请求ID
Message
String
失败响应的错误信息
Recommend
String
排查建议
HostId
String
Host ID
Code
String
失败响应的错误类型,详情请参见服务状态码。
{ "RequestId": "00CDBC76-BC8D-5734-BEA4-xxxxxxxx", "Message": "Specified access key is not found.", "Recommend": "https://api.aliyun.com/troubleshoot?q=InvalidAccessKeyId.NotFound&product=nls-slp&requestId=00CDBC76-BC8D-5734-BEA4-D4752DF81D14", "HostId": "nls-slp.cn-shanghai.aliyuncs.com", "Code": "InvalidAccessKeyId.NotFound" }
签名机制
服务端POP API对每个接口访问请求的发送者都要进行身份验证,需要在请求中包含签名信息。通过签名机制,服务端可以确认哪位用户在做API请求,并能确认请求在网络传输过程中是否被篡改。
安全验证流程
计算签名时,需要阿里云账号的AccessKey Id
和AccessKey Secret
,使用HMAC-SHA1算法进行对称加密。其工作流程如下。
请求端根据API请求内容(包括HTTPS请求参数和请求体)生成签名字符串。
请求端使用阿里云账号的
AccessKey Id
和AccessKey Secret
对第一步生成的签名字符串进行签名,获得该API请求的数字签名。请求端把API请求内容和数字签名一同发送给服务端。
服务端用期望的数字签名和请求端发送过来的数字签名做对比,若完全一致则认为该请求通过验证。否则拒绝该请求。
生成请求的签名字符串
构造规范化的请求字符串。
将HTTPS请求参数(不包括Signature)构造成规范化的请求字符串。规范化步骤:
参数排序。按参数名的字典顺序,对请求参数进行大小写敏感排序。
参数编码。对排序后的请求参数进行规范化设置。
请求参数名和值都要使用
UTF-8
字符集进行URL编码,URL编码规则如下:对于字符 A-Z、a-z、0-9以及字符
-
、_
、.
、~
不编码。对于其他字符编码成“%XY”的格式,其中XY是字符对应ASCII码的16进制表示。比如英文的双引号(”)对应的编码就是%22。
对于扩展的
UTF-8
字符,编码成“%XY%ZA…”的格式。需要说明的是英文空格要被编码为%20,而不是加号
+
。
说明一般支持URL编码的库(比如Java中的java.net.URLEncoder)都是按照“application/x-www-form-urlencoded”的MIME类型的规则进行编码的。实现时可以直接使用此类方式进行编码,然后把编码后的字符串中:加号
+
替换为%20
,星号*
替换为%2A
,%7E
替换为波浪号~
,即可得到上述规则描述的编码字符串。使用等号
=
连接URL编码后的参数名和参数值:percentEncode(参数Key) + “=” + percentEncode(参数值)
。使用与(
&
)号连接第c步URL编码后的请求参数对,如Action=CreateToken&Format=JSON
。说明字符串中第一个参数名前面不需要
&
符号。返回规范化的请求字符串。
示例如下:
String percentEncode(String value) throws UnsupportedEncodingException { return value != null ? URLEncoder.encode(value, URL_ENCODING) .replace("+", "%20") .replace("*", "%2A") .replace("%7E", "~") : null; } // 对参数Key排序 String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {}); Arrays.sort(sortedKeys); // 对排序的参数进行编码、拼接 for (String key : sortedKeys) { canonicalizedQueryString.append("&") .append(percentEncode(key)).append("=") .append(percentEncode(queryParamsMap.get(key))); } queryString = canonicalizedQueryString.toString().substring(1);
说明完整代码实现请参见完整示例的
canonicalizedQuery
函数。构造规范化的请求字符串:
AccessKeyId=LTA******3s2&Action=CosyVoiceClone&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=f20b1beb-e5dc-4245-9e96-aa582e905c1a&SignatureVersion=1.0&Timestamp=2019-04-03T03%3A40%3A13Z&Version=2019-08-19
构造待签名字符串。
将HTTPS请求的方法(GET)、URL编码的URL路径(/)、第1步获取的规范化请求字符串使用与(
&
)符号连接成待签名字符串:HTTPSMethod + "&" + percentEncode("/") + "&" + percentEncode(queryString)
。构造签名字符串代码示例:
StringBuilder strBuilderSign = new StringBuilder(); strBuilderSign.append(HTTPMethod); strBuilderSign.append("&"); strBuilderSign.append(percentEncode(urlPath)); strBuilderSign.append("&"); strBuilderSign.append(percentEncode(queryString)); stringToSign = strBuilderSign.toString();
说明完整代码实现请参见完整示例的
createStringToSign
函数。构造的签名字符串:
GET&%2F&AccessKeyId%3DLTA******F3s%26Action%3DCosyVoiceClone%26Format%3DJSON%26RegionID%3Dcn-shanghai%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3Da237e025-07ea-4d87-bb04-d9b2712d871d%26SignatureVersion%3D1.0%26Timestamp%3D2019-04-19T03%253A31%253A40Z%26Version%3D2019-08-19
计算签名。
签名采用HMAC-SHA1算法 + Base64,编码采用
UTF-8
。根据AccessKey Secret,将第2步构造的待签名字符串使用HMAC-SHA1算法计算出对应的数字签名。其中,计算签名时使用的AccessKey Secret必须在其后面增加一个与字符
&
。签名也要做URL编码。
计算签名的代码示例:
signature = Base64( HMAC-SHA1(stringToSign, accessKeySecret + "&") ); // 进行URL编码 signature = percentEncode(signature)
说明完整代码实现请阅读完整示例的
sign
函数。计算得到的签名:
# 签名串 AKIktdPUMCV12fTh667BLXeuCtg= # URL编码后的结果 AKIktdPUMCV12fTh667BLXeuCtg%3D
计算签名后,将签名的键值对用符号
=
连接,并使用符号&
添加到第1步获取的请求字符串中,作为HTTPS GET请求参数,发送到服务端,获取Token。String queryStringWithSign = "Signature=" + signature + "&" + queryString;
快速测试
您可以使用以下参数计算签名,比较计算结果是否一致。
AccessKey Id和AccessKey Secret没有提供真实数据,Timestamp是过期的时间,使用这些参数计算的签名,获取Token时会失败,仅用于计算签名测试对比。
AccessKeyId:
my_access_key_id
AccessKeySecret:
my_access_key_secret
Timestamp:
2019-04-18T08:32:31Z
SignatureNonce:
b924c8c3-6d03-4c5d-ad36-d984d3116788
请求参数如下:
AccessKeyId:my_access_key_id
Action:CosyVoiceClone
Version:2019-08-19
Timestamp:2019-04-18T08:32:31Z
Format:JSON
RegionId:cn-shanghai
SignatureMethod:HMAC-SHA1
SignatureVersion:1.0
VoicePrefix:my_voice_prefix
Url:my_url
SignatureNonce:3D472c6930-3f4f-11ef-a0b8-72ec8d600bed
规范化请求字符串。
AccessKeyId=my_access_key_id&Action=CosyVoiceClone&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=3D472c6930-3f4f-11ef-a0b8-72ec8d600bed&SignatureVersion=1.0&Timestamp=2019-04-18T08%3A32%3A31Z&Url=my_url&Version=2019-08-19&VoicePrefix=my_voice_prefix
构造待签名字符串。
POST&%2F&AccessKeyId%3Dmy_access_key_id%26Action%3DCosyVoiceClone%26Format%3DJSON%26RegionId%3Dcn-shanghai%26SignatureMethod%3DHMAC-SHA1%26SignatureNonce%3D3D472c6930-3f4f-11ef-a0b8-72ec8d600bed%26SignatureVersion%3D1.0%26Timestamp%3D2019-04-18T08%253A32%253A31Z%26Url%3Dmy_url%26Version%3D2019-08-19%26VoicePrefix%3Dmy_voice_prefix
计算得到签名。
xDyEd10/tcCLyq5mfV3QEipF9vs= # URL编码后的结果 xDyEd10%2FtcCLyq5mfV3QEipF9vs%3D
得到携带签名的请求字符串。
Signature=xDyEd10%2FtcCLyq5mfV3QEipF9vs%3D&AccessKeyId=my_access_key_id&Action=CosyVoiceClone&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=3D472c6930-3f4f-11ef-a0b8-72ec8d600bed&SignatureVersion=1.0&Timestamp=2019-04-18T08%3A32%3A31Z&Url=my_url&Version=2019-08-19&VoicePrefix=my_voice_prefix
组合成HTTPS请求链接。
https://nls-slp.cn-shanghai.aliyuncs.com/?Signature=xDyEd10%2FtcCLyq5mfV3QEipF9vs%3D&AccessKeyId=my_access_key_id&Action=CosyVoiceClone&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=3D472c6930-3f4f-11ef-a0b8-72ec8d600bed&SignatureVersion=1.0&Timestamp=2019-04-18T08%3A32%3A31Z&Url=my_url&Version=2019-08-19&VoicePrefix=my_voice_prefix
替换第5步获取的HTTPS请求链接,使用浏览器或者curl,进行CosyVoice声音复刻。
curl -X POST https://nls-slp.cn-shanghai.aliyuncs.com/?Signature=xDyEd10%2FtcCLyq5mfV3QEipF9vs%3D&AccessKeyId=my_access_key_id&Action=CosyVoiceClone&Format=JSON&RegionId=cn-shanghai&SignatureMethod=HMAC-SHA1&SignatureNonce=3D472c6930-3f4f-11ef-a0b8-72ec8d600bed&SignatureVersion=1.0&Timestamp=2019-04-18T08%3A32%3A31Z&Url=my_url&Version=2019-08-19&VoicePrefix=my_voice_prefix
完整示例
本文提供Java、Python语言示例代码,您可根据协议和示例实现其他语言的客户端程序。
调用接口前,需配置环境变量,通过环境变量读取访问凭证。智能语音交互的AccessKey ID和AccessKey Secret的环境变量名为:ALIYUN_AK_ID和ALIYUN_AK_SECRET。
Java示例
依赖文件:
<!-- http://mvnrepository.com/artifact/com.alibaba/fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.83</version>
</dependency>
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>3.9.1</version>
</dependency>
示例代码:
package org.example;
import okhttp3.*;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
public class OpenApiClone {
private final static String TIME_ZONE = "GMT";
private final static String FORMAT_ISO8601 = "yyyy-MM-dd'T'HH:mm:ss'Z'";
private final static String URL_ENCODING = "UTF-8";
private static final String ALGORITHM_NAME = "HmacSHA1";
private static final String ENCODING = "UTF-8";
/**
* 获取时间戳
* 必须符合ISO8601规范,并需要使用UTC时间,时区为+0。
*/
public static String getISO8601Time(Date date) {
Date nowDate = date;
if (null == date) {
nowDate = new Date();
}
SimpleDateFormat df = new SimpleDateFormat(FORMAT_ISO8601);
df.setTimeZone(new SimpleTimeZone(0, TIME_ZONE));
return df.format(nowDate);
}
/**
* 获取UUID
*/
public static String getUniqueNonce() {
UUID uuid = UUID.randomUUID();
return uuid.toString();
}
/**
* URL编码
* 使用UTF-8字符集按照RFC3986规则编码请求参数和参数取值。
*/
public static String percentEncode(String value) throws UnsupportedEncodingException {
return value != null ? URLEncoder.encode(value, URL_ENCODING).replace("+", "%20")
.replace("*", "%2A").replace("%7E", "~") : null;
}
/***
* 将参数排序后,进行规范化设置,组合成请求字符串。
* @param queryParamsMap 所有请求参数
* @return 规范化的请求字符串
*/
public static String canonicalizedQuery( Map<String, String> queryParamsMap) {
String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {});
Arrays.sort(sortedKeys);
String queryString = null;
try {
StringBuilder canonicalizedQueryString = new StringBuilder();
for (String key : sortedKeys) {
canonicalizedQueryString.append("&")
.append(percentEncode(key)).append("=")
.append(percentEncode(queryParamsMap.get(key)));
}
queryString = canonicalizedQueryString.toString().substring(1);
System.out.println("规范化后的请求参数串:" + queryString);
} catch (UnsupportedEncodingException e) {
System.out.println("UTF-8 encoding is not supported.");
e.printStackTrace();
}
return queryString;
}
/***
* 构造签名字符串
* @param method HTTP请求的方法
* @param urlPath HTTP请求的资源路径
* @param queryString 规范化的请求字符串
* @return 签名字符串
*/
public static String createStringToSign(String method, String urlPath, String queryString) {
String stringToSign = null;
try {
StringBuilder strBuilderSign = new StringBuilder();
strBuilderSign.append(method);
strBuilderSign.append("&");
strBuilderSign.append(percentEncode(urlPath));
strBuilderSign.append("&");
strBuilderSign.append(percentEncode(queryString));
stringToSign = strBuilderSign.toString();
System.out.println("构造的签名字符串:" + stringToSign);
} catch (UnsupportedEncodingException e) {
System.out.println("UTF-8 encoding is not supported.");
e.printStackTrace();
}
return stringToSign;
}
/***
* 计算签名
* @param stringToSign 签名字符串
* @param accessKeySecret 阿里云AccessKey Secret加上与号&
* @return 计算得到的签名
*/
public static String sign(String stringToSign, String accessKeySecret) {
try {
Mac mac = Mac.getInstance(ALGORITHM_NAME);
mac.init(new SecretKeySpec(
accessKeySecret.getBytes(ENCODING),
ALGORITHM_NAME
));
byte[] signData = mac.doFinal(stringToSign.getBytes(ENCODING));
String signBase64 = DatatypeConverter.printBase64Binary(signData);
System.out.println("计算的得到的签名:" + signBase64);
String signUrlEncode = percentEncode(signBase64);
System.out.println("UrlEncode编码后的签名:" + signUrlEncode);
return signUrlEncode;
} catch (NoSuchAlgorithmException e) {
throw new IllegalArgumentException(e.toString());
} catch (UnsupportedEncodingException e) {
throw new IllegalArgumentException(e.toString());
} catch (InvalidKeyException e) {
throw new IllegalArgumentException(e.toString());
}
}
/***
* 发送HTTP POST请求
* @param queryString 请求参数
*/
public static void processPostRequest(String queryString) {
/**
* 设置HTTP GET请求
* 1. 使用HTTP协议
* 2. 服务域名:nls-slp.cn-shanghai.aliyuncs.com
* 3. 请求路径:/
* 4. 设置请求参数
*/
String url = "https://nls-slp.cn-shanghai.aliyuncs.com";
url = url + "/";
url = url + "?" + queryString;
System.out.println("HTTP请求链接:" + url);
RequestBody body = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), "");
Request request = new Request.Builder()
.url(url)
.header("Accept", "application/json")
.post(body)
.build();
try {
OkHttpClient client = new OkHttpClient();
Response response = client.newCall(request).execute();
String result = response.body().string();
if (response.isSuccessful()) {
System.out.println(result);
}
else {
System.err.println("请求失败: " + result);
}
response.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 调用CosyVoiceClone接口复刻声音
* @param accessKeyId 阿里云AccessKey ID
* @param accessKeySecret 阿里云AccessKey Secret
* @param voicePrefix 声音前缀
* @param audioFileUrl 音频文件URL
*/
public static void cosyCLone(String accessKeyId, String accessKeySecret, String voicePrefix, String audioFileUrl) {
// 所有请求参数
Map<String, String> queryParamsMap = new HashMap<String, String>();
queryParamsMap.put("AccessKeyId", accessKeyId);
queryParamsMap.put("Action", "CosyVoiceClone");
queryParamsMap.put("Version", "2019-08-19");
queryParamsMap.put("Timestamp", getISO8601Time(null));
queryParamsMap.put("Format", "JSON");
queryParamsMap.put("RegionId", "cn-shanghai");
queryParamsMap.put("SignatureMethod", "HMAC-SHA1");
queryParamsMap.put("SignatureVersion", "1.0");
queryParamsMap.put("SignatureNonce", getUniqueNonce());
queryParamsMap.put("VoicePrefix", voicePrefix);
queryParamsMap.put("Url", audioFileUrl);
/**
* 1.构造规范化的请求字符串
*/
String queryString = canonicalizedQuery(queryParamsMap);
if (null == queryString) {
System.out.println("构造规范化的请求字符串失败!");
return;
}
/**
* 2.构造签名字符串
*/
String method = "POST"; // 发送请求的 HTTP 方法,POST
String urlPath = "/"; // 请求路径
String stringToSign = createStringToSign(method, urlPath, queryString);
if (null == stringToSign) {
System.out.println("构造签名字符串失败");
return;
}
/**
* 3.计算签名
*/
String signature = sign(stringToSign, accessKeySecret + "&");
if (null == signature) {
System.out.println("计算签名失败!");
return;
}
/**
* 4.将签名加入到第1步获取的请求字符串
*/
String queryStringWithSign = "Signature=" + signature + "&" + queryString;
System.out.println("带有签名的请求字符串:" + queryStringWithSign);
/**
* 5.发送HTTP POST请求,调用ListCosyVoice接口可以查询您某个前缀所有声音的状态。
*/
processPostRequest(queryStringWithSign);
}
/**
* 调用ListCosyVoice接口可以查询您某个前缀所有声音的状态。
* @param accessKeyId 阿里云AccessKey ID
* @param accessKeySecret 阿里云AccessKey Secret
* @param voicePrefix 声音前缀
* @param pageIndex 页码
* @param pageSize 页大小
*/
public static void cosyList(String accessKeyId, String accessKeySecret, String voicePrefix, int pageIndex, int pageSize) {
// 所有请求参数
Map<String, String> queryParamsMap = new HashMap<String, String>();
queryParamsMap.put("AccessKeyId", accessKeyId);
queryParamsMap.put("Action", "ListCosyVoice");
queryParamsMap.put("Version", "2019-08-19");
queryParamsMap.put("Timestamp", getISO8601Time(null));
queryParamsMap.put("Format", "JSON");
queryParamsMap.put("RegionId", "cn-shanghai");
queryParamsMap.put("SignatureMethod", "HMAC-SHA1");
queryParamsMap.put("SignatureVersion", "1.0");
queryParamsMap.put("SignatureNonce", getUniqueNonce());
queryParamsMap.put("VoicePrefix", voicePrefix);
queryParamsMap.put("PageIndex", String.valueOf(pageIndex));
queryParamsMap.put("PageSize", String.valueOf(pageSize));
/**
* 1.构造规范化的请求字符串
*/
String queryString = canonicalizedQuery(queryParamsMap);
if (null == queryString) {
System.out.println("构造规范化的请求字符串失败!");
return;
}
/**
* 2.构造签名字符串
*/
String method = "POST"; // 发送请求的 HTTP 方法,POST
String urlPath = "/"; // 请求路径
String stringToSign = createStringToSign(method, urlPath, queryString);
if (null == stringToSign) {
System.out.println("构造签名字符串失败");
return;
}
/**
* 3.计算签名
*/
String signature = sign(stringToSign, accessKeySecret + "&");
if (null == signature) {
System.out.println("计算签名失败!");
return;
}
/**
* 4.将签名加入到第1步获取的请求字符串
*/
String queryStringWithSign = "Signature=" + signature + "&" + queryString;
System.out.println("带有签名的请求字符串:" + queryStringWithSign);
/**
* 5.发送HTTP POST请求,调用ListCosyVoice接口可以查询您某个前缀所有声音的状态。
*/
processPostRequest(queryStringWithSign);
}
public static void main(String args[]) {
if (args.length < 2) {
System.err.println("Need params: <AccessKey Id> <AccessKey Secret>");
System.exit(-1);
}
String accessKeyId = System.getenv().get("ALIYUN_AK_ID");
String accessKeySecret = System.getenv().get("ALIYUN_AK_SECRET");
String voicePrefix = "your-voice-prefix";
String audioUrl = "audio-file-url";
System.out.println(getISO8601Time(null));
cosyCLone(accessKeyId, accessKeySecret, voicePrefix, audioUrl);
cosyList(accessKeyId, accessKeySecret, voicePrefix, 1, 10);
}
}
Python示例
Python版本要求:Python3.4及以上。
安装Python HTTP库Requests:
pip install requests
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import base64
import hashlib
import hmac
import requests
import time
import uuid
from urllib import parse
class CosyClone:
@staticmethod
def _encode_text(text):
encoded_text = parse.quote_plus(text)
return encoded_text.replace('+', '%20').replace('*', '%2A').replace('%7E', '~')
@staticmethod
def _encode_dict(dic):
keys = dic.keys()
dic_sorted = [(key, dic[key]) for key in sorted(keys)]
encoded_text = parse.urlencode(dic_sorted)
return encoded_text.replace('+', '%20').replace('*', '%2A').replace('%7E', '~')
@staticmethod
def cosy_clone(access_key_id, access_key_secret, voicePrefix, audio_url):
parameters = {'AccessKeyId': access_key_id,
'Action': 'CosyVoiceClone',
'Format': 'JSON',
'RegionId': 'cn-shanghai',
'SignatureMethod': 'HMAC-SHA1',
'SignatureNonce': str(uuid.uuid1()),
'SignatureVersion': '1.0',
'Timestamp': time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
'Version': '2019-08-19',
'VoicePrefix': voicePrefix,
'Url': audio_url,
}
# 构造规范化的请求字符串
query_string = CosyClone._encode_dict(parameters)
print('规范化的请求字符串: %s' % query_string)
# 构造待签名字符串
string_to_sign = 'POST' + '&' + CosyClone._encode_text('/') + '&' + CosyClone._encode_text(query_string)
print('待签名的字符串: %s' % string_to_sign)
# 计算签名
secreted_string = hmac.new(bytes(access_key_secret + '&', encoding='utf-8'),
bytes(string_to_sign, encoding='utf-8'),
hashlib.sha1).digest()
signature = base64.b64encode(secreted_string)
print('签名: %s' % signature)
# 进行URL编码
signature = CosyClone._encode_text(signature)
print('URL编码后的签名: %s' % signature)
# 调用服务
full_url = 'https://nls-slp.cn-shanghai.aliyuncs.com/?Signature=%s&%s' % (signature, query_string)
print('url: %s' % full_url)
# 提交HTTP POST请求
response = requests.post(full_url)
print(response.text)
@staticmethod
def cosy_list(access_key_id, access_key_secret, voice_prefix, page_index=1, page_size=10):
parameters = {'AccessKeyId': access_key_id,
'Action': 'ListCosyVoice',
'Format': 'JSON',
'RegionId': 'cn-shanghai',
'SignatureMethod': 'HMAC-SHA1',
'SignatureNonce': str(uuid.uuid1()),
'SignatureVersion': '1.0',
'Timestamp': time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
'Version': '2019-08-19',
'VoicePrefix': voice_prefix,
'PageIndex': page_index,
'PageSize': page_size,
}
# 构造规范化的请求字符串
query_string = CosyClone._encode_dict(parameters)
print('规范化的请求字符串: %s' % query_string)
# 构造待签名字符串
string_to_sign = 'POST' + '&' + CosyClone._encode_text('/') + '&' + CosyClone._encode_text(query_string)
print('待签名的字符串: %s' % string_to_sign)
# 计算签名
secreted_string = hmac.new(bytes(access_key_secret + '&', encoding='utf-8'),
bytes(string_to_sign, encoding='utf-8'),
hashlib.sha1).digest()
signature = base64.b64encode(secreted_string)
print('签名: %s' % signature)
# 进行URL编码
signature = CosyClone._encode_text(signature)
print('URL编码后的签名: %s' % signature)
# 调用服务
full_url = 'https://nls-slp.cn-shanghai.aliyuncs.com/?Signature=%s&%s' % (signature, query_string)
print('url: %s' % full_url)
# 提交HTTP POST请求
response = requests.post(full_url)
print(response.text)
if __name__ == "__main__":
# 用户信息
access_key_id = os.getenv('ALIYUN_AK_ID')
access_key_secret = os.getenv('ALIYUN_AK_SECRET')
voice_prefix = 'your-voice-prefix'
audio_url = 'your-audio-file-url'
CosyClone.cosy_clone(access_key_id, access_key_secret, voice_prefix, audio_url)
CosyClone.cosy_list(access_key_id, access_key_secret, voice_prefix)
服务状态码
状态码 | 状态消息 | 原因和处理方法 |
40001000 | QUOTA_ERROR | 检查是否开通服务。 |
40001001 | VOICE_LIMIT_ERROR | 音色克隆数量超限,目前默认1000个。 |
40001002 | VOICE_PREFIX_ERROR | 音色名前缀不满足规则:
|
40002000 | AUDIO_URL_ERROR | 音频URL地址无效。 |
40002001 | AUDIO_DOWNLOAD_FAIL | 下载音频失败。 |
40002002 | FILE_SIZE_EXCEED | 音频文件超过10 MB。 |
40002003 | AUDIO_SAMPLE_RATE_ERROR | 音频采样率小于16 kHz。 |
40002004 | AUDIO_FORMAT_ERROR | 音频格式错误,解码失败,目前支持 |
40003000 | SILENT_AUDIO_ERROR | 音频内无足够的有效语音。 |
40003001 | AUDIO_SNR_ERROR | 音频信噪比太低。 |
50000000 | SERVER_ERROR | 服务错误,一般可通过重试解决。 在使用CosyVoice声音复刻功能时,如果出现该错误,通常是因为录音质量不合格。请按照录音操作指南中的指引重新录制声音并进行声音复刻。请注意尽量保证录制的声音无杂音,并避免频繁的不必要的停顿,确保至少有5秒以上的连续声音。 |