[Android] Speech Recognition(음성 인식), Text to Speech(문자열 음성 출력) 기능 샘플
[샘플 빌드 환경]
- Android Studio Electric Eel 2022.1.1 Patch 2
- Android TargetSDK 33
- Android MinSDK 21
- Gradle-7.5
- 샘플앱 버전 : 1.1
- 샘플앱 Git : https://github.com/mystic707/SpeechTextRecognitionSample
1. 음성 인식 기능
private lateinit var speechRecognizer: SpeechRecognizer
private var resultListener: SampleDataManager.RecognitionResultListener? = null
fun listenSpeech() {
// Intent 구성
var intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, MainActivity.context.packageName)
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "ko-KR") // 한국어 설정
// speechRecognizer 초기화 및 음성 인식 기능 시작
speechRecognizer = SpeechRecognizer.createSpeechRecognizer(MainActivity.context)
speechRecognizer.setRecognitionListener(recogListener)
speechRecognizer.startListening(intent)
}
private val recogListener: RecognitionListener = object : RecognitionListener {
override fun onReadyForSpeech(params: Bundle?) {
// 말하기 시작할 준비가 되었을 때
Toast.makeText(MainActivity.context, "음성인식 시작", Toast.LENGTH_SHORT).show()
}
override fun onBeginningOfSpeech() {
// 말하기 시작 시
Toast.makeText(MainActivity.context, "듣는중", Toast.LENGTH_SHORT).show()
}
override fun onRmsChanged(rmsdB: Float) {
// 입력받는 소리의 크기
}
override fun onBufferReceived(buffer: ByteArray?) {
// 말을 시작하고 인식된 단어를 반환
}
override fun onEndOfSpeech() {
// 말하기 중지 시
Toast.makeText(MainActivity.context, "듣는 완료", Toast.LENGTH_SHORT).show()
}
override fun onError(error: Int) {
// 에러
// TODO : 에러 반환
/**
ERROR_NETWORK = 2 : Other network related errors
ERROR_AUDIO = 3 : Audio recording error
ERROR_SERVER = 4 : Server sends error status
ERROR_CLIENT = 5 : Other client side errors
ERROR_SPEECH_TIMEOUT = 6 : No speech input
ERROR_NO_MATCH = 7 : No recognition result matched
ERROR_RECOGNIZER_BUSY = 8 : RecognitionService busy
ERROR_INSUFFICIENT_PERMISSIONS = 9 : Insufficient permissions
ERROR_TOO_MANY_REQUESTS = 10 : Too many requests from the same client
ERROR_SERVER_DISCONNECTED = 11 : Server has been disconnected, e.g. because the app has crashed
ERROR_LANGUAGE_NOT_SUPPORTED = 12 : Requested language is not available to be used with the current recognizer
ERROR_LANGUAGE_UNAVAILABLE = 13 : Requested language is supported, but not available currently (e.g. not downloaded yet)
ERROR_CANNOT_CHECK_SUPPORT = 14 : The service does not allow to check for support
*/
}
override fun onResults(results: Bundle?) {
// 인식 결과 준비되었을 때
// (말한 단어가 리스트에 쌓여 인식 직전 준비 시)
var recogString = getAllSpeechToText(results)
// TODO : recogString의 인식 결과 문자열을 활용
}
override fun onPartialResults(partialResults: Bundle?) {
// 부분 인식 결과를 사용할 수 있을 때
}
override fun onEvent(eventType: Int, params: Bundle?) {
// 향후 이벤트를 추가하기 위한 예약
}
}
private fun getAllSpeechToText(results: Bundle?): String {
var allText = ""
results?.let {
var matches = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)
matches?.let {
for(match in it) {
allText += match
}
}
}
return allText
}
구현한 listenSpeech() 메서드를 통해 음성 인식 기능을 수행한다.
Intent 객체에 Speech Recognition과 관련된 설정을 한다.
언어를 설정할 수 있고 샘플에선 한국어(ko-KR) 인식을 테스트 하였다.
Intent 객체가 구성되면 SpeechRecognizer 객채에 intent 데이터를 사용하여 음성 인식 기능을 시작한다.
RecognizerListener에 override해야하는 virtual 메서드 중 주요 메서드의 기능은 다음과 같다.
- onReadyForSpeech : 음성 인식 기능이 시작되어 음성을 받을 수 있는 상태를 알림
- onBeginningOfSpeech : 음성 input이 시작되면 알림
- onEndOfSpeech : 음성 input이 종료되면 알림
- onError : 에러 발생 시 알림
- onResults : 음성 인식 결과가 있는 경우 알림, 데이터(Bundle)를 반환
- Bundle 데이터에서 SppechRecognizer.RESULTS_RECOGNITION 키를 사용하여 인식 결과를 String으로 받을 수 있다.
편안한 음성으로 말할때 음성 인식률이 상당히 좋았다.
해당 기능을 앱의 백그라운드에서도 실행되도록하고 특정 키워드가 입력되었을때 추가 음성을 받도록 한다면
(마치 '하이 빅스비' 이후 > 띠링 하는 소리 출력 > 추가 음성 input 대기 > '날씨'라고 말하기 > '날씨' 키워드 수집되면 관련 데이터 반환)
빅스비나 시리처럼 음성 데이터를 활용할 수 있을 것 같다. (다만, 백그라운드에서 계속 돌고 있다면 디바이스의 베터리나 메모리 효율이...)
2. 문자열 음성 출력
class TextRecogManager {
lateinit var tts: TextToSpeech
init {
tts = TextToSpeech(MainActivity.context, object : TextToSpeech.OnInitListener {
override fun onInit(status: Int) {
if(status != android.speech.tts.TextToSpeech.ERROR) {
tts.language = Locale.KOREAN // 한국어 설정
}
}
})
}
@Synchronized
fun speakWithText(text: String?) {
text?.let {
tts.setPitch(1.0f) // 목소리 톤
tts.setSpeechRate(1.0f) // 목소리 속도
tts.speak(it, TextToSpeech.QUEUE_FLUSH, null, null) // 음성 출력
}
}
}
구현한 speakWithText 메서드를 통해 파라메터로 받은 텍스트를 음성으로 읽는다.
TextToSpeech 객체를 초기화할 때 언어를 설정할 수 있다. 이후 음성을 읽기 전에 해당 객체에서 목소리의 정도를 정의하고 음성으로 출력할 수 있다.