見出し画像

Androidアプリ開発入門 (3) - SpeechRecognizer

Androidアプリでの「SpeechRecognizer」による「音声認識」の実装方法をまとめました。

・API 31: Android 12 (S)

前回

1. SpeechRecognizer

カスタムUIを使った「音声認識」を実装するには、「SpeechRecognizer」で「RecognizerIntent」を実行して使います。このクラスのメソッドは、すべてメインスレッドから呼ぶ必要があります。

2. AndroidManifest.xml

AndroidManifest.xml」に以下の項目を設定します。

・android.permission.RECORD_AUDIO : マイクのパーミッション
<uses-permission android:name="android.permission.RECORD_AUDIO" />

さらに、Android 11(API Level 30)を対象とするアプリの場合、音声認識を利用するには、以下の要素を追加する必要があります。

<queries>
  <intent>
    <action android:name="android.speech.RecognitionService" />
  </intent>
</queries>

ユーザー権限の許可には「PermissionsDispatcher」を使っています。

3. UI

前回と同様です。

4. カスタムUIの音声認識

コードは、以下のように記述します。

package net.npaka.speechrecognizerex
import android.Manifest
import android.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import android.widget.Toast
import android.speech.SpeechRecognizer
import android.speech.RecognitionListener
import android.speech.RecognizerIntent
import android.content.Intent
import android.view.View
import android.widget.Button
import java.util.*
import permissions.dispatcher.*

@RuntimePermissions
class MainActivity : AppCompatActivity(), View.OnClickListener {
    // 音声認識
    private var recognizer: SpeechRecognizer? = null

    // 参照
    private var label: TextView? = null
    private var button: Button? = null


//====================
// ライフサイクル
//====================
    // 起動時に呼ばれる
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        this.supportActionBar?.hide()

        // 参照
        this.label = findViewById(R.id.label)
        this.label!!.setText("")
        this.button = findViewById(R.id.button)
        this.button!!.setOnClickListener(this)
        setUiActive(false)

        // 音声認識の開始
        setupRecognizerWithPermissionCheck()
    }


//====================
// 操作
//====================
    // UIの有効化・無効化
    private fun setUiActive(active: Boolean) {
        runOnUiThread {
            this.button!!.isEnabled = active
        }
    }

    // テキストの指定
    private fun setText(text: String) {
        runOnUiThread {
            this.label!!.setText(text)
        }
    }

    // クリック時に呼ばれる
    override fun onClick(v: View) {
        // 音声認識の開始
        startRecognizer()
    }


//====================
// パーミッション
//====================
    // 許可された時に呼ばれる
    @NeedsPermission(Manifest.permission.RECORD_AUDIO)
    fun setupRecognizer() {
        _setupRecognizer()
        setUiActive(true)
    }

    // 説明が必要な時に呼ばれる
    @OnShowRationale(Manifest.permission.RECORD_AUDIO)
    fun onCameraShowRationale(request: PermissionRequest) {
        AlertDialog.Builder(this)
            .setPositiveButton("許可") { _, _ -> request.proceed() }
            .setNegativeButton("許可しない") { _, _ -> request.cancel() }
            .setCancelable(false)
            .setMessage("マイクを利用します")
            .show()
    }

    // 拒否された時に呼ばれる
    @OnPermissionDenied(Manifest.permission.RECORD_AUDIO)
    fun onCameraPermissionDenied() {
        Toast.makeText(this, "拒否されました", Toast.LENGTH_SHORT).show()
    }

    // 「今後表示しない」が選択された時に呼ばれる
    @OnNeverAskAgain(Manifest.permission.RECORD_AUDIO)
    fun onCameraNeverAskAgain() {
       Toast.makeText(this, "「今後表示しない」が選択されました", Toast.LENGTH_SHORT).show()
    }


//====================
// 音声認識
//====================
    // 音声認識の初期化
    private fun _setupRecognizer() {
        // SpeechRecognizerの生成
        this.recognizer = SpeechRecognizer.createSpeechRecognizer(this)
        this.recognizer!!.setRecognitionListener(object : RecognitionListener {
            // 部分認識結果の取得時に呼ばれる
            override fun onPartialResults(results: Bundle) {
                // 認識テキストの取得
                val recData = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
                if (recData!!.size > 0) {
                    setText(recData[0])
                }
            }

            // 認識結果の取得時に呼ばれる
            override fun onResults(results: Bundle) {
                // 認識テキストの取得
                val recData = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
                if (recData!!.size > 0) {
                    setText(recData[0])
                }

                // UIの有効化
                setUiActive(true)
            }

            // エラー時に呼ばれる
            override fun onError(error: Int) {
                // エラー表示
                setText(errorCode2message(error))

                // UIの有効化
                setUiActive(true)
            }

            // その他
            override fun onReadyForSpeech(params: Bundle) {}
            override fun onBeginningOfSpeech() {}
            override fun onEndOfSpeech() {}
            override fun onBufferReceived(buffer: ByteArray) {}
            override fun onRmsChanged(rmsdB: Float) {}
            override fun onEvent(eventType: Int, params: Bundle) {}
        })
    }

    // 音声認識の開始
    private fun startRecognizer() {
        // UIの無効化
        setUiActive(false)

        // RecognizerIntentの生成
        var intent: Intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)

        // スピーチの傾聴の開始
        this.recognizer!!.startListening(intent)
    }

    // エラーコード → メッセージ
    private fun errorCode2message(errorCode: Int): String {
        when (errorCode) {
            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"
        }
        return "Unknown error"
    }
}

