Clone voices using OpenAPI

Updated at:

The CosyVoice voice cloning service lets you quickly generate a natural-sounding custom voice that closely resembles the original. You only need to provide a short audio sample. To clone a voice with CosyVoice using OpenAPI, you must write your own code to handle identity verification and request logic. You can use OpenAPI to clone and query voices if a software development kit (SDK) is not available for your programming language or to control dependent components. This topic describes the API protocol.

Request message

The client can send requests over HTTPS using the POST method. The server provides an API that is based on the Alibaba Cloud POP protocol. Therefore, the client must implement the Alibaba Cloud POP signature mechanism.

  • URL

    Protocol

    URL

    Method

    HTTPS

    https://nls-slp.cn-shanghai.aliyuncs.com

    POST

  • Request parameters

    Name

    Type

    Required

    Description

    AccessKeyId

    String

    Yes

    The AccessKey ID of your Alibaba Cloud account.

    Action

    String

    Yes

    POP API name:

    • Voice cloning: CosyVoiceClone

    • Query voices: ListCosyVoice

    Version

    String

    Yes

    POP API Version: 2019-08-19

    Format

    String

    Yes

    The format of the response: JSON

    RegionId

    String

    Yes

    The ID of the region where the service is located: cn-shanghai

    Timestamp

    String

    Yes

    The timestamp of the request. The timestamp must follow the ISO 8601 standard and be in UTC. The time zone is +0. Format: YYYY-MM-DDThh:mm:ssZ. For example, 2019-04-03T06:15:03Z represents 06:15:03 on April 3, 2019 (UTC).

    SignatureMethod

    String

    Yes

    The signature algorithm: HMAC-SHA1

    SignatureVersion

    String

    Yes

    The version of the signature algorithm: 1.0

    SignatureNonce

    String

    Yes

    A universally unique identifier (UUID) that prevents replay attacks. Each request must have a unique UUID that cannot be reused. The format is A-B-C-D-E, where the number of characters for A, B, C, D, and E is 8, 4, 4, 4, and 12, respectively. Example: 8d1e6a7a-f44e-40d5-aedb-fe4a1c80f434.

    Signature

    String

    Yes

    The signature string calculated from all request parameters. For information about how to generate the signature, see the "Signature mechanism" section.

    VoicePrefix

    String

    Yes

    The prefix for the cloned voice. It can contain only lowercase letters and digits, and must be no more than 10 characters in length.

    Url

    String

    Required only for CosyVoiceClone.

    The URL of the audio file for voice cloning.

    PageIndex

    Integer

    Used only for ListCosyVoice. This parameter is optional. The default value is 1.

    The page number of the voice list to query.

    PageSize

    Integer

    Used only for ListCosyVoice. This parameter is optional. The default value is 10.

    The number of voices on each page.

  • HTTPS request headers

    An HTTPS request header consists of key-value pairs. Each pair is on a separate line. The key and value in each pair are separated by a colon (:). The header includes the following content:

    Name

    Type

    Required

    Description

    Host

    String

    No

    The server domain name for HTTPS requests is nls-meta.cn-shanghai.aliyuncs.com. This domain name is typically parsed automatically from the request URL.

    Accept

    String

    No

    Specifies the content types that the client can accept: application/json. If not set, the default is */*.

    Content-type

    String

    Required for the POST method

    Specifies the data format of the request body for the POST method: application/x-www-form-urlencoded.

Message examples

  • HTTPS POST request message

    • 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

Response

After you send an HTTPS request, the server returns a response. The result is returned in the response as a JSON string.

  • Successful response

    The HTTPS status code is 200. The response fields are described in the following table.

    Parameter

    Type

    Description

    RequestId

    String

    requestId

    Message

    String

    SUCCESS

    VoiceName

    String

    The name of the successfully cloned 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": [
      
          ]
      }
  • Failed response

    The HTTPS status code is not 200. The response fields are described in the following table.

    Parameter

    Type

    Description

    RequestId

    String

    The ID of the request.

    Message

    String

    The error message returned for the failed request.

    Recommend

    String

    Troubleshooting suggestions.

    HostId

    String

    The ID of the host.

    Code

    String

    The error type of the failed response. For more information, see Service status codes.

    {
        "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"
    }

Signature mechanism

The server-side OpenAPI authenticates the sender of each API request. You must include signature information in your requests. The signature mechanism allows the server to identify the user who sends the API request and verify that the request has not been tampered with during transmission.

Security authentication flow

To calculate a signature, you need the AccessKey ID and AccessKey secret of your Alibaba Cloud account. The HMAC-SHA1 algorithm is used for symmetric encryption. The workflow is as follows:

  1. The client generates a string to sign based on the API request content, which includes the HTTPS request parameters and the request body.

  2. The client uses the AccessKey ID and AccessKey secret of the Alibaba Cloud account to sign the string that is generated in the previous step. This creates a digital signature for the API request.

  3. The client sends the API request content and the digital signature to the server.

  4. After receiving the request, the server repeats steps 1 and 2 to calculate the expected digital signature. The server retrieves the user's key.

  5. The server compares the expected digital signature with the digital signature sent by the client. If they match, the request is authenticated. Otherwise, the request is rejected.

Generate the string to sign for a request

  1. Construct the canonicalized query string.

    Construct a canonicalized query string from the HTTPS request parameters, excluding the Signature parameter. The steps are as follows:

    1. Sort the parameters. Sort the request parameters by parameter name in case-sensitive lexicographical order.

    2. Encode the parameters. Encode the sorted request parameters.

      URL-encode the request parameter names and values using the UTF-8 character set. The URL encoding rules are as follows:

      • Do not encode uppercase letters (A-Z), lowercase letters (a-z), digits (0-9), hyphens (-), underscores (_), periods (.), or tildes (~).

      • Encode other characters into the "%XY" format, where XY is the hexadecimal representation of the character's ASCII code. For example, a double quotation mark (") is encoded as %22.

      • Encode extended UTF-8 characters into the "%XY%ZA..." format.

      • An English space must be encoded as %20, not as a plus sign (+).

      Note

      Most URL encoding libraries, such as `java.net.URLEncoder` in Java, encode according to the rules of the "application/x-www-form-urlencoded" Multipurpose Internet Mail Extensions (MIME) type. You can use this method for encoding. Then, in the encoded string, replace plus signs (+) with %20, asterisks (*) with %2A, and %7E with tildes (~) to obtain the encoded string that is described in the preceding rules.

    3. Use an equal sign (=) to connect the URL-encoded parameter name and value: percentEncode(ParameterKey) + "=" + percentEncode(ParameterValue).

    4. Use an ampersand (&) to connect the URL-encoded request parameter pairs from the previous step. For example: Action=CreateToken&Format=JSON.

      Note

      Do not add an ampersand (&) before the first parameter name in the string.

    5. Return the canonicalized query string.

    The following provides an example:

    String percentEncode(String value) throws UnsupportedEncodingException {
        return value != null ? URLEncoder.encode(value, URL_ENCODING)
                .replace("+", "%20")
                .replace("*", "%2A")
                .replace("%7E", "~") : null;
    }
    
    // Sort the parameter keys
    String[] sortedKeys = queryParamsMap.keySet().toArray(new String[] {});
    Arrays.sort(sortedKeys);
    
    // Encode and concatenate the sorted parameters
    for (String key : sortedKeys) {
        canonicalizedQueryString.append("&")
                .append(percentEncode(key)).append("=")
                .append(percentEncode(queryParamsMap.get(key)));
    }
    queryString = canonicalizedQueryString.toString().substring(1);
    Note

    For the complete code implementation, see the canonicalizedQuery function in the full example.

    Construct the canonicalized query string:

    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
  2. Construct the string to sign.

    Concatenate the HTTPS request method (GET), the URL-encoded URL path (/), and the canonicalized query string from step 1 with the & character to create the string to be signed: HTTPSMethod + "&" + percentEncode("/") + "&" + percentEncode(queryString).

    The following code provides an example of how to construct the string to sign:

    StringBuilder strBuilderSign = new StringBuilder();
    strBuilderSign.append(HTTPMethod);
    strBuilderSign.append("&");
    strBuilderSign.append(percentEncode(urlPath));
    strBuilderSign.append("&");
    strBuilderSign.append(percentEncode(queryString));
    
    stringToSign = strBuilderSign.toString();
    Note

    For the complete code implementation, see the createStringToSign function in the full example.

    The constructed string to sign:

    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
  3. Calculate the signature.

    • The signature is calculated using the HMAC-SHA1 algorithm and then Base64-encoded. The character encoding is UTF-8.

    • Use your AccessKey secret to calculate the HMAC-SHA1 digital signature for the string to sign that you constructed in Step 2. When calculating the signature, you must append an ampersand (&) to your AccessKey secret.

    • The signature must also be URL-encoded.

    The following code provides an example of how to calculate the signature:

    signature = Base64( HMAC-SHA1(stringToSign, accessKeySecret + "&") );
    // URL-encode the signature
    signature = percentEncode(signature)
    Note

    For the complete code implementation, see the sign function in the full example.

    The calculated signature:

    # Signature string
    AKIktdPUMCV12fTh667BLXeuCtg=
    # URL-encoded result
    AKIktdPUMCV12fTh667BLXeuCtg%3D

    After you calculate the signature, join its key and value with an = character and append the result to the query string from step 1 with an & character. You can then send the complete string to the server-side in an HTTPS GET request to obtain the token.

    String queryStringWithSign = "Signature=" + signature + "&" + queryString;

Quick test

You can use the following parameters to calculate a signature and compare the result with your own.

Note

The AccessKey ID and AccessKey secret are not real. The timestamp has expired. A signature calculated with these parameters will fail when you try to obtain a token. This test is provided only for you to compare your signature calculation results.

  • AccessKeyId:

    my_access_key_id

  • AccessKeySecret:

    my_access_key_secret

  • Timestamp:

    2019-04-18T08:32:31Z

  • SignatureNonce:

    b924c8c3-6d03-4c5d-ad36-d984d3116788

The request parameters are as follows:

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
  1. Construct the canonicalized query string.

    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
  2. Construct the string to sign.

    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
  3. Calculate the signature.

    xDyEd10/tcCLyq5mfV3QEipF9vs=
    # URL-encoded result
    xDyEd10%2FtcCLyq5mfV3QEipF9vs%3D
  4. Obtain the request string with the signature.

    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. Assemble the HTTPS request URL.

    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
  6. Use the HTTPS request URL from Step 5 to clone a CosyVoice voice using a browser or cURL.

    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

Complete examples

Note
  • This topic provides example code in Java and Python. You can implement client programs in other languages based on the protocol and the provided examples.

  • Before you call the API, configure environment variables to read your access credentials. The environment variable names for the Intelligent Speech Interaction AccessKey ID and AccessKey secret are ALIYUN_AK_ID and ALIYUN_AK_SECRET.

Java example

Dependencies:

<!-- 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>

Example code:

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";
    /**
     * Get the timestamp.
     * Must conform to the ISO 8601 standard and use UTC with time zone +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);
    }
    /**
     * Get the UUID.
     */
    public static String getUniqueNonce() {
        UUID uuid = UUID.randomUUID();
        return uuid.toString();
    }
    /**
     * URL encoding.
     * Use the UTF-8 character set to encode request parameters and their values according to RFC 3986.
     */
    public static String percentEncode(String value) throws UnsupportedEncodingException {
        return value != null ? URLEncoder.encode(value, URL_ENCODING).replace("+", "%20")
                .replace("*", "%2A").replace("%7E", "~") : null;
    }
    /***
     * Sorts the parameters, normalizes them, and combines them into a request string.
     * @param queryParamsMap   All request parameters.
     * @return The canonicalized query string.
     */
    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("Canonicalized query string: " + queryString);
        } catch (UnsupportedEncodingException e) {
            System.out.println("UTF-8 encoding is not supported.");
            e.printStackTrace();
        }
        return queryString;
    }
    /***
     * Constructs the string to sign.
     * @param method       The HTTP request method.
     * @param urlPath      The URI of the HTTP request.
     * @param queryString  The canonicalized query string.
     * @return The string to sign.
     */
    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("Constructed string to sign: " + stringToSign);
        } catch (UnsupportedEncodingException e) {
            System.out.println("UTF-8 encoding is not supported.");
            e.printStackTrace();
        }
        return stringToSign;
    }
    /***
     * Calculates the signature.
     * @param stringToSign      The string to sign.
     * @param accessKeySecret   The Alibaba Cloud AccessKey secret with an ampersand (&) appended.
     * @return The calculated signature.
     */
    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("Calculated signature: " + signBase64);
            String signUrlEncode = percentEncode(signBase64);
            System.out.println("URL-encoded signature: " + 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());
        }
    }
    /***
     * Sends an HTTP POST request.
     * @param queryString The request parameters.
     */
    public static void processPostRequest(String queryString) {
        /**
         * Configure the HTTP POST request.
         * 1. Use the HTTPS protocol.
         * 2. Service domain name: nls-slp.cn-shanghai.aliyuncs.com
         * 3. Request path: /
         * 4. Set the request parameters.
         */
        String url = "https://nls-slp.cn-shanghai.aliyuncs.com";
        url = url + "/";
        url = url + "?" + queryString;
        System.out.println("HTTP request URL: " + 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("Request failed: " + result);
            }
            response.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * Calls the CosyVoiceClone operation to clone a voice.
     * @param accessKeyId The Alibaba Cloud AccessKey ID.
     * @param accessKeySecret The Alibaba Cloud AccessKey secret.
     * @param voicePrefix The voice prefix.
     * @param audioFileUrl The URL of the audio file.
     */
    public static void cosyCLone(String accessKeyId, String accessKeySecret, String voicePrefix, String audioFileUrl) {
        // All request parameters
        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. Construct the canonicalized query string.
         */
        String queryString = canonicalizedQuery(queryParamsMap);
        if (null == queryString) {
            System.out.println("Failed to construct the canonicalized query string!");
            return;
        }
        /**
         * 2. Construct the string to sign.
         */
        String method = "POST";  // The HTTP method for the request, POST.
        String urlPath = "/";   // The request path.
        String stringToSign = createStringToSign(method, urlPath, queryString);
        if (null == stringToSign) {
            System.out.println("Failed to construct the string to sign");
            return;
        }
        /**
         * 3. Calculate the signature.
         */
        String signature = sign(stringToSign, accessKeySecret + "&");
        if (null == signature) {
            System.out.println("Failed to calculate the signature!");
            return;
        }
        /**
         * 4. Add the signature to the request string from step 1.
         */
        String queryStringWithSign = "Signature=" + signature + "&" + queryString;
        System.out.println("Request string with signature: " + queryStringWithSign);
        /**
         * 5. Send an HTTP POST request. You can call the ListCosyVoice operation to query the status of all voices with a specific prefix.
         */
        processPostRequest(queryStringWithSign);
    }

    /**
     * Calls the ListCosyVoice operation to query the status of all voices with a specific prefix.
     * @param accessKeyId The Alibaba Cloud AccessKey ID.
     * @param accessKeySecret The Alibaba Cloud AccessKey secret.
     * @param voicePrefix The voice prefix.
     * @param pageIndex The page number.
     * @param pageSize The page size.
     */
    public static void cosyList(String accessKeyId, String accessKeySecret, String voicePrefix, int pageIndex, int pageSize) {
        // All request parameters
        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. Construct the canonicalized query string.
         */
        String queryString = canonicalizedQuery(queryParamsMap);
        if (null == queryString) {
            System.out.println("Failed to construct the canonicalized query string!");
            return;
        }
        /**
         * 2. Construct the string to sign.
         */
        String method = "POST";  // The HTTP method for the request, POST.
        String urlPath = "/";   // The request path.
        String stringToSign = createStringToSign(method, urlPath, queryString);
        if (null == stringToSign) {
            System.out.println("Failed to construct the string to sign");
            return;
        }
        /**
         * 3. Calculate the signature.
         */
        String signature = sign(stringToSign, accessKeySecret + "&");
        if (null == signature) {
            System.out.println("Failed to calculate the signature!");
            return;
        }
        /**
         * 4. Add the signature to the request string from step 1.
         */
        String queryStringWithSign = "Signature=" + signature + "&" + queryString;
        System.out.println("Request string with signature: " + queryStringWithSign);
        /**
         * 5. Send an HTTP POST request. You can call the ListCosyVoice operation to query the status of all voices with a specific prefix.
         */
        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 = "your-audio-file-url";
        System.out.println(getISO8601Time(null));

        cosyCLone(accessKeyId, accessKeySecret, voicePrefix, audioUrl);
        cosyList(accessKeyId, accessKeySecret, voicePrefix, 1, 10);
    }
}

