Android/Application

[Android] Speech Recognition(음성 인식), Text to Speech(문자열 음성 출력) 기능 샘플

mystic-agit 2023. 5. 17. 12:09

[샘플 빌드 환경]

- 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 객체를 초기화할 때 언어를 설정할 수 있다. 이후 음성을 읽기 전에 해당 객체에서 목소리의 정도를 정의하고 음성으로 출력할 수 있다.