Androidで音声認識のアプリを作成できないか検討しています。
RecognizerIntentで、音声を認識したら画面の文字が認識した文字に代わるというサンプルアプリを作成したのですが、音声の認識がまともに行えていません。
以下のようなコードで実装しています。
AndroidManifest
1<?xml version="1.0" encoding="utf-8"?> 2<manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="jp.co.appnet.speechrecogsample"> 4 <uses-permission android:name="android.permission.RECORD_AUDIO" /> 5 <uses-permission android:name="android.permission.INTERNET"/> 6. 7. 8.
Kotlin
1import android.Manifest 2import android.content.Intent 3import android.content.pm.PackageManager 4import android.os.Bundle 5import android.os.Handler 6import android.speech.RecognizerIntent 7import android.speech.SpeechRecognizer 8import android.widget.Toast 9import androidx.appcompat.app.AppCompatActivity 10import androidx.core.app.ActivityCompat 11import kotlinx.android.synthetic.main.activity_main.* 12import java.util.* 13 14 15class MainActivity : AppCompatActivity() { 16 17 private var recognizer: SpeechRecognizer? = null 18 private var recognizeActive = false 19 20 override fun onCreate(savedInstanceState: Bundle?) { 21 super.onCreate(savedInstanceState) 22 setContentView(R.layout.activity_main) 23 24 initRecognizer() 25 26 tv_result.text = "ここに結果が出る予定" 27 28 val permission = Manifest.permission.RECORD_AUDIO 29 if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED){ 30 ActivityCompat.requestPermissions(this, arrayOf(permission), 0) 31 return 32 } 33 34 } 35 36 override fun onResume() { 37 super.onResume() 38 startRecognizer() 39 } 40 41 override fun onPause() { 42 super.onPause() 43 } 44 45 override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { 46 super.onRequestPermissionsResult(requestCode, permissions, grantResults) 47 val result = grantResults.contentToString() 48 if (result == "[0]" || result == "[-1]") { 49 startRecognizer() 50 } 51 } 52 53 private var recognizerIntent: Intent? = null 54 55 //初期化 56 private fun initRecognizer() { 57 recognizerIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) 58 recognizerIntent?.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM) 59 recognizerIntent?.putExtra(RecognizerIntent.EXTRA_PREFER_OFFLINE, true) 60 recognizerIntent?.putExtra(RecognizerIntent.EXTRA_LANGUAGE, Locale.JAPANESE.toString()) 61 62 recognizer = SpeechRecognizer.createSpeechRecognizer(this) 63 recognizer!!.setRecognitionListener(RecognitionListener()) 64 } 65 66 private fun startRecognizer() { 67 println("startRecognizer") 68 recognizer!!.startListening(recognizerIntent) 69 } 70 71 private inner class RecognitionListener : android.speech.RecognitionListener { 72 73 override fun onResults(results: Bundle?) { 74 75 //ここで結果 76 val recData = results!!.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION) 77 println(recData) 78 if(recData!!.size > 0) { 79 if (recData[0] != "") { 80 var recordtxt = "" 81 for(record in recData) { 82 recordtxt += record + "\n" 83 } 84 tv_result.text = recordtxt 85 } 86 } 87 recognizeActive = false 88 89 try { Thread.sleep(1000) } catch (e: Exception) { } 90 startRecognizer() 91 } 92 93 override fun onReadyForSpeech(params: Bundle?) { 94 println("onReadyForSpeech") 95 recognizeActive = true 96 } 97 98 override fun onError(error: Int) { 99 val errorText = when(error) { 100 SpeechRecognizer.ERROR_AUDIO -> "オーディオ録音エラー。" 101 SpeechRecognizer.ERROR_CLIENT -> "クライアントサイドエラー" 102 SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> "権限不足エラー" 103 SpeechRecognizer.ERROR_NETWORK -> "通信エラー" 104 SpeechRecognizer.ERROR_NETWORK_TIMEOUT -> "通信タイムアウト" 105 SpeechRecognizer.ERROR_NO_MATCH -> "認識失敗エラー" 106 SpeechRecognizer.ERROR_RECOGNIZER_BUSY -> "ビジー" 107 SpeechRecognizer.ERROR_SERVER -> "サーバーエラー" 108 SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> "スピーチタイムアウト" 109 else -> "その他のエラー" 110 } 111 Toast.makeText(applicationContext, errorText, Toast.LENGTH_SHORT).show() 112 println(errorText) 113 114 try { Thread.sleep(1000) } catch (e: Exception) { } 115 startRecognizer() 116 } 117 118 override fun onBeginningOfSpeech() {} 119 override fun onEndOfSpeech() {} 120 override fun onBufferReceived(buffer: ByteArray?) {} 121 override fun onEvent(eventType: Int, params: Bundle?) {} 122 override fun onPartialResults(partialResults: Bundle?) {} 123 override fun onRmsChanged(rmsdB: Float) {} 124 125 } 126
gradle
1apply plugin: 'com.android.application' 2apply plugin: 'kotlin-android' 3apply plugin: 'kotlin-android-extensions' 4 5android { 6 compileSdkVersion 29 7 buildToolsVersion "29.0.3" 8 9 defaultConfig { 10 applicationId "jp.co.appnet.speechrecogsample" 11 minSdkVersion 28 12 targetSdkVersion 29 13 versionCode 1 14 versionName "1.0" 15 16 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 } 18 19 buildTypes { 20 release { 21 minifyEnabled false 22 proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 23 } 24 } 25} 26 27dependencies { 28 implementation fileTree(dir: "libs", include: ["*.jar"]) 29 implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" 30 implementation 'androidx.core:core-ktx:1.3.0' 31 implementation 'androidx.appcompat:appcompat:1.1.0' 32 implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 33 testImplementation 'junit:junit:4.12' 34 androidTestImplementation 'androidx.test.ext:junit:1.1.1' 35 androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 36 37}
実機で実行し端末に向かって発話してみても、延々と以下のようなログが出力されます。
I/System.out: onReadyForSpeech I/System.out: スピーチタイムアウト I/System.out: startRecognizer I/System.out: onReadyForSpeech I/System.out: スピーチタイムアウト I/System.out: startRecognizer I/System.out: onReadyForSpeech I/System.out: スピーチタイムアウト I/System.out: startRecognizer . . .
たまに結果が返ってきますが、ひらがな一文字だったりと極端に短かったり意図しない言葉だったりします。また、何もしゃべっていないのに文字が返ってきたりします。
タブレットのマイクが悪いのかと最初は疑い、グーグル検索で音声認識を試してみたら、小声での発話でも認識しました。
認識が上手く行かないのは、コードの書き方に致命的な箇所があるからなのでしょうか。
どなたか知見をお持ちの方がいらっしゃいましたら、ご教示頂けると助かります。
開発環境
Windows 10
Android Studio 4.0
テスト実機のAPIレベル 28
あなたの回答
tips
プレビュー