Python Example

Note
  • Python version requirement: Python 3.4 or later.

  • Install the Requests HTTP library for Python:

  •  pip install requests
#!/usr/bin/env python3
# -*- 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,
                      }
        # Construct the canonicalized query string
        query_string = CosyClone._encode_dict(parameters)
        print('Canonicalized query string: %s' % query_string)
        # Construct the string to sign
        string_to_sign = 'POST' + '&' + CosyClone._encode_text('/') + '&' + CosyClone._encode_text(query_string)
        print('String to sign: %s' % string_to_sign)
        # Calculate the signature
        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('Signature: %s' % signature)
        # URL-encode the signature
        signature = CosyClone._encode_text(signature)
        print('URL-encoded signature: %s' % signature)
        # Call the service
        full_url = 'https://nls-slp.cn-shanghai.aliyuncs.com/?Signature=%s&%s' % (signature, query_string)
        print('url: %s' % full_url)
        # Submit the HTTP POST request
        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,
                      }
        # Construct the canonicalized query string
        query_string = CosyClone._encode_dict(parameters)
        print('Canonicalized query string: %s' % query_string)
        # Construct the string to sign
        string_to_sign = 'POST' + '&' + CosyClone._encode_text('/') + '&' + CosyClone._encode_text(query_string)
        print('String to sign: %s' % string_to_sign)
        # Calculate the signature
        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('Signature: %s' % signature)
        # URL-encode the signature
        signature = CosyClone._encode_text(signature)
        print('URL-encoded signature: %s' % signature)
        # Call the service
        full_url = 'https://nls-slp.cn-shanghai.aliyuncs.com/?Signature=%s&%s' % (signature, query_string)
        print('url: %s' % full_url)
        # Submit the HTTP POST request
        response = requests.post(full_url)
        print(response.text)


