[MediaRecorder] 녹음 기능 형성을 시도기

2021. 11. 2. 14:00Android

728x90

#Android #Kotiln #MediaRecorder #기본기능 

 

댓글과 좋아요는 사랑입니다. 

안녕하세요. 가장 기본적인 기능을 구현하는데 포커스를 맞춰 재작 했습니다.

안드로이드 녹음 기능입니다.

 

[목표] 

녹음 후 파일 재생을 위해서 무엇을 했는지 나열을 해보고자 합니다. 

 

[완료]

가장 먼저 안드로이드 개발자 문서를 참고하였고, 이를 약간 수정하여 개발하였습니다. 

1. 권한을 Android Manifest.xml에 설정 

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

 

2. 권한을 넣어줍니다. 

(필수) val PERMISSION_REQUEST_RECORD_AUDIO = 100  // 권한 요청을 위한 코드 

(필수) Manifest.permission.RECORD_AUDIO // AUDIO 권한의 코드 

(필수) ActivityCompat.requestPermissions // 사용자에게 권한을 요구할 화면을 호출하는 함수 

시작 화면인 MainActivity.kt 파일에 위의 코드들을 넣어줍니다. 

(선택) ActivityCompat.shouldShowRequestPermissionRationale // 사용자가 권한에 대한 설명을 요구하는 경우 필요한 함수

(선택) ActivityCompat.OnRequestPermissionsResultCallback // 구현체 구현, 추가적인 기능 구현이 필요하면 구현하면 됩니다. 

class MainActivity : AppCompatActivity() , ActivityCompat.OnRequestPermissionsResultCallback {

    val PERMISSION_REQUEST_RECORD_AUDIO = 1000

	// 앱에서 다른 권한도 넣어줄 때 Array로 작성하는게 편리하다. 
    var REQUIRED_PERMISSIONS =
        arrayOf(
            Manifest.permission.RECORD_AUDIO
        )


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_sample_list)
		...
        requestPermission()
		...
    }


    private fun requestPermission(){

        // RA : RECORD_AUDIO
        // RA_CHECK_RESULT  : RECORD_AUDIO CHECK RESULT
        val RA_CHECK_RESULT = ContextCompat.checkSelfPermission(
            this,
            Manifest.permission.RECORD_AUDIO
        ) == PackageManager.PERMISSION_GRANTED


        if( !RA_CHECK_RESULT ){
            if (
                ActivityCompat.shouldShowRequestPermissionRationale(
                    this,
                    Manifest.permission.RECORD_AUDIO
                )
            ) {
               // 사용자가 권한을 거부한 내역이 있어서.
                // 그것에 대한 설명할 UI를 생성시켜줌.

            }
        }

        ActivityCompat.requestPermissions(this,REQUIRED_PERMISSIONS,PERMISSION_REQUEST_RECORD_AUDIO)

    }

    ...
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)

        if (requestCode == PERMISSION_REQUEST_RECORD_AUDIO ){

	    // 권한이 확인 된 경우 다음처리 하는 내용 

        }
    }

}

3. 권한을 획득 후 녹음 기능 구현

 

(필수) btnRecord  // 이 버튼을 UI에 그려줍니다. 역할은 클릭하면 녹음 중이 아닌 경우, 녹음합니다. 

                       // 녹음 중인 경우, 녹음을 정지합니다. 

 

(중요 필수) startRecording() 함수 중 MediaRecorder().apply 부분의 모든 내용은 빠져서는 안됩니다. 

(필수의 부연설명) MediaRecorder().apply 여기에서 prepare() 나 start()는 recorder의 생애주기를 따르는 것입니다. 

https://developer.android.com/reference/android/media/MediaRecorder  개발자 문서를 참고하셔서 State Diagram에 따라 필요한 부분입니다. State Diagram과 같이 표현한 이유는 MediaRecorder()의 상태를 이렇게 관리해라 라는 가이드입니다.

 

(중요 필수) stopRecording() 함수

(필수의 부연설명) 위에서 언급되었듯, recorder객체의 stop()과 release()의 경우 State Diagram을 참고하십시오. 

 

 

 

class AudioRecordActivity : AppCompatActivity(),View.OnClickListener {

