也许,有些时候,你需要这个需求呢,来上代码
我会写出两种不同方式的文字转语音demo,直接copy走用,节省开发时间
git项目下载地址
1.直接使用jdk的 jacob,效果还不错,特点:免费的 2.百度文字转语音,特点:大厂的API肯定好用,功能不少,这个如果是企业开发,推荐使用,需要收费的,可以直接申请账号注册,有appid 等参数,配置好直接用
项目demo,需要的去git下载吧
需要下载jacob-1.18,网上一堆
项目中引入pom文件
<!-- /artifact/com.jacob/jacob 文字转语音 --><dependency><groupId>com.hynnet</groupId><artifactId>jacob</artifactId><version>1.18</version></dependency>
1.java合成方法
package com.jourwon.httpclient.util;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.InputStream;import com.jacob.activeX.ActiveXComponent;import .Dispatch;import .Variant;public class Test {/*** 语音转文字并播放* * @param txt*/public static void textToSpeech(String text) {ActiveXComponent ax = null;try {ax = new ActiveXComponent("Sapi.SpVoice");// 运行时输出语音内容Dispatch spVoice = ax.getObject();// 音量 0-100ax.setProperty("Volume", new Variant(100));// 语音朗读速度 -10 到 +10ax.setProperty("Rate", new Variant(-2));// 执行朗读Dispatch.call(spVoice, "Speak", new Variant(text));// 下面是构建文件流把生成语音文件ax = new ActiveXComponent("Sapi.SpFileStream");Dispatch spFileStream = ax.getObject();ax = new ActiveXComponent("Sapi.SpAudioFormat");Dispatch spAudioFormat = ax.getObject();// 设置音频流格式Dispatch.put(spAudioFormat, "Type", new Variant(22));// 设置文件输出流格式Dispatch.putRef(spFileStream, "Format", spAudioFormat);// 调用输出 文件流打开方法,创建一个.wav文件Dispatch.call(spFileStream, "Open", new Variant("./text.mp3"), new Variant(3), new Variant(true));// 设置声音对象的音频输出流为输出文件对象Dispatch.putRef(spVoice, "AudioOutputStream", spFileStream);// 设置音量 0到100Dispatch.put(spVoice, "Volume", new Variant(100));// 设置朗读速度Dispatch.put(spVoice, "Rate", new Variant(-2));// 开始朗读Dispatch.call(spVoice, "Speak", new Variant(text));// 关闭输出文件Dispatch.call(spFileStream, "Close");Dispatch.putRef(spVoice, "AudioOutputStream", null);spAudioFormat.safeRelease();spFileStream.safeRelease();spVoice.safeRelease();ax.safeRelease();} catch (Exception e) {e.printStackTrace();}}public static void main(String[] args) {String s = "当月亮和太阳处于地球两侧,并且月亮和太阳的黄经相差180度时,从地球上看,此时的月亮最圆,称之为“满月”";textToSpeech(s);File file = new File("result.mp3"); // 打开mp3文件即可播放String fileName = "Operator.doc".toString(); // 文件的默认保存名// 读到流中try {InputStream inStream = new FileInputStream("c:/Operator.doc");} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} // 文件的存放路径}}
2.百度语音合成方法
package com.jourwon.httpclient.util;import java.io.File;import java.io.FileOutputStream;import .HttpURLConnection;import .URL;public class TtsMain {public static void main(String[] args) throws Exception {(new TtsMain()).run();}// 填写申请百度语音申请的appkey 申请地址:百度AI开放平台private final String appKey = "自己申请";// 填写申请百度语音申请的APP SECRETprivate final String secretKey = "自己申请";// text 的内容为"欢迎使用百度语音合成"的urlencode,utf-8 编码private final String text = "百度百科是百度公司推出的一部内容开放、自由的网络百科全书。";// 发音人选择, 0为普通女声,1为普通男生,3为情感合成-度逍遥,4为情感合成-度丫丫,默认为普通女声private final int per = 0;// 语速,取值0-9,默认为5中语速private final int spd = 5;// 音调,取值0-9,默认为5中语调private final int pit = 5;// 音量,取值0-9,默认为5中音量private final int vol = 5;// 调用地址public final String url = "/text2audio";// 用户唯一标识,用来区分用户,填写机器 MAC 地址或 IMEI 码,长度为60以内private String cuid = "0322java";private void run() throws Exception {TokenHolder holder = new TokenHolder(appKey, secretKey, TokenHolder.ASR_SCOPE);holder.resfresh();String token = holder.getToken();String url2 = url + "?tex=" + ConnUtil.urlEncode(text);url2 += "&per=" + per;url2 += "&spd=" + spd;url2 += "&pit=" + pit;url2 += "&vol=" + vol;url2 += "&cuid=" + cuid;url2 += "&tok=" + token;url2 += "&lan=zh&ctp=1";HttpURLConnection conn = (HttpURLConnection) new URL(url2).openConnection();conn.setConnectTimeout(5000);String contentType = conn.getContentType();if (contentType.contains("mp3")) {byte[] bytes = ConnUtil.getResponseBytes(conn);// 存在项目根目录下,去文件夹点击可以播放File file = new File("result.mp3");FileOutputStream os = new FileOutputStream(file);os.write(bytes);os.close();System.out.println("mp3 file write to " + file.getAbsolutePath());} else {System.err.println("ERROR: content-type= " + contentType);String res = ConnUtil.getResponseString(conn);System.err.println(res);}}}
//工具类
package com.jourwon.httpclient.util;import org.json.JSONObject;import java.io.IOException;import .HttpURLConnection;import .URL;/*** token的获取类 将apiKey和secretKey换取token,注意有效期保存在expiresAt*/public class TokenHolder {public static final String ASR_SCOPE = "audio_voice_assistant_get";public static final String TTS_SCOPE = "audio_tts_post";/*** url , Token的url,http可以改为https*/private static final String url = "/oauth/2.0/token";/*** asr的权限 scope 是 "audio_voice_assistant_get" tts 的权限 scope 是* "audio_tts_post"*/private String scope;/*** 网页上申请语音识别应用获取的apiKey*/private String apiKey;/*** 网页上申请语音识别应用获取的secretKey*/private String secretKey;/*** 保存访问接口获取的token*/private String token;/*** 当前的时间戳,毫秒*/private long expiresAt;/*** @param apiKey* 网页上申请语音识别应用获取的apiKey* @param secretKey* 网页上申请语音识别应用获取的secretKey*/public TokenHolder(String apiKey, String secretKey, String scope) {this.apiKey = apiKey;this.secretKey = secretKey;this.scope = scope;}/*** 获取token,refresh 方法后调用有效** @return*/public String getToken() {return token;}/*** 获取过期时间,refresh 方法后调用有效** @return*/public long getExpiresAt() {return expiresAt;}/*** 获取token** @return* @throws IOException* http请求错误* @throws DemoException* http接口返回不是 200, access_token未获取*/public void resfresh() throws Exception {String getTokenURL = url + "?grant_type=client_credentials" + "&client_id=" + ConnUtil.urlEncode(apiKey)+ "&client_secret=" + ConnUtil.urlEncode(secretKey);// 打印的url出来放到浏览器内可以复现System.out.println("token url:" + getTokenURL);URL url = new URL(getTokenURL);HttpURLConnection conn = (HttpURLConnection) url.openConnection();conn.setConnectTimeout(5000);String result = ConnUtil.getResponseString(conn);System.out.println("Token result json:" + result);parseJson(result);}/*** @param result* token接口获得的result* @throws DemoException*/private void parseJson(String result) throws Exception {JSONObject json = new JSONObject(result);if (!json.has("access_token")) {// 返回没有access_token字段throw new DemoException("access_token not obtained, " + result);}if (!json.has("scope")) {// 返回没有scope字段throw new DemoException("scopenot obtained, " + result);}if (!json.getString("scope").contains(scope)) {throw new DemoException("scope not exist, " + scope + "," + result);}token = json.getString("access_token");expiresAt = System.currentTimeMillis() + json.getLong("expires_in") * 1000;}}
//异常处理
package com.jourwon.httpclient.util;public class DemoException extends Exception {public DemoException(String message) {super(message);}}
//util相关类
package com.jourwon.httpclient.util;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.io.UnsupportedEncodingException;import .HttpURLConnection;import .URLEncoder;/*** 与连接相关的Util类*/public class ConnUtil {/*** UrlEncode, UTF-8 编码** @param str* 原始字符串* @return*/public static String urlEncode(String str) {String result = null;try {result = URLEncoder.encode(str, "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}return result;}/*** 从HttpURLConnection 获取返回的字符串** @param conn* @return* @throws IOException* @throws DemoException*/public static String getResponseString(HttpURLConnection conn) throws IOException, DemoException {return new String(getResponseBytes(conn));}/*** 从HttpURLConnection 获取返回的bytes 注意 HttpURLConnection自身问题,* 400类错误,会直接抛出异常。不能获取conn.getInputStream();** @param conn* @return* @throws IOException* http请求错误* @throws DemoException* http 的状态码不是 200*/public static byte[] getResponseBytes(HttpURLConnection conn) throws IOException, DemoException {int responseCode = conn.getResponseCode();if (responseCode != 200) {System.err.println("http 请求返回的状态码错误,期望200, 当前是 " + responseCode);if (responseCode == 401) {System.err.println("可能是appkey appSecret 填错");}throw new DemoException("http response code is" + responseCode);}InputStream inputStream = conn.getInputStream();byte[] result = getInputStreamContent(inputStream);return result;}/*** 将InputStream内的内容全部读取,作为bytes返回** @param is* @return* @throws IOException* @see InputStream.read()*/public static byte[] getInputStreamContent(InputStream is) throws IOException {byte[] b = new byte[1024];// 定义一个输出流存储接收到的数据ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();// 开始接收数据int len = 0;while (true) {len = is.read(b);if (len == -1) {// 数据读完break;}byteArrayOutputStream.write(b, 0, len);}return byteArrayOutputStream.toByteArray();}}
//http工具类
package com.jourwon.httpclient.util;import java.io.IOException;import java.io.UnsupportedEncodingException;import java.util.ArrayList;import java.util.HashMap;import java.util.List;import java.util.Map;import java.util.Map.Entry;import java.util.Set;import org.apache.http.HttpStatus;import org.apache.http.NameValuePair;import org.apache.http.client.config.RequestConfig;import org.apache.http.client.entity.UrlEncodedFormEntity;import org.apache.http.client.methods.CloseableHttpResponse;import org.apache.http.client.methods.HttpDelete;import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.client.methods.HttpPut;import org.apache.http.client.methods.HttpRequestBase;import org.apache.http.client.utils.URIBuilder;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.message.BasicNameValuePair;import org.apache.http.util.EntityUtils;import com.jourwon.httpclient.pojo.HttpClientResult;/*** Description: httpClient工具类* * @author JourWon* @date Created on 4月19日*/public class HttpClientUtils {// 编码格式。发送编码格式统一用UTF-8private static final String ENCODING = "UTF-8";// 设置连接超时时间,单位毫秒。private static final int CONNECT_TIMEOUT = 6000;// 请求获取数据的超时时间(即响应时间),单位毫秒。private static final int SOCKET_TIMEOUT = 6000;/*** 发送get请求;不带请求头和请求参数* * @param url 请求地址* @return* @throws Exception*/public static HttpClientResult doGet(String url) throws Exception {return doGet(url, null, null);}/*** 发送get请求;带请求参数* * @param url 请求地址* @param params 请求参数集合* @return* @throws Exception*/public static HttpClientResult doGet(String url, Map<String, String> params) throws Exception {return doGet(url, null, params);}/*** 发送get请求;带请求头和请求参数* * @param url 请求地址* @param headers 请求头集合* @param params 请求参数集合* @return* @throws Exception*/public static HttpClientResult doGet(String url, Map<String, String> headers, Map<String, String> params) throws Exception {// 创建httpClient对象CloseableHttpClient httpClient = HttpClients.createDefault();// 创建访问的地址URIBuilder uriBuilder = new URIBuilder(url);if (params != null) {Set<Entry<String, String>> entrySet = params.entrySet();for (Entry<String, String> entry : entrySet) {uriBuilder.setParameter(entry.getKey(), entry.getValue());}}// 创建http对象HttpGet httpGet = new HttpGet(uriBuilder.build());/*** setConnectTimeout:设置连接超时时间,单位毫秒。* setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection* 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。* setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。*/RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpGet.setConfig(requestConfig);// 设置请求头packageHeader(headers, httpGet);// 创建httpResponse对象CloseableHttpResponse httpResponse = null;try {// 执行请求并获得响应结果return getHttpClientResult(httpResponse, httpClient, httpGet);} finally {// 释放资源release(httpResponse, httpClient);}}/*** 发送post请求;不带请求头和请求参数* * @param url 请求地址* @return* @throws Exception*/public static HttpClientResult doPost(String url) throws Exception {return doPost(url, null, null);}/*** 发送post请求;带请求参数* * @param url 请求地址* @param params 参数集合* @return* @throws Exception*/public static HttpClientResult doPost(String url, Map<String, String> params) throws Exception {return doPost(url, null, params);}/*** 发送post请求;带请求头和请求参数* * @param url 请求地址* @param headers 请求头集合* @param params 请求参数集合* @return* @throws Exception*/public static HttpClientResult doPost(String url, Map<String, String> headers, Map<String, String> params) throws Exception {// 创建httpClient对象CloseableHttpClient httpClient = HttpClients.createDefault();// 创建http对象HttpPost httpPost = new HttpPost(url);/*** setConnectTimeout:设置连接超时时间,单位毫秒。* setConnectionRequestTimeout:设置从connect Manager(连接池)获取Connection* 超时时间,单位毫秒。这个属性是新加的属性,因为目前版本是可以共享连接池的。* setSocketTimeout:请求获取数据的超时时间(即响应时间),单位毫秒。 如果访问一个接口,多少时间内无法返回数据,就直接放弃此次调用。*/RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpPost.setConfig(requestConfig);// 设置请求头/*httpPost.setHeader("Cookie", "");httpPost.setHeader("Connection", "keep-alive");httpPost.setHeader("Accept", "application/json");httpPost.setHeader("Accept-Language", "zh-CN,zh;q=0.9");httpPost.setHeader("Accept-Encoding", "gzip, deflate, br");httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36");*/packageHeader(headers, httpPost);// 封装请求参数packageParam(params, httpPost);// 创建httpResponse对象CloseableHttpResponse httpResponse = null;try {// 执行请求并获得响应结果return getHttpClientResult(httpResponse, httpClient, httpPost);} finally {// 释放资源release(httpResponse, httpClient);}}/*** 发送put请求;不带请求参数* * @param url 请求地址* @param params 参数集合* @return* @throws Exception*/public static HttpClientResult doPut(String url) throws Exception {return doPut(url);}/*** 发送put请求;带请求参数* * @param url 请求地址* @param params 参数集合* @return* @throws Exception*/public static HttpClientResult doPut(String url, Map<String, String> params) throws Exception {CloseableHttpClient httpClient = HttpClients.createDefault();HttpPut httpPut = new HttpPut(url);RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpPut.setConfig(requestConfig);packageParam(params, httpPut);CloseableHttpResponse httpResponse = null;try {return getHttpClientResult(httpResponse, httpClient, httpPut);} finally {release(httpResponse, httpClient);}}/*** 发送delete请求;不带请求参数* * @param url 请求地址* @param params 参数集合* @return* @throws Exception*/public static HttpClientResult doDelete(String url) throws Exception {CloseableHttpClient httpClient = HttpClients.createDefault();HttpDelete httpDelete = new HttpDelete(url);RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(CONNECT_TIMEOUT).setSocketTimeout(SOCKET_TIMEOUT).build();httpDelete.setConfig(requestConfig);CloseableHttpResponse httpResponse = null;try {return getHttpClientResult(httpResponse, httpClient, httpDelete);} finally {release(httpResponse, httpClient);}}/*** 发送delete请求;带请求参数* * @param url 请求地址* @param params 参数集合* @return* @throws Exception*/public static HttpClientResult doDelete(String url, Map<String, String> params) throws Exception {if (params == null) {params = new HashMap<String, String>();}params.put("_method", "delete");return doPost(url, params);}/*** Description: 封装请求头* @param params* @param httpMethod*/public static void packageHeader(Map<String, String> params, HttpRequestBase httpMethod) {// 封装请求头if (params != null) {Set<Entry<String, String>> entrySet = params.entrySet();for (Entry<String, String> entry : entrySet) {// 设置到请求头到HttpRequestBase对象中httpMethod.setHeader(entry.getKey(), entry.getValue());}}}/*** Description: 封装请求参数* * @param params* @param httpMethod* @throws UnsupportedEncodingException*/public static void packageParam(Map<String, String> params, HttpEntityEnclosingRequestBase httpMethod)throws UnsupportedEncodingException {// 封装请求参数if (params != null) {List<NameValuePair> nvps = new ArrayList<NameValuePair>();Set<Entry<String, String>> entrySet = params.entrySet();for (Entry<String, String> entry : entrySet) {nvps.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));}// 设置到请求的http对象中httpMethod.setEntity(new UrlEncodedFormEntity(nvps, ENCODING));}}/*** Description: 获得响应结果* * @param httpResponse* @param httpClient* @param httpMethod* @return* @throws Exception*/public static HttpClientResult getHttpClientResult(CloseableHttpResponse httpResponse,CloseableHttpClient httpClient, HttpRequestBase httpMethod) throws Exception {// 执行请求httpResponse = httpClient.execute(httpMethod);// 获取返回结果if (httpResponse != null && httpResponse.getStatusLine() != null) {String content = "";if (httpResponse.getEntity() != null) {content = EntityUtils.toString(httpResponse.getEntity(), ENCODING);}return new HttpClientResult(httpResponse.getStatusLine().getStatusCode(), content);}return new HttpClientResult(HttpStatus.SC_INTERNAL_SERVER_ERROR);}/*** Description: 释放资源* * @param httpResponse* @param httpClient* @throws IOException*/public static void release(CloseableHttpResponse httpResponse, CloseableHttpClient httpClient) throws IOException {// 释放资源if (httpResponse != null) {httpResponse.close();}if (httpClient != null) {httpClient.close();}}}
如果觉得《Java文字转语音功能实现》对你有帮助,请点赞、收藏,并留下你的观点哦!