if __name__ == "__main__":
    # User information
    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)
   

Service status codes

Status code

Status message

Cause and solution

40001000

QUOTA_ERROR

Check whether the service is activated.

40001001

VOICE_LIMIT_ERROR

The number of cloned voices exceeds the limit. The default limit is 1,000.

40001002

VOICE_PREFIX_ERROR

The voice name prefix does not meet the following rules:

  • It cannot be empty.

  • It can be up to 10 characters in length.

  • It can contain only digits and letters.

40002000

AUDIO_URL_ERROR

The audio URL is invalid.

40002001

AUDIO_DOWNLOAD_FAIL

Failed to download the audio.

40002002

FILE_SIZE_EXCEED

The audio file exceeds 10 MB.

40002003

AUDIO_SAMPLE_RATE_ERROR

The audio sample rate is less than 16 kHz.

40002004

AUDIO_FORMAT_ERROR

The audio format is incorrect and decoding failed. The supported formats are wav, mp3, m4a, and aac.

40003000

SILENT_AUDIO_ERROR

There is not enough valid speech in the audio.

40003001

AUDIO_SNR_ERROR

The signal-to-noise ratio (SNR) of the audio is too low.

50000000

SERVER_ERROR

A service error occurred. Retrying the operation usually resolves the issue.

When you use the CosyVoice voice cloning feature, this error usually occurs because the recording quality is poor. Follow the instructions in the Recording guide to re-record the audio and perform voice cloning. Make sure that the recorded audio has no noise, avoid frequent and unnecessary pauses, and ensure that there are at least 5 seconds of continuous sound.

-

ACCESS_DENIED : Permission denied!

Permission denied.

During voice cloning, this error occurs if you use a RAM user that has not been granted the AliyunNLSFullAccess permission. For more information, see Manage RAM user permissions.