    private lateinit var binding: ActivityAudioRecordBinding

    private var fileName: String = ""

    private var recorder: MediaRecorder? = null
    
    var mRecording = false
    var mPlaying = false

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

		// 이 부분은 바인딩 라이브러리가 적용된 내용이라 그대로 복사 붙여넣기 하시면
        // 안됩니다!
        binding = ActivityAudioRecordBinding.inflate(layoutInflater)
        setContentView(binding.root)

		// 클릭 이벤트를 넣어줍니다. 
        binding.btnRecord.setOnClickListener (this)
        
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            externalCacheDirs?.forEach {
                if ( it.exists() ){
                	// 이 부분은 실제로 녹음한파일들이 잘 저장이 됬는지 확인하기 위합입니다. 
                    it.list().iterator().forEach { it1 ->
                        Log.d("캐쉬","externalCacheDirs/cache/${it1}}")
                    }
                }
            }
        }

        // Record to the external cache directory for visibility
        fileName = "${externalCacheDir?.absolutePath}/AR"+SimpleDateFormat("dd_M_yyy_hh_mm_ss").format(Date())+".3gp"

        binding.tvFileName.text="$fileName" // 이 화면이 만들어졌을 때 어느 파일에 저장하는지 확인.

		btnRecord.setOnClickListener( this )
        btnPlay.setOnClickListener( this )

    }

    private fun onRecord(start: Boolean) = if (start) {
        startRecording()
    } else {
        stopRecording()
    }

    private fun startRecording() {
        recorder = MediaRecorder().apply {
            setAudioSource(MediaRecorder.AudioSource.MIC)
            setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
            setOutputFile(fileName)
            setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)

            try {
                prepare()
            } catch (e: IOException) {
                Log.e(LOG_TAG, "prepare() failed")
            }

            start()
        }
    }

    private fun stopRecording() {
        recorder?.apply {
            stop()
            release()
        }
        recorder = null
    }


    override fun onClick(v: View?) {

        when (v?.id){
            (R.id.btnRecord) ->{
                clickRecording()
            }
        }
    }

    fun clickRecording(){
        // view Side Logic
        onRecord(mRecording)
        if( mRecording ){
            btnRecord.setText("Ready to Recording")
        } else {
            btnRecord.setText("Start recording")
        }
        mRecording = !mRecording
    }

}

 

[포인트]

1. 구현의 포커스는 일단 기능만 돌아가면 된다. 

2. RECORD_AUDIO 권한 삽입 및 허가 기능 

3. MediaRecorder 객체를 다루기. 

 

[고급 기능 구현 포인트]

1. 안드로이드 앱이 마켓에 올라가는 경우라면 MediaMuxer기능은 모든 버전에서 지원이 불가능하다. 따라서 서비스가 하나 만들어져야 한다면 불가능하다고 이야기하는 게 맞다. (2021년 기준) 

2. 메타데이터 추가 부분의 경우 데이터 자체에 추가적으로 넣는 게 가능합니다. 이 의미는 서버와 통신하면서 이 파일에 대한 정보를 보낼 수도 있지만, 파일 그 자체에 담겨있으므로 아무래도 파일 그 자체에 데이터에 넣을 수 있단 점을 보아 데이터가 집약적이라고 볼 수도 있습니다. 

 

[추후 과제]

1. 녹음 파일 목록 보는 화면단 만들기. (사용자가 직접 다뤄봐야 하기 때문에 그렇습니다.) 혹은 지원해주는 UI가 있는 것인지.

2. 녹음 파일 재생하는 것 알아봐야 합니다. 이 편은 다음 글에서 다뤄볼 예정입니다. 

   ex) MediaPlayer , ExoPlayer 

3. 기존 녹음 파일을 수정해야 되는 경우가 생긴다면 MediaRecorder 객체를 이용하는 게 유력해 보입니다. 여기서 Output파일을 지정할 때 덮어 씌우는 건지(Overwrite), 특정 부분만 변경하는 건지(Replace) 알아봐야 합니다.  

 

 

 

 

 

 

 

 

 

 

[reference]

https://developer.android.com/guide/topics/media/mediarecorder?hl=ko