【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)
}
パーミッションをチェックします。
パーミッションを許可すると、音声認識を準備します。
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"
}
}
上記のエラーをテキストで表現するメソッドです。