[Android] STT

2 분 소요

🙋‍♀️ STT(Speech To Text)

STT란 무엇일까?? 이는 Speech To Text의 줄임말로 말 그대로 음성을 텍스트로 전환하는 것을 말한다.

안드로이드에서는 Speach API를 활용해 STT 기능을 구현할 수 있다.


🙋‍♀️ Speech API 사용법

➡️ 필요한 권한 획득

    <uses-permission android:name="android.permission.RECORD_AUDIO"/>


➡️ STT 작업 진행

우선 stt를 적용하기 위해서 버전과 퍼미션 체크를 진행해야 한다.

    private fun isPermissionGranted(): Boolean {
        return !(Build.VERSION.SDK_INT >= 23 &&
                ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)
    }

    private val requestAudioLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()) { isGranted ->
        if (!isGranted) {
            val error = resources.getString(R.string.dialog_permission)
            val dialog = AudioPermissionDialog(error)
            dialog.show(requireActivity().supportFragmentManager, "AudioPermissionDialog")
        }
    }

ActivityReusltContract의 RequestPermission을 통해 Audio 권한을 요청한다.

ActivityContract에 대한 자세한 설명은 다음 게시글을 참고하길 바란다.


음성 인식 서비스에 접근하려면 SpeechRecognizer 클래스를 사용해야 한다. 해당 클래스를 사용하기 위해서는 createSpeechRecognizer를 통해서 SpeechRecognizer 클래스를 생성해야 한다.

또한, 음성 인식을 실행하려면 intent를 생성해야 하는데 RecognizerIntent를 사용한다.

 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val speechRecognizer = SpeechRecognizer.createSpeechRecognizer(context)
        val speechRecognizerIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
        //음성 인식의 음성 모델 정보 설정
        speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
        //음성 인식의 인식되는 언어 설정
        speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "ko-KR")

        ...


다음으로 listener를 정의해 음성 인식 과정을 처리한다.

이때 사용하는 것이 RecognitionListener이다.

    private val recognitionListener: RecognitionListener = object: RecognitionListener {
        //말하기 시작할 준비가 될 때 호출
        override fun onReadyForSpeech(p0: Bundle?) {
            TODO("Not yet implemented")
        }
    
        //말하기 시작할 때 호출
        override fun onBeginningOfSpeech() {
            TODO("Not yet implemented")
        }

        //사운드 크기가 달라졌을 때 호출
        override fun onRmsChanged(p0: Float) {
            TODO("Not yet implemented")
        }

        //말하기 시작하고 더 많은 단어들이 인식되었을 때 호출
        override fun onBufferReceived(p0: ByteArray?) {
            TODO("Not yet implemented")
        }

        //말하기가 끝났을 때 호출
        override fun onEndOfSpeech() {
            TODO("Not yet implemented")
        }

        //에러가 발생했을 때 호출
        override fun onError(p0: Int) {
            TODO("Not yet implemented")
        }

        //인식 결과가 준비되면 호출
        override fun onResults(p0: Bundle?) {
            TODO("Not yet implemented")
        }

        //부분적으로 인식된 결과를 사용할 수 있을 때 호출
        override fun onPartialResults(p0: Bundle?) {
            TODO("Not yet implemented")
        }

        //향후 이벤트를 예약
        override fun onEvent(p0: Int, p1: Bundle?) {
            TODO("Not yet implemented")
        }

    }


이렇게 작성한 리스너를 이제 SpeechRecognizer에 설정하면 된다.

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        ...

        speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "ko-KR")
        //리스너 설정
        speechRecognizer.setRecognitionListener(recognitionListener) 

이렇게 하면 stt를 사용할 준비가 되었다!!!


이제 직접 사용해보자!!

//음성 인식 시작
speechRecognizer.startListening(speechRecognizerIntent)

//음성 인식 종료
speechRecognizer.stopListening()

위의 시작/종료 코드를 버튼 리스너 속에 작성하는 등 필요한 부분에서 사용하면 된다!


➡️ 전체 코드

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val speechRecognizer = SpeechRecognizer.createSpeechRecognizer(context)
        val speechRecognizerIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
        speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
        speechRecognizerIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, "ko-KR")
        speechRecognizer.setRecognitionListener(recognitionListener)

        binding.btnRadioPlay.setOnClickListener {
            if (isPermissionGranted()) {
                speechRecognizer.startListening(speechRecognizerIntent)
            } else{
                requestAudioLauncher.launch(Manifest.permission.RECORD_AUDIO)
            }

        }

        binding.btnRadioStop.setOnClickListener {
            speechRecognizer.stopListening()
        }
    }

    private fun isPermissionGranted(): Boolean {
        return !(Build.VERSION.SDK_INT >= 23 &&
                ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)
    }

    private val requestAudioLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()) { isGranted ->
        if (!isGranted) {
            val error = resources.getString(R.string.dialog_permission)
            val dialog = AudioPermissionDialog(error)
            dialog.show(requireActivity().supportFragmentManager, "AudioPermissionDialog")
        }
    }


    private val recognitionListener: RecognitionListener = object: RecognitionListener {
        override fun onReadyForSpeech(p0: Bundle?) {}

        override fun onBeginningOfSpeech() {
            binding.btnRadioPlay.visibility = View.GONE
            binding.btnRadioStop.visibility = View.VISIBLE
        }

        override fun onRmsChanged(p0: Float) {}

        override fun onBufferReceived(p0: ByteArray?) {}

        override fun onEndOfSpeech() {
            binding.btnRadioStop.visibility = View.GONE
            binding.btnRadioPlay.visibility = View.VISIBLE
        }

        override fun onError(p0: Int) {
            val message = when (p0) {
                SpeechRecognizer.ERROR_AUDIO -> resources.getString(R.string.stt_audio_error)
                SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> resources.getString(R.string.stt_permission_error)
                SpeechRecognizer.ERROR_NETWORK -> resources.getString(R.string.stt_network_error)
                SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> resources.getString(R.string.stt_time_error)
                else -> resources.getString(R.string.stt_default_error)
            }

            //메세지 처리
            Log.d("error", message)
        }

        override fun onResults(p0: Bundle?) {
            p0?.let {
                val content: ArrayList<String>? =
                    p0.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)

                content.let {
                    //결과값 처리
                    Log.d("result", content!!)
                }
            }
        }

        override fun onPartialResults(p0: Bundle?) {}

        override fun onEvent(p0: Int, p1: Bundle?) {}

    }


🙇‍♀️ 부족한 부분이 있다면 말씀해주세요! 감사합니다!


📃참고

댓글남기기