【Android】音声認識機能修正

動機

音声認識機能を真ん中にダイアローグが出てくるよりはテキストの下に画像が出てそこで音声認識するように修正しました。

またパーミッションを許可するダイヤログを追加しました。

始めましょう

1.AndroidManifest.xml 

<uses-permission android:name="android.permission.RECORD_AUDIO" />

RECORD_AUDIO パーミッションを追加します。

2.ChatFragment_パーミッション

private const val lang = 0

class ChatFragment : Fragment(R.layout.chat_fragment) {
    // パーミッション
    private val askMultiplePermissions =
       registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { map ->
           for (entry in map.entries) {
               debugPrint { "askMultiplePermissions: \"${entry.key} = ${entry.value}\"" }
           }
       }

   // 音声認識
   private lateinit var recognizer: SpeechRecognizer
   private var recognizeActive = false

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    ...
       useBinding.chatMicImageView.setOnClickListener {
               // パーミッションのチェック
               checkPermission()
           }
    }
...
}
 // パーミッションのチェック
   private fun checkPermission() {
       if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.RECORD_AUDIO)
           != PackageManager.PERMISSION_GRANTED
       ) {
           // 権限リクエスト
           requestPermissions()
       } else {
           // 音声認識の開始
           speech()
       }
   }
  /**
    * 権限リクエスト
    */
   private fun requestPermissions() {
       val permissions = arrayOf(
           Manifest.permission.RECORD_AUDIO
       )
       askMultiplePermissions.launch(permissions)
   }

パーミッションをチェックします。
パーミッションを許可すると、音声認識を準備します。

画像1

3. speech

/**
    * 音声認識setting
    */
   private fun speech() {
       val speechIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH).also {
           it.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 100)
       }
       when (lang) {
           0 -> {
               // 日本語
               speechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.JAPAN.toString())
           }
           1 -> {
               // 英語
               speechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.ENGLISH.toString())
           }
           2 -> {
               // Off line mode
               speechIntent.putExtra(RecognizerIntent.EXTRA_PREFER_OFFLINE, true)
           }
           else -> {
               speechIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
           }
       }
       try {
           initRecognizer(speechIntent)
       } catch (e: ActivityNotFoundException) {
           Toast.makeText(context, e.message, Toast.LENGTH_SHORT).show()
           e.printStackTrace()
       }
   }

前回と同様にRecognizerIntentに言語を設定し、これを送信します。

4. initRecognizer

// 音声認識の初期化
   private fun initRecognizer(intent: Intent) {
       // SpeechRecognizerの生成
       recognizer = SpeechRecognizer.createSpeechRecognizer(requireContext())
       recognizer.setRecognitionListener(object : RecognitionListener {
           // ユーザーの話を聞く準備ができた時に呼ばれる
           override fun onReadyForSpeech(params: Bundle) {
               // onResultsが2回呼ばれる不具合の対応
               recognizeActive = true
               // TODO 音声認識イメージ表示
               useBinding {
                   it.dummyImg.visibility = View.VISIBLE
               }
           }
           override fun onBeginningOfSpeech() {
               // TODO 音声認識イメージ表示 (話し中)
               useBinding {
                   it.dummyImg.visibility = View.VISIBLE
               }
           }
           override fun onEndOfSpeech() {
               // TODO 表示された音声認識イメージ表示しないように
               useBinding {
                   it.dummyImg.visibility = View.GONE
               }
           }
           // 認識結果の取得時に呼ばれる
           override fun onResults(results: Bundle) {
               // onResultsが2回呼ばれる不具合の対応
               if (!recognizeActive) return
               // 認識結果を取得
               results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)?.let { candidates ->
                   useBinding {
                       // 認識結果の候補の中で、最も有力なものを設定
                       it.chatEditText.setText(candidates[0], TextView.BufferType.NORMAL)
                   }
               }
               recognizeActive = false
           }
           // エラー時に呼ばれる
           override fun onError(error: Int) {
               // エラー表示
               Toast.makeText(requireContext(), errorToStr(error), Toast.LENGTH_SHORT).show()
               // スリープ(これがないと次の音声認識に失敗することがある)
               try {
                   Thread.sleep(1000)
               } catch (e: Exception) {
               }
               recognizeActive = false
               onEndOfSpeech()
           }
           // その他
           override fun onBufferReceived(buffer: ByteArray) {}
           override fun onPartialResults(results: Bundle) {}
           override fun onRmsChanged(rmsdB: Float) {}
           override fun onEvent(eventType: Int, params: Bundle) {}
       })
       recognizer.startListening(intent)
   }

SpeechRecognizerを生成し、 setRecognitionListenerを通じて
ユーザーの話を聞く準備ができた時に呼ばれる時(onReadyForSpeech)、話をしている時(onBeginningOfSpeech)、話が終わった時(onEndOfSpeech)などを処理します。

ここで画像を処理して、ダイヤログではなく画像に音声が認識されるようにしました。

onResultsに以前と同様に認識された音声をテキストに適用します。

onErrorには音声認識途中のエラーを処理します。

5. +)errorToStr

// エラー → 文字列
   private fun errorToStr(error: Int): String {
       when (error) {
           SpeechRecognizer.ERROR_AUDIO -> {
               return "ERROR_AUDIO"
           }
           SpeechRecognizer.ERROR_CLIENT -> {
               return "ERROR_CLIENT"
           }
           SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> {
               return "ERROR_INSUFFICIENT_PERMISSIONS"
           }
           SpeechRecognizer.ERROR_NETWORK -> {
               return "ERROR_NETWORK"
           }
           SpeechRecognizer.ERROR_NETWORK_TIMEOUT -> {
               return "ERROR_NETWORK_TIMEOUT"
           }
           SpeechRecognizer.ERROR_NO_MATCH -> {
               return "ERROR_NO_MATCH"
           }
           SpeechRecognizer.ERROR_RECOGNIZER_BUSY -> {
               return "ERROR_RECOGNIZER_BUSY"
           }
           SpeechRecognizer.ERROR_SERVER -> {
               return "ERROR_SERVER"
           }
           SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> {
               return "ERROR_SPEECH_TIMEOUT"
           }
           else -> return "ERROR"
       }
   }

上記のエラーをテキストで表現するメソッドです。

6. 結果

画像2





この記事が気に入ったらサポートをしてみませんか?