一、解决byte[]数组转String类型失真
public class ByteConvertUtil {
/**
* 二进制转String
* @param bArr
* @return
*/
public static String bytesToHexString(byte[] bArr) {
StringBuffer sb = new StringBuffer(bArr.length);
String sTmp;
for (int i = 0; i < bArr.length; i++) {
sTmp = Integer.toHexString(0xFF & bArr[i]);
if (sTmp.length() < 2){
sb.append(0);
}
sb.append(sTmp.toUpperCase());
}
return sb.toString();
}
/**
* hex字符串转byte数组
* @param inHex 待转换的Hex字符串
* @return 转换后的byte数组结果
*/
public static byte[] hexToByteArray(String inHex){
int hexlen = inHex.length();
byte[] result;
if (hexlen % 2 == 1){
//奇数
hexlen++;
result = new byte[(hexlen/2)];
inHex="0"+inHex;
}else {
//偶数
result = new byte[(hexlen/2)];
}
int j=0;
for (int i = 0; i < hexlen; i+=2){
result[j]=(byte)Integer.parseInt(inHex.substring(i,i+2),16);
j++;
}
return result;
}
}
二、解决重命名问题
/**
* 标签去重
* @param labelString
* @return
*/
public static String deduplicateLabel(String labelString) {
String[] labelArray = labelString.split(",");
Set<String> stringSet = new HashSet<>();
//去重
for (String str : labelArray) {
if (StringUtils.isBlank(str)) {
continue;
}
stringSet.add(str);
}
//拼接
StringBuffer deduplicatedString = new StringBuffer();
for (String str : stringSet) {
deduplicatedString.append(str).append(",");
}
return deduplicatedString.substring(0, deduplicatedString.length() - 1);
}
/**
* 取别名
* @param name 名称
* @return
*/
public static String aliasName(String name){
if(Pattern.matches(".*([1-9][0-9]{0,100000})", name)){
//拆分数据
String substring = name.substring(name.indexOf("(") + 1, name.indexOf(")"));
int number = Integer.parseInt(substring);
String[] split = name.split("(");
return split[0].concat("(").concat(String.valueOf(number+1)).concat(")");
}else{
return name.concat("(1)");
}
}
三、服务器成本计算
public class ServerCostUtils {
public static int vmCost(ServerCostVO serverCostVO) {
// 服务器按3年折旧,1GB虚拟机使用成本约为11
// 每核心cpu按0.5元计算
int cost = 0;
cost = serverCostVO.getCpu() / 2 + serverCostVO.getMemory() / 1024 * 11;
return cost;
}
public static int ecsCost(ServerCostVO serverCostVO) {
String ecsType = serverCostVO.getCpu() + "/" + serverCostVO.getMemory() / 1024;
// cpu / memory(GB) 此费用在阿里云查询得到
switch (ecsType) {
case "1/1":
return 105;
case "1/2":
return 129;
case "2/4":
return 237;
case "4/8":
return 437;
case "8/16":
return 825;
case "16/32":
return 1601;
case "32/64":
return 3153;
default:
return 0;
}
}
}
四、流式合成TTS
依赖
<dependency>
<groupId>com.alibaba.nls</groupId>
<artifactId>nls-sdk-tts</artifactId>
<version>2.1.6</version>
</dependency>
代码
import com.alibaba.nls.client.AccessToken;
import com.alibaba.nls.client.protocol.NlsClient;
import com.alibaba.nls.client.protocol.OutputFormatEnum;
import com.alibaba.nls.client.protocol.SampleRateEnum;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizer;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerListener;
import com.alibaba.nls.client.protocol.tts.SpeechSynthesizerResponse;
import com.citydo.xclouddesk.dialogflow.utils.BitUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
/**
*
* 语音合成API调用。
* 动态获取token。
* 流式合成TTS。
* 首包延迟计算。
*
*/
@Slf4j
public class SpeechUtils {
private InputStream inputStream;
private byte[] bytes;
private String appKey;
NlsClient client;
/**
* 初始化参数
* @param appKey
* @param accessKeyId
* @param accessKeySecret
* @param url
*/
public SpeechUtils(String appKey, String accessKeyId, String accessKeySecret, String url) {
this.appKey = appKey;
AccessToken accessToken = new AccessToken(accessKeyId, accessKeySecret);
try {
accessToken.apply();
if(url.isEmpty()) {
client = new NlsClient(accessToken.getToken());
}else {
client = new NlsClient(url, accessToken.getToken());
}
} catch (IOException e) {
log.error("客户端连接创建失败:{}", e);
}
}
public SpeechSynthesizerListener SpeechUtilBuffer() {
SpeechSynthesizerListener listener = null;
try {
listener = new SpeechSynthesizerListener() {
private boolean firstRecvBinary = true;
private StringBuffer buffer = new StringBuffer();
//语音合成结束
@Override
public void onComplete(SpeechSynthesizerResponse response) {
bytes = ByteConvertUtil.hexToByteArray(buffer.toString());
}
//语音合成的语音二进制数据
@Override
public void onMessage(ByteBuffer message) {
try {
if (firstRecvBinary) {
//计算首包语音流的延迟,收到第一包语音流时,即可以进行语音播放,以提升响应速度(特别是实时交互场景下)。
firstRecvBinary = false;
}
byte[] bytesArray = new byte[message.remaining()];
message.get(bytesArray, 0, bytesArray.length);
buffer.append(ByteConvertUtil.bytesToHexString(bytesArray));
} catch (Exception e) {
log.error("语音合成流失败:{}", e);
}
}
@Override
public void onFail(SpeechSynthesizerResponse response) {
}
};
} catch (Exception e) {
log.error("TTS调用失败:{}",e);
}
return listener;
}
public SpeechSynthesizerListener SpeechUtilStream() {
SpeechSynthesizerListener listener = null;
try {
listener = new SpeechSynthesizerListener() {
private boolean firstRecvBinary = true;
private ByteArrayOutputStream buf = new ByteArrayOutputStream();
//语音合成结束
@Override
public void onComplete(SpeechSynthesizerResponse response) {
inputStream = new ByteArrayInputStream(buf.toByteArray());
}
//语音合成的语音二进制数据
@Override
public void onMessage(ByteBuffer message) {
try {
if (firstRecvBinary) {
//计算首包语音流的延迟,收到第一包语音流时,即可以进行语音播放,以提升响应速度(特别是实时交互场景下)。
firstRecvBinary = false;
}
byte[] bytesArray = new byte[message.remaining()];
message.get(bytesArray, 0, bytesArray.length);
buf.write(bytesArray, 0, bytesArray.length);
buf.flush();
} catch (Exception e) {
log.error("语音合成流失败:{}", e);
}
}
@Override
public void onFail(SpeechSynthesizerResponse response) {
}
};
} catch (Exception e) {
log.error("TTS调用失败:{}",e);
}
return listener;
}
/**
* 初始化参数
* @param text
* @param voice
* @param volume
* @param speechRate
* @param pitchRate
*/
public void process(String text, String voice, Integer volume, Integer speechRate, Integer pitchRate, boolean isType) {
SpeechSynthesizer synthesizer = null;
try {
//创建实例,建立连接。
if(isType){
synthesizer = new SpeechSynthesizer(client, SpeechUtilBuffer());
}else {
synthesizer = new SpeechSynthesizer(client, SpeechUtilStream());
}
synthesizer.setAppKey(appKey);
//设置返回音频的编码格式
synthesizer.setFormat(OutputFormatEnum.WAV);
//设置返回音频的采样率
synthesizer.setSampleRate(SampleRateEnum.SAMPLE_RATE_16K);
//发音人
synthesizer.setVoice(StringUtils.isEmpty(voice) ? "xiaoyun" :voice);
//语调,范围是-500~500,可选,默认是0。
synthesizer.setPitchRate(StringUtils.isEmpty(pitchRate) ? 0 : pitchRate);
//语速,范围是-500~500,默认是0。
synthesizer.setSpeechRate(StringUtils.isEmpty(speechRate) ? 0 : speechRate);
//音量
synthesizer.setVolume(StringUtils.isEmpty(volume) ? 50 : volume);
//设置用于语音合成的文本
synthesizer.setText(text);
// 是否开启字幕功能(返回相应文本的时间戳),默认不开启,需要注意并非所有发音人都支持该参数。
synthesizer.addCustomedParam("enable_subtitle", false);
synthesizer.start();
synthesizer.waitForComplete();
} catch (Exception e) {
log.error("TTS合成语音失败:{}",e);
} finally {
//关闭连接
if (null != synthesizer) {
synthesizer.close();
}
}
}
public void shutdown() {
client.shutdown();
}
public void setAppKey(String appKey) {
this.appKey = appKey;
}
public String getAppKey() {
return appKey;
}
public byte[] getBytes() {
return bytes;
}
public void setBytes(byte[] bytes) {
this.bytes = bytes;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
}