在一般場景下辨識語音時,大多會跳出聆聽中的對話框,不過在最近的專案需求中,因畫面上需要顯示即時訊息,所以有了在做語音辨識時,不要有任何系統畫面影響APP本身的畫面的需求。
為了達到這項需求,我們需要使用SpeechRecognizer
來自行呼叫聆聽並接收辨識結果。
1.首先要在manifests中登記錄音與網路權限,畢竟要使用到麥克風與讓google辨識~
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.RECORD_AUDIO"/>
然後在Android11以上,需要在application
外面增加queries
標籤。
... ... <queries> <intent> <action android:name="android.speech.RecognitionService" /> </intent> </queries> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" ... ...
2.然後回到主程式中,首先要先確認/取得權限:
要求權限:
ActivityCompat.requestPermissions(Test12Activity.this, new String[]{Manifest.permission.RECORD_AUDIO}, REQUEST_RECORD_PERMISSION);
檢查要求回傳狀態:
private static final int REQUEST_RECORD_PERMISSION = 100; @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case REQUEST_RECORD_PERMISSION: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { //權限已取得 } else { Toast.makeText(this, "Permission Denied!", Toast.LENGTH_SHORT).show(); } } }
3.建立SpeechRecognizer
物件:
首先在class上implements RecognitionListener
:
public class Test12Activity extends AppCompatActivity implements RecognitionListener
implements
後,IDE會哀哀叫要我們把方法補齊:
@Override public void onReadyForSpeech(Bundle bundle) { Log.i(LOG_TAG, "onReadyForSpeech"); } @Override public void onBeginningOfSpeech() { Log.i(LOG_TAG, "onBeginningOfSpeech"); } @Override public void onRmsChanged(float rmsdB) { Log.i(LOG_TAG, "onRmsChanged: " + rmsdB); } @Override public void onBufferReceived(byte[] bytes) { Log.i(LOG_TAG, "onBufferReceived: " + bytes); } @Override public void onEndOfSpeech() { Log.i(LOG_TAG, "onEndOfSpeech"); } @Override public void onError(int i) { String errorMessage = getErrorText(i); Log.d(LOG_TAG, "FAILED " + errorMessage); tvResult.setText(errorMessage); } @Override public void onResults(Bundle results) { Log.i(LOG_TAG, "onResults"); ArrayList<String> matches = results .getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION); String text = ""; for (String result : matches) text += result + "\n"; Log.i(LOG_TAG, "顯示文字: " + text); tvResult.setText(text); } @Override public void onPartialResults(Bundle bundle) { Log.i(LOG_TAG, "onPartialResults"); } @Override public void onEvent(int i, Bundle bundle) { Log.i(LOG_TAG, "onEvent"); } public static String getErrorText(int errorCode) { String message; switch (errorCode) { case SpeechRecognizer.ERROR_AUDIO: message = "Audio recording error"; break; case SpeechRecognizer.ERROR_CLIENT: message = "Client side error"; break; case SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS: message = "Insufficient permissions"; break; case SpeechRecognizer.ERROR_NETWORK: message = "Network error"; break; case SpeechRecognizer.ERROR_NETWORK_TIMEOUT: message = "Network timeout"; break; case SpeechRecognizer.ERROR_NO_MATCH: message = "No match"; break; case SpeechRecognizer.ERROR_RECOGNIZER_BUSY: message = "RecognitionService busy"; break; case SpeechRecognizer.ERROR_SERVER: message = "error from server"; break; case SpeechRecognizer.ERROR_SPEECH_TIMEOUT: message = "No speech input"; break; default: message = "Didn't understand, please try again."; break; } return message; }
看起來很多行,其實就是語音辨識過程中的各種狀態回傳而已,讓我們可以根據各種狀況去做對應的處理。
其中tvResult
為畫面上的TextView
,把訊息顯示出來。
onRmsChanged為目前的音量大小,可以做成動畫用(例如與progressBar
串接變成一個指示器)。
最重要的onResults
為辨識結果,在本例中會得到3個結果,依照google認為的機率排序。
再來是把SpeechRecognizer
初始化的部分完成:
private SpeechRecognizer speech = null; private Intent recognizerIntent; private String LOG_TAG = "Test12Activity"; speech = SpeechRecognizer.createSpeechRecognizer(this); Log.i(LOG_TAG, "isRecognitionAvailable: " + SpeechRecognizer.isRecognitionAvailable(this));//語音辨識是否可用 speech.setRecognitionListener(this); recognizerIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_PREFERENCE, "zh-TW");//收聽語言 recognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM); recognizerIntent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 3);//最大結果數量 recognizerIntent.putExtra(RecognizerIntent.EXTRA_SPEECH_INPUT_MINIMUM_LENGTH_MILLIS, 5000);//最短收聽時間
注意:在測試過程中,有遇到SpeechRecognizer.isRecognitionAvailable(this)會有false的狀況,後來調查後發現是Google語音服務的問題,因為我的測試機是刷別的rom,沒有安裝Google助理。在安裝完Google助理後(會一併安裝Google App與語音搜尋),就會得到可使用的狀態了。所以使用特殊機種(如大陸那邊賣的),有可能會無法使用,所以最好加上判斷並通知使用者。
4.開始語音辨識:
在想要的地方開始執行語音辨識(這邊為按下按鈕):
btnStart.setOnClickListener(v -> { speech.startListening(recognizerIntent); });
呼叫speech.startListening()
即可,把上方建立的Intent設定訊息帶入。
如果要停止辨識,就用speech.stopListening()
即可。
5.釋放資源:
在整個程式執行完成後,別忘記釋放資源:
@Override protected void onStop() { super.onStop(); if (speech != null) { speech.destroy(); Log.i(LOG_TAG, "destroy"); } }
到這邊就是一個無畫面的語音辨識流程了,這種做法可讓整的辨識流程比較可控制,與APP會更好搭配。
-END-
發佈留言