目录
一、描述
二、实现功能(todo)
三、效果展示
四、实现
五、测试
这是一个既可以陪你聊天,又可以帮你打开应用软件和文件的贴心小能手
支持文件,目录的搜索和打开
支持打开应用软件,如打开微信,打开浏览器等
支持聊天对话,图片搜索,天气查询,成语接龙等功能
1. 文件/目录的搜索和打开
2. 打开应用软件,如微信
3. 天气查询、聊天对话、成语接龙
流程:
1. 说话并录制音频文件
2. 推送到百度语音识别平台进行语音识别
3. 判定是否是退出命令:如果是,直接退出
4. 否则,判定识别后的语音是打开文件或应用程序的命令还是普通的对话信息
4.1 如果是打开文件的命令,进入文件搜索模块;
4.2 如果是打开应用程序的命令,进入命令执行模块;
4.3 如果是对话信息,交给图灵平台,并对图灵平台响应的文本进行语音合成并播放
5. 一次交互完成
具体流程图如下:
具体实现之间,有两个准备工作:
1. 注册图灵账号,创建一个图灵机器人应用
2. 注册智能云账号,创建一个语音应用
正式开始实现:
1)构造Json串并向图灵平台发起请求
参考图灵平台提供的api使用文档【创建了一个图灵机器人后,点击“设置” -> 点击最底部“api使用文档” -> 点击左边“接入教程” -> “API V2.0 接入文档”】,向图灵平台发起请求
//进行字符串的序列化(普通字符串 -> Json串)和反序列化(Json串 -> 普通字符串)
public class JsonTest {
private static FieldConfigProperties config = new FieldConfigProperties();
private static final String apiKey = config.getJarvisApiKey();
private static final String userId = config.getJarvisUserId();
private static final String url = config.getJarvisUrl();
//总的调用方法
public String json(String str) {
//1. 构造Json串【序列化】
String res1 = buildJson(str);
//2. 发起请求
String res2 = sendPost(res1);
//3. 解析Json串【反序列化】
return parseJson(res2);
}
//向机器人发起请求
private String sendPost(String str) {
StringBuilder responseStr = new StringBuilder();
BufferedReader in = null;
try {
URL Url = new URL(url);
HttpURLConnection connection = (HttpURLConnection) Url.openConnection();
connection.setDoOutput(true);
connection.setDoInput(true);
connection.setRequestMethod("POST");
connection.setUseCaches(false);
connection.setInstanceFollowRedirects(true);
connection.setRequestProperty("connection", "Keep-Alive");
connection.setRequestProperty("Accept", "application/json");
connection.setRequestProperty("Content-Type", "application/json");
connection.setRequestProperty("Content-Type", "text/plain; charset=utf-8");
connection.connect();
//将字节流转换成字符流 往出写
OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream());
out.write(str);
out.close();
connection.getOutputStream().flush();
//读取响应结果 BufferedReader 字符缓冲输入流
in = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
responseStr.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭输入流
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return responseStr.toString();
}
//序列化:构建Json串
private String buildJson(String str) {
JSONObject jsonObject = new JSONObject();
UserInfo userInfo = new UserInfo();
userInfo.setApiKey(apiKey);
userInfo.setUserId(userId);
InputText inputText = new InputText();
inputText.setText(str);
Perception perception = new Perception();
perception.setInputText(inputText);
int reqType = 0;
jsonObject.put("reqType", reqType);
jsonObject.put("perception", perception);
jsonObject.put("userInfo", userInfo);
return jsonObject.toString();
}
//解析Json串
private String parseJson(String s) {
JSONObject jb = JSONObject.fromObject(s);
JSONArray js = jb.getJSONArray("results");
JSONObject jbS = JSONObject.fromObject(js.getJSONObject(0).toString());
String tmp = jbS.get("resultType").toString();
if (tmp.equals("text")) {
return (String) JSONObject.fromObject(jbS.get("values")).get("text");
}
if (tmp.equals("url")) {
return (String) JSONObject.fromObject(jbS.get("values")).get("url");
}
return null;
}
}
2)录音、语音识别
录音:
//进行录音
public class VoiceRecorder {
private static FieldConfigProperties config = new FieldConfigProperties();
private static TargetDataLine targetDataLine; //录入
private static AudioFormat audioFormat;
public void menu() {
System.out.println("正在聆听...");
newRecorder();
}
private void newRecorder() {
/*
*设置音频的格式
*/
//采样率,从8000,11025,16000,22050,44100
float sampleRate = 8000F;
//每个样本的中位数 8,16
int sampleSizeInBits = 16;
//信道数:单声道为1,立体声为2
int channels = 2;
//singned
boolean signed = true;
//以大端还是小端的顺序来存储音频数据
boolean bigEndian = true;
//构造具有线性PCM 编码和给定参数的AudioFormat
audioFormat = new AudioFormat(sampleRate, sampleSizeInBits,
channels, signed, bigEndian);
//构造数据行的信息对象,这个信息包括单个音频格式
//lineClass:该信息对象所描述的数据行的类
//format:所需的格式
DataLine.Info dataLineInfo = new DataLine.Info(TargetDataLine.class, audioFormat);
try {
targetDataLine = (TargetDataLine) AudioSystem.getLine(dataLineInfo);
new RecorderThread().start();
Thread.sleep(10000); //等待10秒后结束录音,可根据情况自行设置
endRecorder();
} catch (LineUnavailableException | InterruptedException e) {
e.printStackTrace();
}
}
class RecorderThread extends Thread {
@Override
public void run() {
//指定的文件类型 wav格式文件
AudioFileFormat.Type fileType = AudioFileFormat.Type.WAVE;
//设置文件类型和文件扩展名
File audioFile = new File(config.getRecordFile());
try {
targetDataLine.open(audioFormat);
//当开始音频捕获或回放时,生成start事件
targetDataLine.start();
AudioInputStream in = new AudioInputStream(targetDataLine);
//构造从指示的目标数据行读取数据的隐僻输入。该流的格式与目标数据行的格式相同
//fileType:要写入的音频文件的种类
AudioSystem.write(in, fileType, audioFile);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//结束录音
private void endRecorder() {
targetDataLine.stop();
targetDataLine.close();
}
}
语音识别:
参考百度语音识别提供的JAVA SDK文档【创建了一个应用之后,点击“管理应用” -> 点击“技术文档” -> 点击语音识别目录下的“JAVA SDK”】,获取取得语音识别的Java客户端以及语音识别接口的调用方法:
//语音识别:语音转文本
public class SpeechRec {
private FieldConfigProperties config = new FieldConfigProperties();
private final AipSpeech aipSpeech;
// 设置本地语音文件路径
// 获取当前工程路径:System.getProperty("user.dir")
private String recordPath = System.getProperty("user.dir") + File.separator + config.getRecordFile();
public SpeechRec(AipSpeech aipSpeech) {
this.aipSpeech = aipSpeech;
}
public String getResult() {
String path = recordPath;
if (Paths.get(path).toFile().exists()) { //如果本地语音文件存在,则向远程服务上传整段语音进行识别
HashMap options = new HashMap<>();
options.put("dev_pid", config.getJarvisDevPid()); //设置语音类别
// 对本地语音文件进行识别
org.json.JSONObject asrRes = aipSpeech.asr(path, "wav", 16000, options);
// 成功返回
/*{
"err_no": 0,
"err_msg": "success.",
"corpus_no": "15984125203285346378",
"sn": "481D633F-73BA-726F-49EF-8659ACCC2F3D",
"result": ["北京天气"]
}
*/
// 失败返回
/*{
"err_no": 2000,
"err_msg": "data empty.",
"sn": null
}
*/
// 提取返回的结果信息
if (asrRes.getString("err_msg").equals("success.")) {//表示成功返回
return asrRes.getJSONArray("result").getString(0);
}
}
return null;
}
}
3)语音合成、播放
语音合成:
参考百度语音合成提供的JAVA SDK文档【创建了一个应用之后,点击“管理应用” -> 点击“技术文档” -> 点击语音合成目录下的SDK目录下的“JAVA SDK”】,获取 调用语音合成接口的方法
//语音合成:文本转语音
public class TextConvertSpeech {
private FieldConfigProperties config = new FieldConfigProperties();
private final AipSpeech client;
private VoicePlayer voicePlayer;
public TextConvertSpeech(VoicePlayer voicePlayer, AipSpeech client) {
this.voicePlayer = voicePlayer;
this.client = client;
}
public void doSyn(String text) {
if (getMp3(text)) {
playMp3();
} else {
System.out.println("转换失败");
}
}
//将文本转换为MP3文件
private boolean getMp3(String text) {
// 设置可选参数
HashMap options = new HashMap<>();
//语速,取值0-9,默认为5中语速
options.put("spd", "5");
//音调,取值0-9,默认为5中语调
options.put("pit", "5");
//发音人选择,0为女生,1为男生,3为度逍遥,4为度丫丫
options.put("per", "0");
//音量0-15,5为中音量
options.put("vol", 5);
//调用接口,进行语音合成
TtsResponse res = client.synthesis(text, "zh", 1, options);
JSONObject res1 = res.getResult(); //服务器返回的内容,合成成功时为null,失败时包含error_no等信息
//生成的音频数据
byte[] data = res.getData();
if (data != null) {
try {
//写入到文件中去
Util.writeBytesToFileSystem(data, config.getSynthesisFile());
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
// 失败返回
/*{
"err_no":500,
"err_msg":"notsupport.",
"sn":"abcdefgh",
"idx":1
}
*/
//indentFactor缩进因子
if (res1 != null) {
System.out.println(res1.toString(2));
}
return true;
}
//播放MP3文件
private void playMp3() {
voicePlayer = new VoicePlayer(config.getSynthesisFile());
voicePlayer.play();
}
}
播放:
import javazoom.jl.decoder.JavaLayerException;
import javazoom.jl.player.Player;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
// 播放语音
public class VoicePlayer {
private Player player;
//voiceFile:语音合成后的音频文件
private String voiceFile;
public VoicePlayer(String voiceFile) {
this.voiceFile = voiceFile;
}
public void play() {
try {
BufferedInputStream in = new BufferedInputStream(new FileInputStream(voiceFile));
player = new Player(in);
player.play();
} catch (JavaLayerException | FileNotFoundException e) {
e.printStackTrace();
}
}
}
大致要实现的部分就是这些,具体实现详见My Sweety
测试环境:Windows10 ;处理器:AMD A8-8600P Radeon R6, 10 Compute Cores 4C+6G 1.60GHz;内存:4.00GB