(本文源地址:http://blog.csdn.net/speeds3/article/details/75131125)
現(xiàn)代人的生活越來越離不開手機(jī),但我們總會(huì)遇到一些時(shí)候不方便用手去操作,比如開車,玩游戲的時(shí)候。智能語音時(shí)代這種情況有了新的解決方案。本文介紹了一個(gè)使用OLAMI Android SDK進(jìn)行語音識(shí)別和理解,訊飛在線語音合成sdk進(jìn)行語音合成實(shí)現(xiàn)在收到短信時(shí)直接進(jìn)行語音回復(fù)的demo開發(fā)過程。在此基礎(chǔ)上我們也可以很方便的增加其他的功能,比如查新聞,百科等,完成一個(gè)DIY的語音助手。
簡介
OLAMI
OLAMI是由威盛電子(上海)有限公司人工智能軟件研發(fā)團(tuán)隊(duì)推出的一個(gè)人工智能軟件開發(fā)平臺(tái),提供包括自然語音交互技術(shù)在內(nèi)的全方位人機(jī)交互解決方案,覆蓋了眾多垂直領(lǐng)域的語義通用場景。
訊飛語音合成
由科大訊飛提供的語音合成解決方案,解決的主要問題是如何將文字信息轉(zhuǎn)化為可聽的聲音信息,也即“讓機(jī)器像人一樣開口說話”。
開始
整個(gè)demo很簡單,主要流程就是:收到短信->播報(bào)短信->用戶語音回復(fù)->發(fā)送短信。其中的關(guān)鍵就是用戶語音回復(fù)的內(nèi)容如何轉(zhuǎn)換成發(fā)送短信的命令。
獲取OLAMI SDK的密鑰
要使用OLAMI服務(wù),需要先注冊(cè),地址是https://cn.olami.ai/open/website/register/register_data。注冊(cè)過程很簡單,之后綁定手機(jī)就可以使用了。
現(xiàn)在可以回到應(yīng)用管理,點(diǎn)擊查看Key,記錄下App Key和App Secret,留待后面編寫代碼的時(shí)候使用。順便提一下,點(diǎn)擊配置模塊,里面有一些預(yù)定義好的內(nèi)置功能,默認(rèn)勾選的有nonsense(聊天)、date(日歷)和cyclopaedia(百科)這幾個(gè)被勾上,作用我們后面再說。
新建OLAMI應(yīng)用
注冊(cè)完之后進(jìn)入應(yīng)用管理,就可以創(chuàng)建自己的應(yīng)用了。創(chuàng)建完應(yīng)用之后進(jìn)入NLI系統(tǒng)可以編寫語法。我們的應(yīng)用準(zhǔn)備支持重念和回復(fù)功能。所以添加對(duì)應(yīng)的兩句語法:
寫完之后不要忘記發(fā)布:
至此,我們?cè)贠LAMI平臺(tái)的準(zhǔn)備工作就已經(jīng)完成,馬上就將進(jìn)入Android Studio開始編寫代碼。
新建Android工程
在Android Studio中新建一個(gè)工程,Target Devices選Phone and Tablet。Android SDK從API 19開始提供了新的獲取新收到短信的方法,所以Minimum SDK選擇API 19。然后一路Next到Finish。
接收收到短信事件
在manifest中添加接收和發(fā)送短信的權(quán)限:
<uses-permission android:name="android.permission.RECEIVE_SMS" />
<uses-permission android:name="android.permission.SEND_SMS" />
由于這個(gè)應(yīng)用中收到短信廣播后,要回到Activity中進(jìn)行后續(xù)的工作,我們選擇了在Activity動(dòng)態(tài)創(chuàng)建一個(gè)Broadcast receiver,并通過setSmsHandler將二者關(guān)聯(lián)起來:
// 注冊(cè)短信廣播接收器
receiver = new SmsReceiver();
receiver.setSmsHandler(this);
IntentFilter filter = new IntentFilter();
filter.addAction(SMS_RECEIVED_ACTION);
registerReceiver(receiver, filter);
在receiver的onReceive方法中獲得剛收到的短信內(nèi)容:
SmsMessage[] msgs = Telephony.Sms.Intents.getMessagesFromIntent(intent);
然后就可以解析短信的數(shù)據(jù)了。需要注意的是,號(hào)碼如果直接送到tts去讀,有可能會(huì)讀成數(shù)量的形式(例如10086會(huì)讀成一萬零八十六),所以demo中在每個(gè)數(shù)字之間加入了空格。
收到短信后,要播報(bào)短信內(nèi)容,并且要存儲(chǔ)短信以便重聽的時(shí)候使用。所以這里在MainActivity中實(shí)現(xiàn)了一個(gè)接口方便在Receiver中使用:
SmsReceiver.java
public interface SmsHandler {
void processNewMsg(String phoneNumber, String content);
}
MainActivity.java
@Override
public void processNewMsg(String phoneNumber, String content) {
StringBuilder finalAddress = new StringBuilder();
// 播報(bào)號(hào)碼中加入空格,以免讀成數(shù)量
for (int i = 0; i < phoneNumber.length(); i ++) {
finalAddress.append(phoneNumber.charAt(i));
finalAddress.append(' ');
}
String tts = String.format("收到來自%s的短信,內(nèi)容是%s,你想回復(fù)什么?", finalAddress, content);
speak(tts);
// 記錄短信內(nèi)容
number = phoneNumber;
lastMsg = content;
// 打開錄音
try {
recognizer.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
播報(bào)短信
前面我們看到在processNewMsg中調(diào)用了speak方法來念出短信。這個(gè)例子中選擇使用訊飛的在線語音合成api實(shí)現(xiàn)。訊飛語音合成的sdk同樣需要注冊(cè),創(chuàng)建應(yīng)用,完成后即可下載sdk。訊飛的知名度比較高,這里就不詳細(xì)介紹過程了。
訊飛api的使用也很簡單,按照sdk中的文檔添加權(quán)限,把包放到響應(yīng)的位置就可以開始編寫代碼了。首先在onCreate中初始化:
// 初始化訊飛服務(wù)。APPID注冊(cè)訊飛平臺(tái),創(chuàng)建應(yīng)用即可獲得
SpeechUtility uti = SpeechUtility.createUtility(getApplicationContext(), SpeechConstant.APPID + "=595da10d");
if (uti == null) {
System.out.println("create Utility failed. ");
}
tts = SpeechSynthesizer.createSynthesizer(getApplicationContext(), new InitListener() {
@Override
public void onInit(int i) {
System.out.println("tts初始化完成");
tts.setParameter(SpeechConstant.ENGINE_TYPE, SpeechConstant.TYPE_CLOUD);
tts.setParameter(SpeechConstant.ENGINE_MODE, SpeechConstant.MODE_AUTO);
tts.setParameter(SpeechConstant.VOICE_NAME, "xiaoyan");
tts.setParameter(SpeechConstant.SPEED, "40");
}
});
然后就可以使用了:
public void speak(String content) {
tts.startSpeaking(content, new SynthesizerListener() {
@Override
public void onSpeakBegin() {
}
@Override
public void onBufferProgress(int i, int i1, int i2, String s) {
}
@Override
public void onSpeakPaused() {
}
@Override
public void onSpeakResumed() {
}
@Override
public void onSpeakProgress(int i, int i1, int i2) {
}
@Override
public void onCompleted(SpeechError speechError) {
}
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
}
});
}
從代碼里的linstener中我們可以看到訊飛提供了豐富的回調(diào)函數(shù)便于處理整個(gè)語音合成的過程。
至此我們就實(shí)現(xiàn)了收到短信播報(bào)的功能。
語音轉(zhuǎn)語義
OLAMI SDK中提供了方便的接口(參考文檔:OLAMI Android Client SDK & 示例代碼),配置對(duì)應(yīng)權(quán)限后,從打開錄音到得到語義的整個(gè)過程都只需調(diào)用一個(gè)簡單的start方法,之后在對(duì)應(yīng)的回調(diào)函數(shù)中可以拿到結(jié)果。
與訊飛接口類似,首先在onCreate中初始化:
...
// MainActivity的成員變量
RecorderSpeechRecognizer recognizer;
TextRecognizer textRecognizer;
...
// onCreate方法中
// 初始化olami服務(wù)
// 創(chuàng)建 APIConfiguration 對(duì)象
APIConfiguration config = new APIConfiguration(KEY, SECRET, APIConfiguration.LOCALIZE_OPTION_SIMPLIFIED_CHINESE);
recognizer = RecorderSpeechRecognizer.create(this, config);
textRecognizer = new TextRecognizer(config);
// 下面為可選的設(shè)置
recognizer.setLengthOfVADEnd(2000);
textRecognizer.setEndUserIdentifier("Someone");
textRecognizer.setTimeout(1000);
RecorderSpeechRecognizer是語音識(shí)別引擎,TextRecognizer是文本識(shí)別引擎。TextRecognizer在這里用于模擬器測試,在不方便錄音的情況下也能夠做到短信收發(fā)。RecorderSpeechRecognizer.create方法的第一個(gè)參數(shù)是IRecorderSpeechRecognizerListener,這里直接在MainActivity中實(shí)現(xiàn)。這個(gè)Listener中也提供了很豐富的回調(diào)接口,涵蓋了整個(gè)錄音、識(shí)別、音量調(diào)節(jié)過程,很方便使用。
然后在Activity中放一個(gè)按鈕(我這里叫Button2),點(diǎn)擊事件中啟動(dòng)錄音:
public void onButton2Click(View view) {
RecorderSpeechRecognizer.RecordState recordState = recognizer.getRecordState();
// Check to see if we should start recording or stop manually.
if (recordState == RecorderSpeechRecognizer.RecordState.STOPPED) {
try {
// * Request to start voice recording and recognition.
recognizer.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else if (recordState == RecorderSpeechRecognizer.RecordState.RECORDING) {
// * Request to stop voice recording when manually stop,
// and then wait for the final recognition result.
recognizer.stop();
}
}
之后就是在onRecognizeResultChange中處理結(jié)果:
@Override
public void onRecognizeResultChange(APIResponse response) {
if (response.ok() && response.hasData()) {
// 提取語音轉(zhuǎn)文字識(shí)別結(jié)果
//SpeechResult sttResult = response.getData().getSpeechResult();
// 提取 NLI 語義或 IDS 數(shù)據(jù)
if (response.getData().hasNLIResults()) {
NLIResult[] nliResults = response.getData().getNLIResults();
for (NLIResult result : nliResults) {
if (result.getSemantics() != null && result.getSemantics().length > 0) {
for (Semantic semantic : result.getSemantics()) {
if (semantic.getAppModule().equals("sms")) {
switch (semantic.getGlobalModifiers()[0]) {
case "reply": {
for (Slot slot : semantic.getSlots()) {
if (slot.getName().equals("content")) {
replyMsg(slot.getValue());
return;
}
}
}
break;
case "repeat": {
repeatMsg();
}
default:
break;
}
}
}
} else {
speak(result.getDescObject().getReplyAnswer());
}
}
}
}
}
這里提到一個(gè)小插曲,在剛開始做這個(gè)demo的時(shí)候下載的OLAMI SDK還是1.0版jar包的方式,前兩天發(fā)現(xiàn)SDK升級(jí)到了2.0版。2.0版的使用比舊版方便很多,只需在build.gradle中加上配置,就可以直接從gradle中央倉庫獲得??梢奜LAMI官方代碼的維護(hù)還是很勤快的。
回復(fù)短信
接下來只要實(shí)現(xiàn)replyMsg和repeatMsg就大功告成了:
public void replyMsg(String content) {
if (number != null) {
SmsManager.getDefault().sendTextMessage(number, null, content, null, null);
speak("好的");
} else {
speak("我還不知道要發(fā)給誰。");
}
}
private void repeatMsg() {
if (lastMsg != null) {
speak(lastMsg);
} else {
speak("我不知道你想聽的是哪條短信。");
}
}
小結(jié)
總的來說OLAMI和訊飛兩部分的SDK使用起來都很輕松方便,只要把任務(wù)交出去就能拿到結(jié)果。整個(gè)demo結(jié)構(gòu)非常簡單,實(shí)現(xiàn)了主要功能,但正式使用的話還需要很多改進(jìn),比如加上權(quán)限的判斷,以免啟動(dòng)時(shí)報(bào)錯(cuò)退出;豐富OLAMI平臺(tái)上的語法,以適應(yīng)不同用戶的不同說法等。
結(jié)束語
本文主要介紹了如何用OLAMI SDK和訊飛語音合成制作一個(gè)Android平臺(tái)短信小助手的過程。通過對(duì)這些開放平臺(tái)的使用,普通的開發(fā)者能夠很快捷的實(shí)現(xiàn)語音、語義相關(guān)的功能。前面提到的OLAMI平臺(tái)勾選的預(yù)置功能在最后也帶來了很有意思的效果:本來預(yù)想一個(gè)簡單的語音回復(fù)短信工具居然自帶了聊天機(jī)器人的功能。如果我們對(duì)功能進(jìn)行擴(kuò)展,就能很快的為自己量身打造一個(gè)語音助手,這在幾年前是很難想象的事情。
附錄
** 本示例源代碼 **
github:https://github.com/stdioh-cn/MessageDemo2
csdn下載頻道:http://download.csdn.net/detail/speeds3/9899273
** 優(yōu)秀自然語言理解博客文章推薦:**
根據(jù)OLAMI平臺(tái)開發(fā)的日歷Demo
用olami開放語義平臺(tái)做匯率換算應(yīng)用
自然語言處理-實(shí)際開發(fā):用語義開放平臺(tái)olami寫一個(gè)翻譯的應(yīng)用
微信小程序+OLAMI自然語言API接口制作智能查詢工具--快遞、聊天、日歷等
熱門自然語言理解和語音API開發(fā)平臺(tái)對(duì)比
** 自然語言理解開發(fā)愛好者博客匯總:**
http://blog.csdn.net/huangmeimao
http://blog.csdn.net/u011211290
http://blog.csdn.net/u011827504