当前位置:   article > 正文

科大讯飞TTS接口调用保存为mp3格式_tts转mp3

tts转mp3

不废话,直接上code

  1. package com.iflytek.voicecloud.webapi.demo;
  2. import com.google.gson.Gson;
  3. import com.google.gson.JsonObject;
  4. import okhttp3.*;
  5. import okio.ByteString;
  6. import javax.crypto.Mac;
  7. import javax.crypto.spec.SecretKeySpec;
  8. import java.io.File;
  9. import java.io.FileInputStream;
  10. import java.io.FileOutputStream;
  11. import java.io.IOException;
  12. import java.net.URL;
  13. import java.nio.charset.Charset;
  14. import java.text.SimpleDateFormat;
  15. import java.util.Base64;
  16. import java.util.Date;
  17. import java.util.Locale;
  18. import java.util.TimeZone;
  19. import java.io.ByteArrayOutputStream;
  20. import java.io.IOException;
  21. public class WebTTSWS {
  22. private static final String hostUrl = "https://tts-api.xfyun.cn/v2/tts"; //http url 不支持解析 ws/wss schema
  23. private static final String appid = "";//到控制台-语音合成页面获取
  24. private static final String apiSecret = "";//到控制台-语音合成页面获取
  25. private static final String apiKey = "";//到控制台-语音合成页面获取
  26. private static final String text = "在全系5款车型中,上市专享版的性价比最为突出,在有限的价格下提供了大多数实用配置,而且是唯一标配车内氛围灯的车型,不失为价值之选。时尚致雅型和时尚动感型居于中配角色,配置方面较低配车型更加全面,面子和里子都比较到位,符合人们对豪华品牌的期待,同样值得考虑。";
  27. public static final Gson json = new Gson();
  28. public static class WaveHeader {
  29. public final char fileID[] = { 'R', 'I', 'F', 'F' };
  30. public int fileLength;
  31. public char wavTag[] = { 'W', 'A', 'V', 'E' };;
  32. public char FmtHdrID[] = { 'f', 'm', 't', ' ' };
  33. public int FmtHdrLeth;
  34. public short FormatTag;
  35. public short Channels;
  36. public int SamplesPerSec;
  37. public int AvgBytesPerSec;
  38. public short BlockAlign;
  39. public short BitsPerSample;
  40. public char DataHdrID[] = { 'd', 'a', 't', 'a' };
  41. public int DataHdrLeth;
  42. public byte[] getHeader() throws IOException {
  43. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  44. writeChar(bos, fileID);
  45. writeInt(bos, fileLength);
  46. writeChar(bos, wavTag);
  47. writeChar(bos, FmtHdrID);
  48. writeInt(bos, FmtHdrLeth);
  49. writeShort(bos, FormatTag);
  50. writeShort(bos, Channels);
  51. writeInt(bos, SamplesPerSec);
  52. writeInt(bos, AvgBytesPerSec);
  53. writeShort(bos, BlockAlign);
  54. writeShort(bos, BitsPerSample);
  55. writeChar(bos, DataHdrID);
  56. writeInt(bos, DataHdrLeth);
  57. bos.flush();
  58. byte[] r = bos.toByteArray();
  59. bos.close();
  60. return r;
  61. }
  62. private void writeShort(ByteArrayOutputStream bos, int s) throws IOException {
  63. byte[] mybyte = new byte[2];
  64. mybyte[1] = (byte) ((s << 16) >> 24);
  65. mybyte[0] = (byte) ((s << 24) >> 24);
  66. bos.write(mybyte);
  67. }
  68. private void writeInt(ByteArrayOutputStream bos, int n) throws IOException {
  69. byte[] buf = new byte[4];
  70. buf[3] = (byte) (n >> 24);
  71. buf[2] = (byte) ((n << 8) >> 24);
  72. buf[1] = (byte) ((n << 16) >> 24);
  73. buf[0] = (byte) ((n << 24) >> 24);
  74. bos.write(buf);
  75. }
  76. private void writeChar(ByteArrayOutputStream bos, char[] id) {
  77. for (int i = 0; i < id.length; i++) {
  78. char c = id[i];
  79. bos.write(c);
  80. }
  81. }
  82. }
  83. public static void convertAudioFiles(String src, String target) throws Exception {
  84. FileInputStream fis = new FileInputStream(src);
  85. FileOutputStream fos = new FileOutputStream(target);
  86. //计算长度
  87. byte[] buf = new byte[1024 * 4];
  88. int size = fis.read(buf);
  89. int PCMSize = 0;
  90. while (size != -1) {
  91. PCMSize += size;
  92. size = fis.read(buf);
  93. }
  94. fis.close();
  95. //填入参数,比特率等等。这里用的是16位单声道 8000 hz
  96. WaveHeader header = new WaveHeader();
  97. //长度字段 = 内容的大小(PCMSize) + 头部字段的大小(不包括前面4字节的标识符RIFF以及fileLength本身的4字节)
  98. header.fileLength = PCMSize + (44 - 8);
  99. header.FmtHdrLeth = 16;
  100. header.BitsPerSample = 16;
  101. header.Channels = 1;
  102. header.FormatTag = 0x0001;
  103. header.SamplesPerSec = 14500;
  104. header.BlockAlign = (short)(header.Channels * header.BitsPerSample / 8);
  105. header.AvgBytesPerSec = header.BlockAlign * header.SamplesPerSec;
  106. header.DataHdrLeth = PCMSize;
  107. byte[] h = header.getHeader();
  108. assert h.length == 44; //WAV标准,头部应该是44字节
  109. //write header
  110. fos.write(h, 0, h.length);
  111. //write data stream
  112. fis = new FileInputStream(src);
  113. size = fis.read(buf);
  114. while (size != -1) {
  115. fos.write(buf, 0, size);
  116. size = fis.read(buf);
  117. }
  118. fis.close();
  119. fos.close();
  120. System.out.println("Convert OK!");
  121. }
  122. public static void main(String[] args) throws Exception {
  123. // 构建鉴权url
  124. String authUrl = getAuthUrl(hostUrl, apiKey, apiSecret);
  125. OkHttpClient client = new OkHttpClient.Builder().build();
  126. //将url中的 schema http://和https://分别替换为ws:// 和 wss://
  127. String url = authUrl.toString().replace("http://", "ws://").replace("https://", "wss://");
  128. Request request = new Request.Builder().url(url).build();
  129. // 存放音频的文件
  130. SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
  131. String date = sdf.format(new Date());
  132. File f = new File("resource/tts/" + date + ".pcm");
  133. if (!f.exists()) {
  134. f.createNewFile();
  135. }
  136. FileOutputStream os = new FileOutputStream(f);
  137. WebSocket webSocket = client.newWebSocket(request, new WebSocketListener() {
  138. @Override
  139. public void onOpen(WebSocket webSocket, Response response) {
  140. super.onOpen(webSocket, response);
  141. try {
  142. System.out.println(response.body().string());
  143. } catch (IOException e) {
  144. e.printStackTrace();
  145. }
  146. //发送数据
  147. JsonObject frame = new JsonObject();
  148. JsonObject business = new JsonObject();
  149. JsonObject common = new JsonObject();
  150. JsonObject data = new JsonObject();
  151. // 填充common
  152. common.addProperty("app_id", appid);
  153. //填充business
  154. business.addProperty("aue", "raw");
  155. business.addProperty("tte", "UTF8");
  156. business.addProperty("ent", "intp65");
  157. business.addProperty("vcn", "x_yifeng");//到控制台-我的应用-语音合成-添加试用或购买发音人,添加后即显示该发音人参数值,若试用未添加的发音人会报错11200
  158. business.addProperty("pitch", 50);
  159. business.addProperty("speed", 60);
  160. //填充data
  161. data.addProperty("status", 2);//固定位2
  162. data.addProperty("text", Base64.getEncoder().encodeToString(text.getBytes()));
  163. data.addProperty("encoding", "");
  164. //填充frame
  165. frame.add("common", common);
  166. frame.add("business", business);
  167. frame.add("data", data);
  168. webSocket.send(frame.toString());
  169. }
  170. @Override
  171. public void onMessage(WebSocket webSocket, String text) {
  172. super.onMessage(webSocket, text);
  173. //处理返回数据
  174. System.out.println("receive=>" + text);
  175. ResponseData resp = null;
  176. try {
  177. resp = json.fromJson(text, ResponseData.class);
  178. } catch (Exception e) {
  179. e.printStackTrace();
  180. }
  181. if (resp != null) {
  182. if (resp.getCode() != 0) {
  183. System.out.println("error=>" + resp.getMessage() + " sid=" + resp.getSid());
  184. return;
  185. }
  186. if (resp.getData() != null) {
  187. String result = resp.getData().audio;
  188. byte[] audio = Base64.getDecoder().decode(result);
  189. try {
  190. os.write(audio);
  191. os.flush();
  192. } catch (IOException e) {
  193. e.printStackTrace();
  194. }
  195. if (resp.getData().status == 2) {
  196. // todo resp.data.status ==2 说明数据全部返回完毕,可以关闭连接,释放资源
  197. System.out.println("session end ");
  198. System.out.println("合成的音频文件保存在:" + f.getPath());
  199. webSocket.close(1000, "");
  200. try {
  201. convertAudioFiles(f.getPath(),f.getPath()+".mp3");
  202. } catch (Exception e1) {
  203. // TODO Auto-generated catch block
  204. e1.printStackTrace();
  205. }
  206. try {
  207. os.close();
  208. } catch (IOException e) {
  209. e.printStackTrace();
  210. }
  211. }
  212. }
  213. }
  214. }
  215. @Override
  216. public void onMessage(WebSocket webSocket, ByteString bytes) {
  217. super.onMessage(webSocket, bytes);
  218. }
  219. @Override
  220. public void onClosing(WebSocket webSocket, int code, String reason) {
  221. super.onClosing(webSocket, code, reason);
  222. System.out.println("socket closing");
  223. }
  224. @Override
  225. public void onClosed(WebSocket webSocket, int code, String reason) {
  226. super.onClosed(webSocket, code, reason);
  227. System.out.println("socket closed");
  228. }
  229. @Override
  230. public void onFailure(WebSocket webSocket, Throwable t, Response response) {
  231. super.onFailure(webSocket, t, response);
  232. System.out.println("connection failed");
  233. }
  234. });
  235. }
  236. public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
  237. URL url = new URL(hostUrl);
  238. SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
  239. format.setTimeZone(TimeZone.getTimeZone("GMT"));
  240. String date = format.format(new Date());
  241. StringBuilder builder = new StringBuilder("host: ").append(url.getHost()).append("\n").//
  242. append("date: ").append(date).append("\n").//
  243. append("GET ").append(url.getPath()).append(" HTTP/1.1");
  244. Charset charset = Charset.forName("UTF-8");
  245. Mac mac = Mac.getInstance("hmacsha256");
  246. SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(charset), "hmacsha256");
  247. mac.init(spec);
  248. byte[] hexDigits = mac.doFinal(builder.toString().getBytes(charset));
  249. String sha = Base64.getEncoder().encodeToString(hexDigits);
  250. String authorization = String.format("hmac username=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
  251. HttpUrl httpUrl = HttpUrl.parse("https://" + url.getHost() + url.getPath()).newBuilder().//
  252. addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(charset))).//
  253. addQueryParameter("date", date).//
  254. addQueryParameter("host", url.getHost()).//
  255. build();
  256. return httpUrl.toString();
  257. }
  258. public static class ResponseData {
  259. private int code;
  260. private String message;
  261. private String sid;
  262. private Data data;
  263. public int getCode() {
  264. return code;
  265. }
  266. public String getMessage() {
  267. return this.message;
  268. }
  269. public String getSid() {
  270. return sid;
  271. }
  272. public Data getData() {
  273. return data;
  274. }
  275. }
  276. public static class Data {
  277. private int status; //标志音频是否返回结束 status=1,表示后续还有音频返回,status=2表示所有的音频已经返回
  278. private String audio; //返回的音频,base64 编码
  279. private String ced; // 合成进度
  280. }
  281. }

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家小花儿/article/detail/77580
推荐阅读
相关标签
  

闽ICP备14008679号