
# 声音复刻流式接口
#### 前提条件
- 确保已按照[配置Java环境](https://support.huaweicloud.com/sdkreference-sis/sis_05_0041.html)配置完毕。
- 请参考[SDK（websocket）](https://support.huaweicloud.com/sdkreference-sis/sis_05_0004.html)获取最新版本SDK包。
- 使用预置音色或注册的声音合成语音。
- 仅上海一局点支持该接口。
#### 初始化Client
初始化VcsClient详见[表1]。
 表1VcsClient初始化参数 
| 参数名称            | 是否必选 | 参数类型   | 描述                                                                                                                      |
|:---|:---|:---|:---|
| ak              | 是    | String | 用户的ak，可参考[AK/SK认证](https://support.huaweicloud.com/sdkreference-sis/sis_05_0003.html)。                                  |
| sk              | 是    | String | 用户的sk，可参考[AK/SK认证](https://support.huaweicloud.com/sdkreference-sis/sis_05_0003.html)。                                  |
| region          | 是    | String | 区域，如：cn-east-3，具体请参考[终端节点](https://support.huaweicloud.com/api-sis/sis_03_0004.html)。                                   |
| project_id      | 是    | String | 项目ID，同region一一对应，参考获取项目ID。                                                                                              |
| serviceEndPoint | 否    | String | 终端节点，一般使用默认即可，例如：wss://\*\*\*\*\*.myhuaweicloud.com，详见[终端节点](https://support.huaweicloud.com/api-sis/sis_03_0004.html)。 |
   
#### 请求参数
请求类型为VcsRequest，详见[表2]。
 表2VcsRequest 
| 参数名称       | 是否必选 | 参数类型   | 描述                                                                                          |
|:---|:---|:---|:---|
| command    | 是    | String | 指令：START、MSG、END，分别表示开始、语音合成、结束。                                                            |
| config     | 是    | Object | 详见[表3]。                                                    |
| textPieces | 否    | String | 文本条数：ONLY(仅一次)、MULTI(可发送多次文本)。默认：MULTI。                                                     |
| text       | 是    | String | 待合成的文本，支持长度不大于300字符的文本。textPieces取值为ONLY时，文本同配置一同发送，此值必填；textPieces取值为MULTI时，文本后续发送，不需要该参数。 |
| audioPath  | 是    | String | 待合成音频存储路径。                                                                                  |
| textPath   | 否    | String | 待合成文本存储路径。用户既可以选择多次发送文本，也可以传入文本存储路径。                                                        |
   
 表3Config 
| 参数名称        | 是否必选 | 参数类型    | 描述                                                                                     |
|:---|:---|:---|:---|
| audioFormat | 否    | String  | 合成音频的格式。支持pcm、mp3，默认pcm                                                                |
| sampleRate  | 否    | String  | 合成音频的采样率。支持8kHz、16kHz、24kHz，默认16kHz                                                    |
| voiceName   | 是    | String  | 合成所使用的音色名称，可选择用户注册的音色或预置音色。预置音色取值范围参考[表4]。           |
| speed       | 否    | Integer | 语速。 取值范围：-500\~500 默认值：0   |
| pitch       | 否    | Integer | 音高。 取值范围： -500\~500 默认值：0 |
| volume      | 否    | Integer | 音量。 取值范围：0\~100 默认值：50     |
   
 表4Timbre 
| 参数名称 | voice_name                  | 类型   | 使用场景 | 支持采样率（Hz）  |
|:---|:---|:---|:---|:---|
| 华小莉  | chinese_huaxiaoli_common    | 标准女声 | 中英混合 | 8k/16k/24k |
| 华小智  | chinese_huaxiaozhi_common   | 男童声  | 中英混合 | 8k/16k/24k |
| 华小天  | chinese_huaxiaotian_common  | 朝气男声 | 中英混合 | 8k/16k/24k |
| 华小媛  | chinese_huaxiaoyuan_common  | 成熟女声 | 中英混合 | 8k/16k/24k |
| 华小静  | chinese_huaxiaojing_common  | 女童声  | 中英混合 | 8k/16k/24k |
| 华小博  | chinese_huaxiaobo_common    | 标准男声 | 中英混合 | 8k/16k/24k |
| 华小柔  | chinese_huaxiaorou_common   | 标准女声 | 中英混合 | 8k/16k/24k |
| 华小悠  | chinese_huaxiaoyou_common   | 嘹亮女声 | 中英混合 | 8k/16k/24k |
| 华小晴  | chinese_huaxiaoqing_common  | 青春女声 | 中英混合 | 8k/16k/24k |
| 华小溪  | chinese_huaxiaoxi_common    | 温柔女声 | 中英混合 | 8k/16k/24k |
| 华小彤  | chinese_huaxiaotong_common  | 俏皮女声 | 中英混合 | 8k/16k/24k |
| 华小雅  | chinese_huaxiaoya_common    | 标准女声 | 中英混合 | 8k/16k/24k |
| 华小伟  | chinese_huaxiaowei_common   | 成熟男声 | 中英混合 | 8k/16k/24k |
| 华小帅  | chinese_huaxiaoshuai_common | 标准男声 | 中英混合 | 8k/16k/24k |
| 华小杰  | chinese_huaxiaojie_common   | 温柔男声 | 中英混合 | 8k/16k/24k |
   
#### 响应参数
响应参数分为正确响应，详见[表5]，和错误响应，详见[表6]。
 表5正确响应 
| 参数名称     | 参数类型   | 描述                     |
|:---|:---|:---|
| respType | String | 响应类型。参数值为END，表示语音合成结束。 |
| traceId  | String | 服务内部的令牌，可用于在日志中追溯具体流程。 |
| reason   | String | 结束原因。                  |
   
 表6错误响应 
| 参数名称      | 参数类型   | 描述                                                                                                                                                                                                   |
|:---|:---|:---|
| respType  | String | 响应类型。 参数值为ERROR，表示错误响应。 参数值为FATAL_ERROR，表示此次合成发生不可恢复的错误。 参数值为EVENT，表示事件，详见[表7]。 |
| traceId   | String | 服务内部的令牌，可用于在日志中追溯具体流程。                                                                                                                                                                               |
| errorCode | String | 错误码列表。详细错误码解释，请参见错误码。                                                                                                                                                                                |
| errorMsg  | String | 返回错误信息。                                                                                                                                                                                              |
   
 表7事件 
| 参数名称      | 参数类型   | 描述                                              |
|:---|:---|:---|
| event     | String | 事件类型，详见[表8]。 |
| timeStamp | Object | 时间戳。                                            |
   
 表8事件类型 
| 参数名称                       | 参数类型   | 描述              |
|:---|:---|:---|
| EXCEEDED_TOTAL_TEXT_LENGTH | String | MSG 总字数超过10万字符。 |
| VCS_END                    | String | 结束指令成功后，继续发送指令。 |
   
#### 请求示例
```
package com.huawei.sis.demo.cn_demo;
import com.cloud.sdk.util.StringUtils;
import com.huawei.sis.bean.AuthInfo;
import com.huawei.sis.bean.SisConfig;
import com.huawei.sis.bean.SisConstant;
import com.huawei.sis.bean.VcsListener;
import com.huawei.sis.bean.request.VcsRequest;
import com.huawei.sis.bean.response.StateResponse;
import com.huawei.sis.bean.response.VcsDataResponse;
import com.huawei.sis.client.RvcsClient;
import com.huawei.sis.exception.SisException;
import com.huawei.sis.util.JsonUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
/**
 * 声音复刻流式接口Demo
 * <p>
 * Copyright 2021 Huawei Technologies Co.,Ltd.
 */
public class RvcsDemo {
    private static final Logger log = LoggerFactory.getLogger(RvcsDemo.class);
    // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险，建议在配置文件或者环境变量中密文存放，使用时解密，确保安全；
    private String ak = System.getenv("HUAWEICLOUD_SDK_AK");
    private String sk = System.getenv("HUAWEICLOUD_SDK_SK");
    private String region = "";    // 区域，如cn-east-3
    private String projectId = ""; // 项目id，在我的凭证查看。参考https://support.huaweicloud.com/api-sis/sis_03_0008.html
    private String serviceEndPoint = "";  // 可选参数,传入new AuthInfo(ak, sk, region, projectId);
    private String audioPath = ""; // 待合成音频存储路径
    private String textPath = ""; // 可选 待合成文本存储路径,例如"D://text.txt",要求文本中，每行句子不超过300字符
    // 使用该脚本前,保证要使用的声音已经注册,voiceName为声音名称。若没有注册声音,请使用VcsRegisterAndRequest.java脚本，注册声音和查询声音
    private String voiceName = ""; // 音色名称，可以使用注册音色或预置音色
    public static void main(String[] args) {
        RvcsDemo rvcsDemo = new RvcsDemo();
        rvcsDemo.process();
    }
    private SisConfig getConfig() {
        SisConfig config = new SisConfig();
        // 设置连接超时，默认10000ms
        config.setConnectionTimeout(SisConstant.DEFAULT_CONNECTION_TIMEOUT);
        // 设置读取超时，默认10000ms
        config.setReadTimeout(SisConstant.DEFAULT_READ_TIMEOUT);
        // 设置代理, 一定要确保代理可用才启动此设置。 代理初始化也可用不加密的代理，new ProxyHostInfo(host, port);
        // 设置代理，可参考 config.setProxy(new ProxyHostInfo(host, port, user, password));
        return config;
    }
    private void setParameters(VcsRequest request) {
        // 1. 设置音频格式，默认pcm
        request.setAudioFormat("pcm");
        // 2. 设置采样率，24000、16000、8000，默认16000
        request.setSampleRate("16000");
        // 3. 设置音色，如 chinese_huaxiaoli_common、chinese_huaxiaotian_common
        request.setVoiceName(voiceName);
        // 4. 设置语速，取值范围为 -500~500，默认值为0
        request.setSpeed(0);
        // 5. 设置音调，取值范围为 -500~500，默认值为0
        request.setPitch(0);
        // 6. 设置音量，取值范围为 0~100，默认值为50
        request.setVolume(50);
        // 7. 设置模式，取值 ONLY 或 MULTI，默认为MULTI
        // ONLY模式下，用户发送一次文本，服务端流式返回语音数据；MULTI模式下，支持用户多次发送文本，服务端流式返回语音数据，可用于大模型输出实时播报等场景。
        request.setTextPieces("MULTI");
    }
    private static class MyVcsListener implements VcsListener {
        private String path;
        private FileOutputStream fos = null;
        private VcsListenerCallback vcsListenerCallback = new VcsListenerCallback();
        public MyVcsListener() {
        }
        public MyVcsListener(String path) {
            this.path = path;
        }
        @Override
        public void onTranscriptionClose() {
            System.out.println("websocket closed");
        }
        @Override
        public void onTranscriptionConnect() {
            System.out.println("websocket connected");
        }
        @Override
        public void onTranscriptionResponse(VcsDataResponse response) {
            System.out.println("receive binary data " + response.getData().length);
            if (fos == null) {
                return;
            }
            try {
                fos.write(response.getData());
                fos.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onTranscriptionBegin(StateResponse response) {
            printResponse(response);
            try {
                if (StringUtils.isNullOrEmpty(path)) {
                    return;
                }
                File f = new File(path);
                fos = new FileOutputStream(f);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onSTranscriptionEnd(StateResponse response) {
            vcsListenerCallback.onEnd();
            printResponse(response);
            close();
        }
        @Override
        public void onTranscriptionFail(StateResponse response) {
            vcsListenerCallback.onFail();
            printResponse(response);
            close();
        }
        private void close() {
            if (fos == null) {
                return;
            }
            try {
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        public void onCallback(VcsListenerCallback callback) {
            if (callback == null) {
                return;
            }
            this.vcsListenerCallback = callback;
        }
        public static class VcsListenerCallback {
            public void onEnd() {
            }
            public void onFail() {
            }
        }
    }
    private void process() {
        try {
            // 1. 实现监听器接口VcsListener，用户自定义收到响应的处理逻辑
            MyVcsListener vcsListener = new MyVcsListener(audioPath);
            // 2. 初始化RVcsClient
            AuthInfo authInfo = new AuthInfo(ak, sk, region, projectId);
            RvcsClient rvcsClient = new RvcsClient(authInfo, vcsListener, getConfig());
            // 设置回调关闭客户端。此步一定要实施，否则数据处理结束后服务端会因为20s没有接收任何消息而报异常。
            vcsListener.onCallback(new MyVcsListener.VcsListenerCallback() {
                @Override
                public void onEnd() {
                    super.onEnd();
                    rvcsClient.close();
                }
                @Override
                public void onFail() {
                    super.onFail();
                    rvcsClient.close();
                }
            });
            // 3. 配置参数
            VcsRequest vcsRequest = new VcsRequest();
            setParameters(vcsRequest);
            // 4. 建立websocket连接
            rvcsClient.connect(vcsRequest);
            // 5. 发送开始请求、发送文本、发送end请求
            if ("ONLY".equals(vcsRequest.getTextPieces())) {
                // ONLY 模式仅需发送start命令，合成文本须随start命令一起发送
                vcsRequest.setText("欢迎使用华为云语音交互服务。");
                rvcsClient.sendStart();
            } else {
                // 发送开始请求，即将开始请求连带配置发送至服务端
                rvcsClient.sendStart();
                // 发送文本，可连续发送多次，合成结果可以通过监听器获取
                // 方法一,多次发送文本
                rvcsClient.sendMsg("你好");
                rvcsClient.sendMsg("欢迎使用华为云语音交互服务。");
                // 方法二,传入文本路径
                //rvcsClient.sendMsgBatch(textPath); // 传入待合成文本储存路径
                // 发送结束请求
                rvcsClient.sendEnd();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    private static void printResponse(Object response) {
        try {
            System.out.println(JsonUtils.obj2Str(response, true));
        } catch (SisException e) {
            e.printStackTrace();
        }
    }
}
```
#### 响应参数（开始合成）
声音复刻引擎收到合成请求后，会向客户端发送合成开始响应，表示开始处理声音复刻请求。
```
{  
    "resp_type": "START", 
    "trace_id": "567e8537-a89c-13c3-a882-826321939651"  
}
```
#### 响应参数（合成结果）
分多段返回二进制pcm格式或mp3的语音数据流。
#### 响应参数（合成结束）
当合成引擎处理完合成请求后，会发送合成结束响应。客户端收到该响应后关闭当前Websocket链接即可。
```
{  
    "resp_type": "END", 
     "trace_id": "567e8537-a89c-13c3-a882-826321939651", 
     "reason": "NORMAL" 
}
```
#### 响应参数（合成错误）
```
{  
    "resp_type": "ERROR", 
     "trace_id": "567e8537-a89c-13c3-a882-826321939651",  
     "error_code":"SIS.1010",   
     "error_msg":"No valid text, please enter Chinese characters, letters, or digits"
}
```
```
{  
    "resp_type": "FATAL_ERROR",  
     "trace_id": "567e8537-a89c-13c3-a882-826321939651",  
     "error_code": "SIS.0416",  
     "error_msg": "vcs wait text time out"  
}
```
```
{  
    "resp_type": "EVENT",     
     "trace_id": "567e8537-a89c-13c3-a882-826321939651",      
     "event":"VCS_END", 
     "timestamp":1712565936 
}
```
