【Android App】实现在线语音合成功能(使用云知声平台和WebSocket 超详细 附源码)

news/2024/5/2 10:13:33/文章来源:https://blog.csdn.net/jiebaoshayebuhui/article/details/128064893

需要源码和Jar包请点赞关注收藏后评论区留下QQ~~~

一、在线语音合成

虽然国产智能机大多集成了中文语音引擎,但是系统自带的语音工具无法满足商用要求,功能单一,所以势必引入第三方的语音引擎,依靠第三方提供的开发包统一支撑语音的交互操作

此处选用云知声引擎,对新生免费并且语音处理采用公开的WebSocket接口,无须引入额外的语音SDK,进入云知声网址后,在右上角找到AI开放平台,然后注册进入控制台创建应用即可

云知声官网

创建好后如下 要记住key和secret  后面要用v

云知声采用WebSocket接口交互,故而不管是语音合成还是语音识别,都需要定义WebSocket客户端的处理任务,云知声使用JSON字符串封装报文合成后的音频数据通过字节数组传回,具体合成过程如下

1:定义WebSocket客户端的语音合成任务 

实现以下几个功能

1:在请求报文中填写原始文本 音频格式和采样率等合成参数 再把JSON字符串传给WebSocket服务器

2:服务器分批返回字节数组形式的音频流 客户端需要将这些数据依次追加到存储卡中

3:在合成过程中,服务器还会数次返回JSON格式的应答报文 可能不止一个,只有报文中的end字段为true时才表示合成结束

2:把语音任务关联到WebSocket服务器 

此时要拼接完整的URL地址,包含之前在云知声平台的appkey和appsecret,填在SoundUtil这个类中

3:创建并启动语音合成任务

效果如下

 

 合成结束后效果如下 点击右上角的播放可以收听由文字转换的语音

 部分代码如下

需要源码请点赞关注收藏后评论区留下QQ~~~

Java类

package com.example.voice;import android.media.AudioManager;
import android.media.MediaPlayer;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;import com.example.voice.constant.SoundConstant;
import com.example.voice.task.TtsClientEndpoint;
import com.example.voice.util.DateUtil;
import com.example.voice.util.SoundUtil;public class VoiceComposeActivity extends AppCompatActivity {private final static String TAG = "VoiceComposeActivity";private TextView tv_option; // 声明一个文本视图对象private EditText et_compose_text; // 声明一个编辑框对象private TextView tv_result; // 声明一个文本视图对象private String mComposeFilePath; // 合成语音的文件路径private MediaPlayer mMediaPlayer = new MediaPlayer(); // 媒体播放器private boolean isPlaying = false; // 是否正在播音public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_voice_compose);findViewById(R.id.iv_back).setOnClickListener(v -> finish());TextView tv_title = findViewById(R.id.tv_title);tv_title.setText("在线语音合成");tv_option = findViewById(R.id.tv_option);tv_option.setText("开始播放语音");tv_option.setVisibility(View.GONE);et_compose_text = findViewById(R.id.et_compose_text);tv_result = findViewById(R.id.tv_result);findViewById(R.id.btn_compose_voice).setOnClickListener(v -> {String text = et_compose_text.getText().toString();if (TextUtils.isEmpty(text)) {Toast.makeText(this, "请先输入待朗读的一段话", Toast.LENGTH_SHORT).show();return;}new Thread(() -> onlineCompose(text)).start(); // 启动在线合成语音的线程});tv_option.setOnClickListener(v -> {if (!isPlaying) { // 未在播音startPlay(); // 开始播音} else { // 正在播音stopPlay(); // 停止播音}});}// 在线合成语音private void onlineCompose(String text) {mComposeFilePath = String.format("%s/%s.mp3",getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString(),DateUtil.getNowDateTime());// 创建语音合成任务,并指定语音监听器TtsClientEndpoint task = new TtsClientEndpoint(this, mComposeFilePath, text, arg -> {if (Boolean.TRUE.equals(arg[0])) {Toast.makeText(this, "语音合成结束", Toast.LENGTH_SHORT).show();tv_result.setText("音频文件位于"+arg[2]);tv_option.setVisibility(View.VISIBLE);}});SoundUtil.startSoundTask(SoundConstant.URL_TTS, task); // 启动语音合成任务}// 开始播音private void startPlay() {isPlaying = !isPlaying;tv_option.setText("停止播放语音");mMediaPlayer.reset(); // 重置媒体播放器// 设置媒体播放器的完成监听器mMediaPlayer.setOnCompletionListener(mp -> stopPlay());mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); // 设置音频流的类型为音乐try {mMediaPlayer.setDataSource(mComposeFilePath); // 设置媒体数据的文件路径mMediaPlayer.prepare(); // 媒体播放器准备就绪mMediaPlayer.start(); // 媒体播放器开始播放} catch (Exception e) {e.printStackTrace();}}// 停止播音private void stopPlay() {tv_option.setText("开始播放语音");if (mMediaPlayer.isPlaying() || isPlaying) { // 如果正在播放isPlaying = !isPlaying;mMediaPlayer.stop(); // 停止播放Toast.makeText(this, "语音播放结束", Toast.LENGTH_LONG).show();}}@Overrideprotected void onStop() {super.onStop();stopPlay(); // 停止播音}@Overrideprotected void onDestroy() {super.onDestroy();mMediaPlayer.release(); // 释放媒体播放器}
}

