手把手教你用JAVA调用Websocket实现“声音转换”功能(变声)
2021/9/16 11:05:09
本文主要是介绍手把手教你用JAVA调用Websocket实现“声音转换”功能(变声),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
手把手教你用JAVA调用Websocket实现“声音转换”功能(变声)标贝科技
前言
什么是声音转换?
基于深度学习和迁移学习技术,精准将原说话人的声音转换为目标说话人,同时保留原说话人的风格特色。让声音有更多的表现形式。
一、内容太长不愿意看,直接使用系列
首先确认接口调用要求:
实现方式:Websocket
支持音频文件的编码格式及文件名的后缀: pcm
支持音频文件的采样率/位深: 16000Hz/16bit
支持的语言:中文普通话
音频有效时长:不超过180分钟
确认无误后,直接执行 2.2获取权限+2.3.5完整代码示例
二、用JAVA调用标贝科技“声音转换”接口使用流程
2.1.环境准备
java
2.2.获取权限
2.1.1.登录
地址:标贝科技智能语音开放平台
点击上方地址登录,支持短信、密码、微信三种登录方式。
2.1.2.创建应用
登录后,点击创建应用,填写相关信息(未实名认证只能创建一个应用)
(注:实名认证后可获得创建多个应用的权限)
进入应用,其中包含的技术产品有:语音识别、语音合成、声音复刻、声音转换
页面中功能主要包括:服务用量管理、购买服务量管理、开发者文档、授权管理、套餐管理
2.1.3.获取token
点击声音转换—>授权管理—>显示—>获取APISecret—>(获取访问令牌token)
2.3.代码实现
2.3.1.获取token
/** * 授权:需要在开放平台获取【https://ai.data-baker.com/#/?source=qaz123】 */ private static final String clientId = "输入你的clientid"; private static final String clientSecret = "输入你的clientsecret"; /** * 获取token的地址信息 */ public static String tokenUrl = "https://openapi.data-baker.com/oauth/2.0/token?grant_type=client_credentials&client_secret=%s&client_id=%s"; public static String getAccessToken() { String accessToken = ""; OkHttpClient client = new OkHttpClient(); // request 默认是get请求 String url = String.format(tokenUrl, clientSecret, clientId); Request request = new Request.Builder().url(url).build(); JSONObject jsonObject; try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { // 解析 String resultJson = response.body().string(); jsonObject = JSON.parseObject(resultJson); accessToken = jsonObject.getString("access_token"); } } catch (Exception e) { e.printStackTrace(); } return accessToken; }
2.3.2.配置获取音频文件路径和声音转换后的保存路径
/** * 文件路径【开发者需要根据实际路径调整。支持的音频编码格式:PCM,采样率16K,位深16bit,中文普通话,时长不超过180分钟】 */ private static final String pcmFile = ""; /** * 文件夹路径【声音转换后文件存放地址,开发者需要根据实际路径调整。】 */ private static final String dic = "";
2.3.3.创建webscoket连接,配置接口参数
参数:
“access_token”:2.1.3中已获取
“voice_name”:代码中已提供(平台提供五种音色供你测试)
“enable_vad”:true代表启动服务端vad功能,默认false。如果启动系统会根据输入音频进行检测,过 滤环境噪音。否则直接将原始输入音频进行转换。
“align_input”:true代表输出音频与输入音频进行对齐,默认false。即开启vad时会保留静音部分,false丢弃静音部分。
“lastpkg”:是否为最后一包,当时发送最后一包数据时设置为true 告诉系统输入完成
@Override public void onOpen(WebSocket webSocket, Response response) { super.onOpen(webSocket, response); this.startTime = timeBegin.get(); // 该demo直接从文件中读取音频流【实际场景可能是实时从麦克风获取音频流,开发者自行修改获取音频流的逻辑即可】 new Thread(() -> { File file = new File(pcmFile); // 连接成功,开始发送数据 // 第二部分是一个JSON的字符串 JSONObject jsonObject = new JSONObject(); jsonObject.put("access_token", accessToken); jsonObject.put("voice_name", voiceName); jsonObject.put("enable_vad", true); jsonObject.put("align_input", true); jsonObject.put("lastpkg", false); int length = jsonObject.toJSONString().getBytes().length; // 第一部分 byte[] b = new byte[4]; b[0] = (byte) (length >> 24 & 0xFF); b[1] = (byte) (length >> 16 & 0xFF); b[2] = (byte) (length >> 8 & 0xFF); b[3] = (byte) (length & 0xFF); FileInputStream fileInputStream = null; ByteArrayOutputStream byteOut = null; try { fileInputStream = new FileInputStream(file); byteOut = new ByteArrayOutputStream(); int size = 32000; byte[] byteArray = new byte[size]; int totalLength = 0; int read = 0; // 发送音频 while ((read = fileInputStream.read(byteArray, 0, size)) > 0) { totalLength += read; System.out.println(); if (read == size && totalLength < file.length()) { byte[] bytes = ArrayUtils.addAll(ArrayUtils.addAll(b, jsonObject.toJSONString().getBytes()), byteArray); webSocket.send(new ByteString(bytes)); Thread.sleep(40); } else { // 最后一包 jsonObject.put("lastpkg", true); length = jsonObject.toJSONString().getBytes().length; b[0] = (byte) (length >> 24 & 0xFF); b[1] = (byte) (length >> 16 & 0xFF); b[2] = (byte) (length >> 8 & 0xFF); b[3] = (byte) (length & 0xFF); byte[] subarray = ArrayUtils.subarray(byteArray, 0, read); byte[] bytes = ArrayUtils.addAll(ArrayUtils.addAll(b, jsonObject.toJSONString().getBytes()), subarray); webSocket.send(new ByteString(bytes)); } } System.out.println("all data is send"); } catch (Exception e) { e.printStackTrace(); } finally { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (byteOut != null) { try { byteOut.close(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); }
2.3.4.接收返回的语音流并写入文件
@Override public void onMessage(WebSocket webSocket, ByteString bytes) { super.onMessage(webSocket, bytes); byte[] byteArray = bytes.toByteArray(); // byte[]转length int length = (byteArray[0] << 24) + (byteArray[1] << 16) + (byteArray[2] << 8) + byteArray[3]; byte[] jsonArray = ArrayUtils.subarray(byteArray, 4, 4 + length); String jsonStr = new String(jsonArray); JSONObject jsonObject = JSONObject.parseObject(jsonStr); byte[] subarray = ArrayUtils.subarray(byteArray, 4 + length, byteArray.length); File resultPcmFile = new File(dic, new File(pcmFile).getName().replace(".pcm", "_result.pcm")); if (!resultPcmFile.exists()) { try { resultPcmFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } try { // 写入文件 FileOutputStream outputStream = new FileOutputStream(resultPcmFile, true); IOUtils.write(subarray, outputStream); IOUtils.closeQuietly(outputStream); if ((int) jsonObject.get("errcode") == 0) { if (jsonObject.getBoolean("lastpkg")) { //说明数据全部返回完毕,可以关闭连接,释放资源 System.out.println("session end "); System.out.println(sdf.format(startTime) + "开始"); System.out.println(sdf.format(timeEnd.get()) + "结束"); System.out.println("耗时:" + (timeEnd.get().getTime() - startTime.getTime()) + "ms"); System.out.println("本次识别traceId ==》" + jsonObject.getString("traceid")); webSocket.close(1000, ""); } } else { System.out.println("errCode=>" + jsonObject.getInteger("errcode") + " errMsg=>" + jsonObject.getString("errmsg") + " traceId=" + jsonObject.getString("traceid")); // 关闭连接 webSocket.close(1000, ""); System.out.println("发生错误,关闭连接"); return; } } catch (Exception e) { e.printStackTrace(); } }
2.3.5.完整代码示例
package ...... import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import okhttp3.*; import okio.ByteString; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.ArrayUtils; import java.io.*; import java.text.SimpleDateFormat; import java.util.Date; /** * 声音转换WebSocket API接口调用示例 * 附:声音转换Websocket API文档 【https://www.data-baker.com/specs/file/vc_api_websocket】 * <p> * 注意:仅作为demo示例,失败重试、token过期重新获取、日志打印等优化工作需要开发者自行完成 * * @author data-baker */ public class VcWebSocketDemo extends WebSocketListener { /** * 授权:需要在开放平台获取【https://ai.data-baker.com/#/?source=qaz123】 */ private static final String clientId = "输入你的clientid"; private static final String clientSecret = "输入你的clientSecret"; /** * 获取token的地址信息 */ public static String tokenUrl = "https://openapi.data-baker.com/oauth/2.0/token?grant_type=client_credentials&client_secret=%s&client_id=%s"; private static final String hostUrl = "wss://openapi.data-baker.com/ws/voice_conversion"; /** * 文件路径【开发者需要根据实际路径调整。支持的音频编码格式:PCM,采样率16K,位深16bit,中文普通话,时长不超过180分钟】 */ private static final String pcmFile = ""; /** * 文件夹路径【声音转换后文件存放地址,开发者需要根据实际路径调整。】 */ private static final String dic = ""; private static final SimpleDateFormat sdf = new SimpleDateFormat("yyy-MM-dd HH:mm:ss.SSS"); // 开始时间 private static ThreadLocal<Date> timeBegin = ThreadLocal.withInitial(() -> new Date()); // 结束时间 private static ThreadLocal<Date> timeEnd = ThreadLocal.withInitial(() -> new Date()); // 发音人 private String voiceName = "Vc_baklong"; private Date startTime; private String accessToken = getAccessToken(); @Override public void onOpen(WebSocket webSocket, Response response) { super.onOpen(webSocket, response); this.startTime = timeBegin.get(); // 该demo直接从文件中读取音频流【实际场景可能是实时从麦克风获取音频流,开发者自行修改获取音频流的逻辑即可】 new Thread(() -> { File file = new File(pcmFile); // 连接成功,开始发送数据 // 第二部分是一个JSON的字符串 JSONObject jsonObject = new JSONObject(); jsonObject.put("access_token", accessToken); jsonObject.put("voice_name", voiceName); jsonObject.put("enable_vad", true); jsonObject.put("align_input", true); jsonObject.put("lastpkg", false); int length = jsonObject.toJSONString().getBytes().length; // 第一部分 byte[] b = new byte[4]; b[0] = (byte) (length >> 24 & 0xFF); b[1] = (byte) (length >> 16 & 0xFF); b[2] = (byte) (length >> 8 & 0xFF); b[3] = (byte) (length & 0xFF); FileInputStream fileInputStream = null; ByteArrayOutputStream byteOut = null; try { fileInputStream = new FileInputStream(file); byteOut = new ByteArrayOutputStream(); int size = 32000; byte[] byteArray = new byte[size]; int totalLength = 0; int read = 0; // 发送音频 while ((read = fileInputStream.read(byteArray, 0, size)) > 0) { totalLength += read; System.out.println(); if (read == size && totalLength < file.length()) { byte[] bytes = ArrayUtils.addAll(ArrayUtils.addAll(b, jsonObject.toJSONString().getBytes()), byteArray); webSocket.send(new ByteString(bytes)); Thread.sleep(40); } else { // 最后一包 jsonObject.put("lastpkg", true); length = jsonObject.toJSONString().getBytes().length; b[0] = (byte) (length >> 24 & 0xFF); b[1] = (byte) (length >> 16 & 0xFF); b[2] = (byte) (length >> 8 & 0xFF); b[3] = (byte) (length & 0xFF); byte[] subarray = ArrayUtils.subarray(byteArray, 0, read); byte[] bytes = ArrayUtils.addAll(ArrayUtils.addAll(b, jsonObject.toJSONString().getBytes()), subarray); webSocket.send(new ByteString(bytes)); } } System.out.println("all data is send"); } catch (Exception e) { e.printStackTrace(); } finally { if (fileInputStream != null) { try { fileInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (byteOut != null) { try { byteOut.close(); } catch (IOException e) { e.printStackTrace(); } } } }).start(); } @Override public void onMessage(WebSocket webSocket, String text) { super.onMessage(webSocket, text); } @Override public void onMessage(WebSocket webSocket, ByteString bytes) { super.onMessage(webSocket, bytes); byte[] byteArray = bytes.toByteArray(); // byte[]转length int length = (byteArray[0] << 24) + (byteArray[1] << 16) + (byteArray[2] << 8) + byteArray[3]; byte[] jsonArray = ArrayUtils.subarray(byteArray, 4, 4 + length); String jsonStr = new String(jsonArray); JSONObject jsonObject = JSONObject.parseObject(jsonStr); byte[] subarray = ArrayUtils.subarray(byteArray, 4 + length, byteArray.length); File resultPcmFile = new File(dic, new File(pcmFile).getName().replace(".pcm", "_result.pcm")); if (!resultPcmFile.exists()) { try { resultPcmFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } try { // 写入文件 FileOutputStream outputStream = new FileOutputStream(resultPcmFile, true); IOUtils.write(subarray, outputStream); IOUtils.closeQuietly(outputStream); if ((int) jsonObject.get("errcode") == 0) { if (jsonObject.getBoolean("lastpkg")) { //说明数据全部返回完毕,可以关闭连接,释放资源 System.out.println("session end "); System.out.println(sdf.format(startTime) + "开始"); System.out.println(sdf.format(timeEnd.get()) + "结束"); System.out.println("耗时:" + (timeEnd.get().getTime() - startTime.getTime()) + "ms"); System.out.println("本次识别traceId ==》" + jsonObject.getString("traceid")); webSocket.close(1000, ""); } } else { System.out.println("errCode=>" + jsonObject.getInteger("errcode") + " errMsg=>" + jsonObject.getString("errmsg") + " traceId=" + jsonObject.getString("traceid")); // 关闭连接 webSocket.close(1000, ""); System.out.println("发生错误,关闭连接"); return; } } catch (Exception e) { e.printStackTrace(); } } @Override public void onFailure(WebSocket webSocket, Throwable t, Response response) { super.onFailure(webSocket, t, response); try { if (null != response) { int code = response.code(); System.out.println("onFailure code:" + code); System.out.println("onFailure body:" + response.body().string()); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { OkHttpClient client = new OkHttpClient.Builder().build(); Request request = new Request.Builder().url(hostUrl).build(); client.newWebSocket(request, new VcWebSocketDemo()); } public static String getAccessToken() { String accessToken = ""; OkHttpClient client = new OkHttpClient(); // request 默认是get请求 String url = String.format(tokenUrl, clientSecret, clientId); Request request = new Request.Builder().url(url).build(); JSONObject jsonObject; try { Response response = client.newCall(request).execute(); if (response.isSuccessful()) { // 解析 String resultJson = response.body().string(); jsonObject = JSON.parseObject(resultJson); accessToken = jsonObject.getString("access_token"); } } catch (Exception e) { e.printStackTrace(); } return accessToken; } }
这篇关于手把手教你用JAVA调用Websocket实现“声音转换”功能(变声)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-28MQ底层原理资料详解:新手入门教程
- 2024-11-28MQ项目开发资料详解:新手入门教程
- 2024-11-28MQ项目开发资料详解:入门与初级用户指南
- 2024-11-28MQ消息队列资料入门教程
- 2024-11-28MQ消息队列资料:新手入门详解
- 2024-11-28MQ消息中间件资料详解与应用教程
- 2024-11-28MQ消息中间件资料入门教程
- 2024-11-28MQ源码资料详解与入门教程
- 2024-11-28MQ源码资料入门教程
- 2024-11-28RocketMQ底层原理资料详解