Android 科大訊飛 線上和離線語音聽寫
效果圖:
參考資料
專案裡要用語音聽寫,想到了科大訊飛,參考上面的資料完成了最簡單的線上有UI的語音識別,後面想要改成離線也可以使用。參考下面的文章
下載好語記和離線資源之後,想要改成有UI的RecognizerDialog離線語音聽寫,但是沒成功,試了一下沒有UI的SpeechRecognizer,發現可以,只需在設定引數的時候加上一句
recognizer.setParameter(SpeechConstant.ENGINE_TYPE,SpeechConstant.TYPE_LOCAL);//呼叫語記介面
就可以使用了。可能是RecognizerDialog帶有UI介面,已經預設寫成線上的了吧
下面只貼出主活動的程式碼,其他參考上面的資料
package com.example.administrator.voicerecognition;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.RecognizerListener;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.SpeechUtility;
import com.iflytek.cloud.ui.RecognizerDialog;
import com.iflytek.cloud.ui.RecognizerDialogListener;
public class MainActivity extends AppCompatActivity {
private Button btn_voiceRec,btn_voiceRecOffline;
private RecognizerDialog iatDialog=null;
private SpeechRecognizer recognizer=null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SpeechUtility.createUtility(this, "appid=xxx");//替換為實際的appid
btn_voiceRec = (Button) findViewById(R.id.btn_voiceRec);
btn_voiceRec.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (iatDialog!=null){
iatDialog.cancel();
iatDialog.destroy();
}
startDialogOnline();
}
});
btn_voiceRecOffline= (Button) findViewById(R.id.btn_voiceRecOffline);
btn_voiceRecOffline.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// if (recognizer!=null){
// recognizer.cancel();
// recognizer.destroy();
// }
startNoDialogOffline();
}
});
}
/**
* 初始化監聽器。
*/
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
Log.d("tag", "SpeechRecognizer init() code = " + code);
if (code != ErrorCode.SUCCESS) {
Log.d("tag", "初始化失敗,錯誤碼:" + code);
}
}
};
public void startDialogOnline() {
//1.建立SpeechRecognizer物件,第二個引數:本地聽寫時傳InitListener
iatDialog = new RecognizerDialog(this, mInitListener);
//2.設定聽寫引數
iatDialog.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
iatDialog.setParameter(SpeechConstant.ACCENT, "mandarin ");
//3.設定回撥介面
iatDialog.setListener(new RecognizerDialogListener() {
@Override
public void onResult(RecognizerResult recognizerResult, boolean b) {
if (!b) {
String json = recognizerResult.getResultString();
String str = JsonParser.parseIatResult(json);
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onError(SpeechError speechError) {
Log.d("error", speechError.toString());
}
});
//4.開始聽寫
iatDialog.show();
}
private void startNoDialogOffline(){
//1.建立SpeechRecognizer物件,第二個引數:本地聽寫時傳InitListener
recognizer = SpeechRecognizer.createRecognizer(this,null);
//2.設定聽寫引數
recognizer.setParameter(SpeechConstant.ENGINE_TYPE,SpeechConstant.TYPE_LOCAL);//呼叫語記介面
recognizer.setParameter(SpeechConstant.DOMAIN, "iat");//引數設為語音聽寫
recognizer.setParameter(SpeechConstant.LANGUAGE, "zh_cn");//中文
recognizer.setParameter(SpeechConstant.ACCENT, "mandarin ");//普通話
//3.設定回撥介面
recognizer.startListening(new RecognizerListener() {
@Override
public void onVolumeChanged(int i, byte[] bytes) {
}
@Override
public void onBeginOfSpeech() {
}
@Override
public void onEndOfSpeech() {
}
@Override
public void onResult(RecognizerResult recognizerResult, boolean isLast) {
if (isLast) {
String json = recognizerResult.getResultString();
String str = JsonParser.parseIatResult(json);
Log.d("tag",str);
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onError(SpeechError speechError) {
Log.d("error", speechError.toString());
}
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
}
});
}
}
—————————————————————————————————
2016.11.2更新
離線語音做了個簡單的UI,效果如圖:
完整程式碼如下,改動部分已註釋:
package com.example.administrator.voicerecognition;
import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.Toast;
import com.iflytek.cloud.ErrorCode;
import com.iflytek.cloud.InitListener;
import com.iflytek.cloud.RecognizerListener;
import com.iflytek.cloud.RecognizerResult;
import com.iflytek.cloud.SpeechConstant;
import com.iflytek.cloud.SpeechError;
import com.iflytek.cloud.SpeechRecognizer;
import com.iflytek.cloud.SpeechUtility;
import com.iflytek.cloud.ui.RecognizerDialog;
import com.iflytek.cloud.ui.RecognizerDialogListener;
public class MainActivity extends AppCompatActivity {
private Button btn_voiceRec,btn_voiceRecOffline;
private RecognizerDialog iatDialog=null;
private SpeechRecognizer recognizer=null;
//新增對話方塊和語音聽寫完成標誌------------------
private ProgressDialog pDialog=null;
private boolean isFinish=false;
//-----------------------------------
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SpeechUtility.createUtility(this, "appid=xxx");//替換為實際的appid
btn_voiceRec = (Button) findViewById(R.id.btn_voiceRec);
btn_voiceRec.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (iatDialog!=null){
iatDialog.cancel();
iatDialog.destroy();
}
startDialogOnline();
}
});
btn_voiceRecOffline= (Button) findViewById(R.id.btn_voiceRecOffline);
btn_voiceRecOffline.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
// if (recognizer!=null){
// recognizer.cancel();
// recognizer.destroy();
// }
startNoDialogOffline();
//顯示對話方塊----------------
showProgressDialog();
//----------------------
}
});
}
//對話方塊顯示方法-----------------------------------
private void showProgressDialog() {
pDialog = new ProgressDialog(MainActivity.this);
pDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
pDialog.setProgress(100);
pDialog.setMessage("請稍等...");
pDialog.setIndeterminate(false);
pDialog.show();
WindowManager.LayoutParams lp = pDialog.getWindow().getAttributes();
lp.gravity = Gravity.CENTER;
Window win = pDialog.getWindow();
win.setAttributes(lp);
new Thread(new Runnable() {
@Override
public void run() {
//long startTime = System.currentTimeMillis();
int progress = 0;
//while (System.currentTimeMillis() - startTime < 1000) {
//語音聽寫完成後,釋放對話方塊
while (!isFinish) {
try {
progress += 10;
pDialog.setProgress(progress);
Thread.sleep(100);
} catch (InterruptedException e) {
pDialog.dismiss();
}
}
pDialog.dismiss();
isFinish=false;
}
}).start();
}
//--------------------------------------------------------
/**
* 初始化監聽器。
*/
private InitListener mInitListener = new InitListener() {
@Override
public void onInit(int code) {
Log.d("tag", "SpeechRecognizer init() code = " + code);
if (code != ErrorCode.SUCCESS) {
Log.d("tag", "初始化失敗,錯誤碼:" + code);
}
}
};
public void startDialogOnline() {
//1.建立SpeechRecognizer物件,第二個引數:本地聽寫時傳InitListener
iatDialog = new RecognizerDialog(this, mInitListener);
//2.設定聽寫引數
iatDialog.setParameter(SpeechConstant.LANGUAGE, "zh_cn");
iatDialog.setParameter(SpeechConstant.ACCENT, "mandarin ");
//3.設定回撥介面
iatDialog.setListener(new RecognizerDialogListener() {
@Override
public void onResult(RecognizerResult recognizerResult, boolean isLast) {
if (!isLast) {
String json = recognizerResult.getResultString();
String str = JsonParser.parseIatResult(json);
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
}
}
@Override
public void onError(SpeechError speechError) {
Log.d("error", speechError.toString());
}
});
//4.開始聽寫
iatDialog.show();
}
private void startNoDialogOffline(){
//1.建立SpeechRecognizer物件,第二個引數:本地聽寫時傳InitListener
recognizer = SpeechRecognizer.createRecognizer(this,null);
//2.設定聽寫引數
recognizer.setParameter(SpeechConstant.ENGINE_TYPE,SpeechConstant.TYPE_LOCAL);//呼叫語記介面
recognizer.setParameter(SpeechConstant.DOMAIN, "iat");//引數設為語音聽寫
recognizer.setParameter(SpeechConstant.LANGUAGE, "zh_cn");//中文
recognizer.setParameter(SpeechConstant.ACCENT, "mandarin ");//普通話
//3.設定回撥介面
recognizer.startListening(new RecognizerListener() {
@Override
public void onVolumeChanged(int i, byte[] bytes) {
}
@Override
public void onBeginOfSpeech() {
}
@Override
public void onEndOfSpeech() {
}
@Override
public void onResult(RecognizerResult recognizerResult, boolean isLast) {
if (isLast) {
String json = recognizerResult.getResultString();
String str = JsonParser.parseIatResult(json);
Log.d("tag",str);
Toast.makeText(MainActivity.this, str, Toast.LENGTH_SHORT).show();
//語音聽寫完成標誌---------------------------
isFinish=true;
//------------------------------------------
}
}
@Override
public void onError(SpeechError speechError) {
Log.d("error", speechError.toString());
}
@Override
public void onEvent(int i, int i1, int i2, Bundle bundle) {
}
});
}
}
注:經過多次測試後發現,第一次點選按鈕離線語音聽寫無法識別,第二次點選按鈕才可正常實現聽寫,之後都可以正常聽寫,這個bug目前還沒有解決
—————————————————————————————————
2016.11.6 更新
第一次離線語音聽寫無反應bug的解決辦法:在onCreat()裡先執行一次,不顯示UI,這樣再點選就是第二次了。。。雖然很蠢,但是功能上算是實現了,後面有時間再仔細想想更好的辦法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
SpeechUtility.createUtility(this, "appid=xxx");//替換為實際的appid
btn_voiceRec = (Button) findViewById(R.id.btn_voiceRec);
btn_voiceRec.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startDialogOnline();
}
});
btn_voiceRecOffline = (Button) findViewById(R.id.btn_voiceRecOffline);
//先聽寫一次----------------------------
startNoDialogOffline();
//------------------------------------
btn_voiceRecOffline.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
startNoDialogOffline();
showProgressDialog();
}
});
}
注:除錯過程中又發現,在單獨使用線上的情況下,線上的RecognizerDialogListener的onResult()會呼叫兩次,輸出兩個str,第一個是聽寫內容,第二個是標點符號。如果是if (!isLast),那麼輸出的是聽寫內容,如果是if (isLast),輸出的是標點符號。很顯然,需要的是聽寫內容,這時要用if (!isLast)。
如果和離線的混用,就像上面一樣,在onCreat()裡先執行了一次startNoDialogOffline(),那麼經測試RecognizerDialogListener的onResult()只會呼叫一次,輸出一個str,聽寫內容和標點符號連在一起的(具體原因不清楚),這時候如果是if (!isLast),就不會有輸出了。所以這裡要用if (isLast)。
單獨使用離線的情況下,RecognizerListener的onResult()是隻會呼叫一次,輸出一個str。這時候如果是if (!isLast),就不會有輸出了。所以這裡要用if (isLast)。
所以說線上的要和離線的分開使用,本文只是個例子,實際使用過程中也不會同時使用。簡單來說,線上用if (!isLast)判斷,離線用if (isLast)判斷