SoundUtil类

package com.example.voice.util;import android.util.Log;import com.example.voice.constant.SoundConstant;import java.net.URI;
import java.security.MessageDigest;import javax.websocket.ContainerProvider;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;public class SoundUtil {private final static String TAG = "SoundUtil";// 启动语音处理任务(语音识别或者语音合成)public static void startSoundTask(String url, Object task) {long time = System.currentTimeMillis();StringBuilder paramBuilder = new StringBuilder();// 填写该应用在开放平台上申请的密钥和密码paramBuilder.append(SoundConstant.APP_KEY).append(time).append(SoundConstant.APP_SECRET);String sign = getSHA256Digest(paramBuilder.toString());StringBuilder param = new StringBuilder();param.append("appkey=azkk2kwv5f22m5z4iebchxsetodz3y677chtzniz").append(SoundConstant.APP_KEY).append("&").append("time=").append(time).append("&").append("sign=").append(sign).append("&").append("appsecret=6d6f4426e005e6b7f9a7fee2a9fdda44");String fullUrl = url + param.toString();Log.d(TAG, "fullUrl="+fullUrl);// 获取WebSocket容器WebSocketContainer container = ContainerProvider.getWebSocketContainer();try {URI uri = new URI(fullUrl); // 创建一个URI对象// 连接WebSocket服务器,并关联语音处理任务获得连接会话Session session = container.connectToServer(task, uri);// 设置文本消息的最大缓存大小session.setMaxTextMessageBufferSize(1024 * 1024 * 10);// 设置二进制消息的最大缓存大小session.setMaxBinaryMessageBufferSize(1024 * 1024 * 10);} catch (Exception e) {e.printStackTrace();}}// 获得SHA摘要private static String getSHA256Digest(String data) {String digest = null;try {MessageDigest md = MessageDigest.getInstance("SHA-256");byte[] bytes = md.digest(data.getBytes("UTF-8"));digest = byte2hex(bytes);} catch (Exception e) {e.printStackTrace();}return digest;}// 二进制转十六进制字符串private static String byte2hex(byte[] bytes) {StringBuilder sign = new StringBuilder();for (int i = 0; i < bytes.length; i++) {String hex = Integer.toHexString(bytes[i] & 0xFF);if (hex.length() == 1) {sign.append("0");}sign.append(hex.toUpperCase());}return sign.toString();}}

创作不易 觉得有帮助请 点赞关注收藏~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_37780.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

缓存穿透、缓存击穿、缓存雪崩及其解决方案

缓存&#xff08;cache&#xff09;&#xff0c;大家都非常熟悉&#xff0c;几乎每个系统乃至整个计算机体系中都会用到。在分布式系统架构中&#xff0c;主要用于减轻数据库的压力&#xff0c;提高系统的响应速度和并发吞吐&#xff0c;即空间(内存)换时间。当大量的读、写请求…

2023年天津财经大学珠江学院专升本经济学专业课考试大纲

天津财经大学珠江学院2023年高职升本科专业课考试《经济学》考试大纲一、本大纲系天津财经大学珠江学院2023年高职升本科《经济学》课程考试大纲。所列考试范围出自郑健壮、王培才主编的教材《经济学基础&#xff08;第二版&#xff09;》&#xff0c;清华大学出版社&#xff0…

React - Ant Design4.x版本安装使用,并按需引入和自定义主题

React - Ant Design4.x版本安装使用&#xff0c;并按需引入和自定义主题一. 安装使用 antd二&#xff0e;antd 高级配置安装 craco&#xff0c;对 create-react-app 的默认配置进行自定义自定义主题安装 babel-plugin-import &#xff0c;按需加载组件代码和样式Ant Design官网…

mycat-3-实战篇

1 总结&#xff1a; 1&#xff1a;用的表必须在mycat的配置文件中配置。 2&#xff1a;mycat默认分片策略中&#xff0c;都是针对表的主键&#xff0c;默认是id,如果主键不是id的&#xff0c;请去rule.xml自己复制一份修改 3&#xff1a; 2 注意细讲解 1&#xff1a;schem…

车辆大全和车牌识别系统毕业设计,车牌识别系统设计与实现,车牌AI识别系统论文毕设作品参考

功能清单 【后台管理员功能】 系统设置&#xff1a;设置网站简介、关于我们、联系我们、加入我们、法律声明 广告管理&#xff1a;设置小程序首页轮播图广告和链接 留言列表&#xff1a;所有用户留言信息列表&#xff0c;支持删除 会员列表&#xff1a;查看所有注册会员信息&a…

TypeScript开启

TypeScript是什么&#xff1f; typescript是以JavaScript为基础构建的语言&#xff0c;是一个Javascript的超集&#xff0c;可以在任何支持JavaScript的平台中执行&#xff0c;typescript扩展了JavaScript&#xff0c;并添加了类型。 注意&#xff1a;ts不能被js直接解析执行&…

27个超实用Chrome DevTools 调试技巧 source 全局搜索(持续更新)

谷歌开发者工具提供了一系列的功能来帮助开发者高效 Debug 网页应用&#xff0c;让他们可以更快地查找和修复 bug。在谷歌的开发者工具中&#xff0c;有非常多有用的小工具&#xff0c;但是很多开发者并不知道。通过这篇文章&#xff0c;我把我常用的那些高效 Debug 的 Chrome …

大数据(9f)Flink双流JOIN

文章目录概述开发环境使用状态列表实现 INNER JOIN&#xff08;双流connect后CoProcessFunction&#xff09;基于间隔的JOIN&#xff08;Interval Join&#xff09;基于窗口的JOIN&#xff08;Window Join&#xff09;概述 Flink双流JOIN可用算子或SQL实现&#xff0c;FlinkSQ…

Flutter 5 大本地数据库解决方案

Flutter 5 大本地数据库解决方案 原文 https://levelup.gitconnected.com/top-5-local-database-solutions-for-flutter-development-6351cd494070 前言 这里列出了最流行的数据库解决方案以及代码示例。 选择正确的数据管理系统对于提高效率和可 extension 性以及影响可用性和…

PyQt5学习笔记--摄像头实时视频展示、多线程处理、视频编解码

目录 1--前言 2--基于Qt Designer设计ui文件 3--视频的编解码操作 4--完整代码 5--结果展示 6--存在的问题 7--参考 1--前言 ① 创建两个线程&#xff0c;主线程为ui线程&#xff0c;子线程用于读取摄像头视频&#xff0c;将处理后的图像帧数据&#xff08;处理操作可以…

JDBC操作数据库实现增、删、查、改

0.JDBC概念 实际开发中,手动的输入SQL语句是少之又少,大多数情况下是通过编译代码进行来控制自动执行. 具体操作如下: 上述展示有一个【自己写的Mysql客户端】&#xff0c;这种操作是非常容易的&#xff0c;因为各种数据库本身就提供一系列的API&#xff0c;可以让用户很方便…

内存一致性,指令重排序,内存屏障,volatile解析

文章目录为什么会存在“内存可见性”问题重排序与内存可见性的关系as-if-serial语义单线程程序的重排序规则多线程程序的重排序规则happen-before是什么解决方案&#xff1a;内存屏障Volatile关键字解决内存可见性问题的实现原理为什么会存在“内存可见性”问题 下图为x86架构…

如何利用快解析远程访问家庭智能网关

随着家庭宽带用户的暴增&#xff0c;涌现出了许多连接家居设备和控制中心的产品&#xff0c;如家庭智能网关。家庭智能网关是家居智能化的心脏&#xff0c;通过它实现系统的信息采集、信息输入、信息输出、集中控制、远程控制、联动控制等功能。 ​ 智能家庭网关具备智能家居控…

3D-SKIPDENSESEG医学图像分割

蓝色三角、黄色三角、红色三角相对应。 得到第三个feature map&#xff0c;反卷积会恢复到原来的尺寸 Dense block&#xff0c;通道增加了 Transition&#xff0c;池化 用正则表达式把里面的h5文件匹配一下吧 os.path.join()把两个部分的路径拼一下 root_path —data_train *.…

day13_面向对象的三大特征之一(封装)

封装概述 为什么需要封装&#xff1f; 现实生活中&#xff0c;每一个个体与个体之间是有边界的&#xff0c;每一个团体与团体之间是有边界的&#xff0c;而同一个个体、团体内部的信息是互通的&#xff0c;只是对外有所隐瞒。例如&#xff1a;我们使用的电脑&#xff0c;内部…

链表之删除单链表中的重复节点

文章目录删除单链表中的重复节点题目描述解题思路代码实现删除单链表中的重复节点 力扣链接 题目描述 编写代码&#xff0c;移除未排序链表中的重复节点。保留最开始出现的节点。 示例1:输入&#xff1a;[1, 2, 3, 3, 2, 1]输出&#xff1a;[1, 2, 3]示例2:输入&#xff1a;…

潜匿的怪物,你的供应链真的安全吗?

网络钓鱼、DNS欺骗      勒索软件、MITM攻击      在这个网络环境      风声鹤唳的时代      这些网络攻击类型      你一定不会感到陌生      无孔不入,这个词用来形容网络攻击毫不为过。世上没有绝对锋利的矛,同样也没有坚不可摧的盾,即使您养成了安…

这五个适合上班族的副业你知道多少

第二职业赚钱的路子有什么&#xff1f;从理论上讲&#xff0c;第二职业就是一个创业的过程&#xff0c;也遵照自主创业一般规律。可是第二职业是在业余时间和没有灵活运用资源挣钱&#xff0c;和创业有所不同。第二职业门坎变低&#xff0c;更比较发达&#xff0c;因此今天小编…

vmware虚拟机centos7扩容

vmware先进行磁盘扩展 从原来的20G扩展到100G: 扩展磁盘时可能会遇到需要修复磁盘的情况: // 进入vmware安装目录cd D:\开发工具\VMWare\ // 修复, 其中.vmdk文件为虚拟机磁盘文件vmware-vdiskmanager -R "F:\VM_Centos\CentOS 7 64 位.vmdk" 扩展成功&#xff1…

合作对策模型的简单实现

以如下题目作为示例&#xff1a; 一位歌手(S)&#xff0c;一位钢琴家 (P) 和一位鼓手(D) 组成一个小乐队在俱乐部同台演出能得到演出费1000元&#xff0c;若歌手和钢琴家一起演出能得800元。而只有钢琴家和鼓手一起演出能得到650元&#xff0c;钢琴独奏表演能得300元&#xff…