用鍵盤輸入訊息,對年輕人或許稀鬆平常,但對長者而言,使用語音的方式或許更輕鬆。所以除了畫面字體放大外,我們也使用語音識別與輸出技術,提供長者更友善的工具。
上次的文章 [Zenbo開發系列] 06-安裝DDE語料到Zenbo 有講到 DDE 的問題和使用 Android 語音識別與輸出的原因。那麼,今天就要來介紹 Android 這邊的實作囉!
STT 語音轉文字
STT (Speach to Text)
完整程式碼: https://gitlab.com/graduate_lab415/chatbot/-/blob/master/app/src/main/java/com/cmrdb/app/chatbot/view/ChatActivity.java
語音轉文字的功能用在左下的麥克風按鈕,點擊之後會有個對話框請使用者說出問句。

我們讓麥克風按鈕被點擊後,執行一個 Intent。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
   | ImageButton btnSpeak = findViewById(R.id.speak); btnSpeak.setOnClickListener(new View.OnClickListener() {     @Override     public void onClick(View v) {         Intent sttIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);         sttIntent.putExtra(                 RecognizerIntent.EXTRA_LANGUAGE_MODEL,                 RecognizerIntent.LANGUAGE_MODEL_FREE_FORM         );         sttIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.getDefault());         sttIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.start_to_speak));
          try {             startActivityForResult(sttIntent, REQUEST_CODE_STT);         } catch (ActivityNotFoundException e) {             Log.d(TAG, "onClick: " + e.getLocalizedMessage());             Toast.makeText(ChatActivity.this, "Your device does not support STT.", Toast.LENGTH_LONG).show();         }     } });
   | 
 
之後我們就能在 onActivityResult 取得語音辨識的結果,並把文字顯示到 EditText 上。
這樣設計的原因是想解決 DDE 常辨識不到文字的狀況,使用者可以確認語音識別的內容是否正確,再點選送出紐。
1 2 3 4 5 6 7 8 9 10 11 12 13
   | @Override protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {     super.onActivityResult(requestCode, resultCode, data);     if (requestCode == REQUEST_CODE_STT) {         if (resultCode == Activity.RESULT_OK && data != null) {             ArrayList<String> result = data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);             if (result != null) {                 String recognizedText = result.get(0);                 mUserInput.setText(recognizedText);             }         }     } }
   | 
 
TTS 文字轉語音
TTS (Text to Speach)
我是在 ViewModel 的呼叫使用 TTSSpeaker。
ChatViewModel
完整程式碼: https://gitlab.com/graduate_lab415/chatbot/-/blob/master/app/src/main/java/com/cmrdb/app/chatbot/viewmodel/ChatViewModel.java
這邊參數需要傳兩個 Callback 給 TTSSpeaker。
- 第一個 
TTSCallback 是我自己寫的,主要用來確認 TTS 初始化是否成功,onReady 才能執行將句子唸出來的動作。 
- 第二個 
UtteranceProgressListener 是要確認 TTS 目前的狀況,onStart(開始)、onDone(唸完)、onError(錯誤)。 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
   | private void speak(String message) {     TTSCallback ttsCallback = new TTSCallback() {         @Override         public void onReady() {             mSpeaker.speak(message);         }
          @Override         public void onFail() {             Log.d(TAG, "ttsCallback onFail");         }     };     UtteranceProgressListener speakingStatus = new UtteranceProgressListener() {         @Override         public void onStart(String utteranceId) {             Log.d(TAG, "speakingStatus onStart");         }
          @Override         public void onDone(String utteranceId) {             Log.d(TAG, "speakingStatus onDone");             mSpeaker.destroy();         }
          @Override         public void onError(String utteranceId) {             Log.d(TAG, "speakingStatus onError");             mSpeaker.destroy();         }     };
      mSpeaker = new TTSSpeaker(mApplication, ttsCallback, speakingStatus); }
  | 
 
TTSSpeaker
完整程式碼: https://gitlab.com/graduate_lab415/chatbot/-/blob/master/app/src/main/java/com/cmrdb/app/chatbot/controller/TTSSpeaker.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
   | public class TTSSpeaker {     private TextToSpeech mTTS;     private TTSCallback mCallback;     private Application mApplication;
      public TTSSpeaker(Application application, TTSCallback callback, UtteranceProgressListener utteranceProgressListener) {         mApplication = application;         mCallback = callback;         mTTS = new TextToSpeech(mApplication, new TextToSpeech.OnInitListener() {             @Override             public void onInit(int status) {                 if (status == TextToSpeech.SUCCESS) {                     int result = mTTS.setLanguage(Locale.TAIWAN);                         if (result == TextToSpeech.LANG_MISSING_DATA                             || result == TextToSpeech.LANG_NOT_SUPPORTED) {                         Log.e("TTS", "This Language is not supported");                     } else {                         mTTS.setPitch(1);                             mTTS.setSpeechRate(1);                         }                     mTTS.setOnUtteranceProgressListener(utteranceProgressListener);                     mCallback.onReady();                 } else {                     Log.e("TTS", "Initialization Failed!");                     mCallback.onFail();                 }             }
          });     }
      public void speak(String message) {         mTTS.speak(message, TextToSpeech.QUEUE_FLUSH, null, "tts1");
      }
      public void destroy() {             mTTS.shutdown();     } }
  | 
 
Demo