一. 导入依赖坐标
<!-- gson -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.6</version>
</dependency>
<!-- websocket -->
<dependency>
<groupId>org.java-websocket</groupId>
<artifactId>Java-WebSocket</artifactId>
<version>1.3.6</version>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.5</version>
</dependency>
二. 添加实体
package com.test.util.audioUtil;
public class AudioEntry {
// 文件名称(服务器上的物理路径)
private String fileName;
// 文件来源 如:概况...
private String source;
// 所属地区 如:湖北省、武汉市等
private String fileArea;
// 文件生成的日期
private String fileDate;
// 状态 是否已经完全生成
private boolean isFinish;
public String getFileName() {
return fileName;
}
public String getSource() {
return source;
}
public String getFileArea() {
return fileArea;
}
public String getFileDate() {
return fileDate;
}
public boolean isFinish() {
return isFinish;
}
public void setFileName(String fileName) {
this.fileName = fileName;
}
public void setSource(String source) {
this.source = source;
}
public void setFileArea(String fileArea) {
this.fileArea = fileArea;
}
public void setFileDate(String fileDate) {
this.fileDate = fileDate;
}
public void setFinish(boolean finish) {
isFinish = finish;
}
}
三. 编写生成语音核心代码
package com.test.util.audioUtil;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import org.apache.commons.codec.binary.Base64;
import org.apache.http.client.utils.URIBuilder;
import org.java_websocket.client.WebSocketClient;
import org.java_websocket.handshake.ServerHandshake;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
public class AudioSocket {
public static Gson json = new Gson();
// 每日生成的音频文件
public static List<AudioEntry> audioFileDataList;
// 每次生成的音频文件
public static AudioEntry audioItem;
//获取今天的日期
public static String strDate = new SimpleDateFormat("yyyy-MM-dd ").format(new Date());
public static WebSocketClient client;
// 文件
public static File file;
// 文件操作
public static FileOutputStream os;
// 每次合成后的音频文件
public static List<String> audioList;
// 每次合成后的音频文件的数量
public static int audioListLength;
// 需要合成音频文件的信息
public static JsonObject frame;
// 音频文件是否已经合成完成
public static boolean isAudioFinish = false;
public static String source;
public static String fileArea;
public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
URL url = new URL(hostUrl);
SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("GMT"));
String date = format.format(new Date());
StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").
append("date: ").append(date).append("\n").
append("GET ").append(url.getPath()).append(" HTTP/1.1");
Charset charset = Charset.forName("UTF-8");
Mac mac = Mac.getInstance("hmacsha256");
SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
mac.init(spec);
byte[] hexDigits = mac.doFinal(builder.toString().getBytes(charset));
String sha = Base64.encodeBase64String(hexDigits);
String authorization = String.format("hmac username=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
String para1 = Base64.encodeBase64String(authorization.getBytes(charset));
String para2 = date;
String para3 = url.getHost();
URIBuilder urlBuilder = new URIBuilder("https://" + url.getHost() + url.getPath());
urlBuilder.setParameter("authorization", para1);
urlBuilder.setParameter("date", para2);
urlBuilder.setParameter("host", para3);
URI uri = urlBuilder.build();
return uri.toString();
}
// 获取音频文件
public static String getAudioFile(String audioText, String apiUrl, String apiKey, String apiSecret, String appId, String pdfAudioSavePath) throws Exception {
// 构建鉴权url
String authUrl = getAuthUrl(apiUrl, apiKey, apiSecret);
// 将url中的 schema http://和https://分别替换为ws:// 和 wss://
String url = authUrl.toString().replace("http://", "ws://").replace("https://", "wss://");
// 存放音频的文件
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
String date = sdf.format(new Date());
File file = new File(pdfAudioSavePath);
if (!file.exists()) {
file.mkdirs();
}
String exportPath = pdfAudioSavePath + File.separator + date + ".mp3";
AudioSocket.file = new File(exportPath);//保存为MP3文件
if (!AudioSocket.file.exists()) {
AudioSocket.file.createNewFile();
}
os = new FileOutputStream(AudioSocket.file);
// 发送数据
frame = new JsonObject();
JsonObject business = new JsonObject();
JsonObject common = new JsonObject();
JsonObject data = new JsonObject();
// 填充common
common.addProperty("app_id", appId);
// 填充business
business.addProperty("aue", "lame");//aue参数为lame时,保存文件格式为mp3文件
business.addProperty("sfl", 1);//当设置保存为mp3文件时,还需将sfl设置为1
business.addProperty("tte", "UTF8");//小语种必须使用UNICODE编码
business.addProperty("vcn", "xiaoyan");//到控制台-我的应用-语音合成-添加试用或购买发音人,添加后即显示该发音人参数值,若试用未添加的发音人会报错11200
business.addProperty("pitch", 50);
business.addProperty("speed", 25);
// 填充data
data.addProperty("status", 2);//固定位2
try {
data.addProperty("text", Base64.encodeBase64String(audioText.getBytes("utf8")));
} catch (Exception e) {
e.printStackTrace();
}
//填充frame
frame.add("common", common);
frame.add("business", business);
frame.add("data", data);
try {
client = new WebSocketClient(new URI(url)) {
@Override
public void onOpen(ServerHandshake serverHandshake) {
client.send(frame.toString());
}
@Override
public void onMessage(String msg) {
// 处理返回数据
AudioSocket.ResponseData resp = null;
try {
resp = json.fromJson(msg, ResponseData.class);
} catch (Exception e) {
e.printStackTrace();
}
if (resp != null) {
if (resp.getCode() != 0) {
System.out.println("error=>" + resp.getMessage() + " sid=" + resp.getSid());
return;
}
if (resp.getData() != null) {
String result = resp.getData().audio;
byte[] audio = Base64.decodeBase64(result);
try {
os.write(audio);
os.flush();
} catch (IOException e) {
e.printStackTrace();
}
if (resp.getData().status == 2) {
audioList.add(AudioSocket.file.getPath());
try {
os.close();
if (audioListLength == audioList.size()) {
String mergaFile = mergeAudioFile(audioList);
// audioList里面只存放合成后的文件名
if (mergaFile != null && !mergaFile.equals("")) {
isAudioFinish = true;
audioList.clear();
audioList.add(mergaFile);
audioItem = new AudioEntry();
audioItem.setSource(source);
audioItem.setFileArea(fileArea);
audioItem.setFileDate(strDate);
audioItem.setFileName(mergaFile);
audioItem.setFinish(true);
audioFileDataList.add(audioItem);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
@Override
public void onClose(int i, String s, boolean b) {
//logger.info("链接已关闭");
}
@Override
public void onError(Exception e) {
e.printStackTrace();
//logger.info("发生错误已关闭");
}
};
} catch (Exception e) {
e.printStackTrace();
}
client.connect();
return exportPath;
}
// 合并多个mp3文件
public static String mergeAudioFile(List<String> audioFile) throws Exception {
if (audioFile.size() > 1) {
//取第一个文件
File filefin = new File(audioFile.get(0));
FileOutputStream fileOut = new FileOutputStream(filefin, true);
File fileRead;
FileInputStream fileInput;
byte b[] = new byte[1024];
int len;
for (int i = 1; i < audioFile.size(); i++) {
fileRead = new File(audioFile.get(i));
fileInput = new FileInputStream(fileRead);
while ((len = fileInput.read(b)) != -1) {
for (int j = 0; j < len; j++) {
fileOut.write(b[j]);
}
}
fileInput.close();
//删除合并之前的文件,
fileRead.delete();
}
fileOut.close();
return audioFile.get(0);
}
return audioFile.get(0);
}
public static class ResponseData {
private int code;
private String message;
private String sid;
private Data data;
public int getCode() {
return code;
}
public String getMessage() {
return this.message;
}
public String getSid() {
return sid;
}
public Data getData() {
return data;
}
}
public static class Data {
private int status; // 标志音频是否返回结束 status=1,表示后续还有音频返回,status=2表示所有的音频已经返回
private String audio; // 返回的音频,base64 编码
private String ced; // 合成进度
}
}
四. 编写调用生成语音入口方法
package com.test.util.audioUtil;
import java.util.ArrayList;
public class CreateAudio {
private static final String apiUrl = "";
private static final String apiKey = "";
private static final String apiSecret = "";
private static final String appId = "";
private static final String savePath = "D:/audio"; // 生成的语音保存地址
public static String createAudio(String content) {
AudioSocket.audioList = new ArrayList<>();
AudioSocket.isAudioFinish = false;
AudioSocket.fileArea = "武汉市";
AudioSocket.source = "语音来源";
if (AudioSocket.audioFileDataList == null) {
AudioSocket.audioFileDataList = new ArrayList<>();
}
String exportPath = "未知地址";
// 请求讯飞在线API,合成音频文件
try {
// 获取需要合成音频文件的文本
String audioTextSub;
AudioSocket.audioListLength = (int) Math.ceil(((double) content.length()) / 2000);//获取音频文件的数量
// 若需要合成语音的文本数据大于2000个字符
if (content.length() > 2000) {
// 每2000个字符进行截取
for (int i = 0; i < AudioSocket.audioListLength; i++) {
// 截取到最后
if (i == AudioSocket.audioListLength - 1) {
audioTextSub = content.substring(i * 2000) + "。";
} else {
audioTextSub = content.substring(i * 2000, (i + 1) * 2000) + "。";
}
exportPath = AudioSocket.getAudioFile(audioTextSub, apiUrl, apiKey, apiSecret, appId, savePath);
Thread.sleep(25000); // socket回调方法,延时20秒
}
} else {
exportPath = AudioSocket.getAudioFile(content, apiUrl, apiKey, apiSecret, appId, savePath);
}
} catch (Exception ex) {
ex.printStackTrace();
}
return exportPath;
}
}