需要源码和Jar包请点赞关注收藏后评论区留下QQ~~~
虽然国产智能机大多集成了中文语音引擎,但是系统自带的语音工具无法满足商用要求,功能单一,所以势必引入第三方的语音引擎,依靠第三方提供的开发包统一支撑语音的交互操作
此处选用云知声引擎,对新生免费并且语音处理采用公开的WebSocket接口,无须引入额外的语音SDK,进入云知声网址后,在右上角找到AI开放平台,然后注册进入控制台创建应用即可
云知声官网
创建好后如下 要记住key和secret 后面要用v
云知声采用WebSocket接口交互,故而不管是语音合成还是语音识别,都需要定义WebSocket客户端的处理任务,云知声使用JSON字符串封装报文合成后的音频数据通过字节数组传回,具体合成过程如下
实现以下几个功能
1:在请求报文中填写原始文本 音频格式和采样率等合成参数 再把JSON字符串传给WebSocket服务器
2:服务器分批返回字节数组形式的音频流 客户端需要将这些数据依次追加到存储卡中
3:在合成过程中,服务器还会数次返回JSON格式的应答报文 可能不止一个,只有报文中的end字段为true时才表示合成结束
此时要拼接完整的URL地址,包含之前在云知声平台的appkey和appsecret,填在SoundUtil这个类中
效果如下
合成结束后效果如下 点击右上角的播放可以收听由文字转换的语音
部分代码如下
需要源码请点赞关注收藏后评论区留下QQ~~~
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();
}
}
@Override
protected void onStop() {
super.onStop();
stopPlay(); // 停止播音
}
@Override
protected 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();
}
}
创作不易 觉得有帮助请 点赞关注收藏~~~