音声認識ボタンを押した後、発話すると、リアルタイムに文字起こしされます。

画像1

5. 音声認識が利用可能かどうかの確認

音声認識が利用可能かどうかは、以下のメソッドで確認します。

・static boolean isRecognitionAvailable(Context context)
デフォルトの音声認識サービスが利用可能かどうか。

・static boolean isOnDeviceRecognitionAvailable(Context context)【API Level 31】

オンデバイスの音声認識サービスが利用可能かどうか。

6. SpeechRecognizerの生成

「SpeechRecognizer」を生成するには、以下のメソッドを利用します。

・static SpeechRecognizer createSpeechRecognizer(Context context)
デフォルトの音声認識サービスを利用するSpeechRecognizerを生成します。
・static SpeechRecognizer createOnDeviceSpeechRecognizer(Context context)【API Level 31】
オンデバイスの音声認識サービスを利用するSpeechRecognizerを生成します。

7. SpeechRecognizerのリスナーの設定

「SpeechRecognizer」のリスナー(RecognitionListener)を設定するには、setRecognitionListener()を使います。

リスナーのコールバックは、次のとおりです。すべてメインスレッドで実行されます。

・void onReadyForSpeech(Bundle params)
スピーチの傾聴の準備完了時に呼ばれる。

・void onBeginningOfSpeech()

スピーチの傾聴の開始時に呼ばれる。

・void onPartialResults(Bundle partialResults)
部分認識結果の受信時に呼ばれる。

・void onEndOfSpeech()
スピーチの傾聴の終了時に呼ばれる。

・void onResults(Bundle results)
音声認識結果の受信時に呼ばれる。

・void onError(int error)
エラー時に呼ばれる。

・void onBufferReceived(byte[] buffer)
音の受信時に呼ばれる。引数は単一チャネルのオーディオストリームを表すバッファです。
(big-endian 16-bit integers、サンプルレートは実装依存)

・void onEvent(int eventType, Bundle params)
イベント受信時に呼ばれる。

・void onRmsChanged(float rmsdB)
サウンドレベルの変更時に呼ばれる。

◎ 音声認識結果
onResults()とonPartialResults()に渡されるBundleから音声認識結果を取得するためのキーは、次のとおりです。

・RESULTS_RECOGNITION
ArrayList<String>で音声認識結果のテキストを取得。

・CONFIDENCE_SCORES
float[]で音声認識結果の信頼スコアを取得。

◎ エラーコード
onError()に渡されるエラーコードは、次のとおりです。

・ERROR_AUDIO : 録音エラー。
・ERROR_CLIENT : その他のクライアント側のエラー。
・ERROR_INSUFFICIENT_PERMISSIONS : 権限が不十分。
・ERROR_LANGUAGE_NOT_SUPPORTED : 要求された言語を使用できない。
・ERROR_LANGUAGE_UNAVAILABLE : 要求された言語はサポートされているが、現在利用できない(ダウンロードされていないなど)。
・ERROR_NETWORK : その他のネットワーク関連のエラー。
・ERROR_NETWORK_TIMEOUT : ネットワーク操作のタイムアウト。
・ERROR_NO_MATCH : 一致する認識結果はない。
・ERROR_RECOGNIZER_BUSY : RecognitionServiceがビジー。
・ERROR_SERVER : サーバーエラー。エラーステータスを送信。
・ERROR_SERVER_DISCONNECTED : サーバーが切断。
・ERROR_SPEECH_TIMEOUT : 音声入力なし。
・ERROR_TOO_MANY_REQUESTS : 同じクライアントからのリクエストが多すぎる。

8. スピーチの傾聴の開始と停止

音声認識の開始と停止には、以下のメソッドを利用します。

・void startListening(Intent recognizerIntent)
スピーチの傾聴を開始します。

・void stopListening()
スピーチの傾聴を停止します。音声が完了したと判断した時に、自動停止するため、通常は呼ぶ必要ありません。

・void cancel()
スピーチの傾聴をキャンセルします。

次回



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