오디오와 비디오를 record하는데 쓰인다.
일련의 과정들이 진행된다음에 녹음을 할 수있다.
위의 그림이 일반적인 과정.
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
recorder.setOutputFile(PATH_NAME);
recorder.prepare();
recorder.start(); // Recording is now started
...
recorder.stop();
recorder.reset(); // You can reuse the object by going back to setAudioSource() step
recorder.release(); // Now the object cannot be reused
예시 코드
AudieEncoder는 마이크에서 들어온 soruce를 AMR_NB방식으로 압축한다는 의미이다.
안드로이드에서 지원하는 audio 포맷이 따로있다.
audio support 공식문서
점으로 표시된건 모든 버전에서 가능
private var recorder:MediaRecorder? =null
private val recordingFilePath :String by lazy {
"${externalCacheDir?.absolutePath}/recording.3gp"
}
두 프로퍼티를 사용!.
private fun startRecording(){
recorder = MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
setOutputFile(recordingFilePath)
prepare()
}
recorder?.start()
state = State.ON_RECORDING
}
레코딩을 시작하려면 여러가지 과정이 필요하다.
외부 캐시 디렉토리에 임시적으로 저장할 것이다.
위에 선언해둔 외부 캐시 FilePath를 이용!
recorder?.start()로 녹음기를 실행시킬 수 있다.
그리고 state를 바꿔준다.
private fun stopRecording(){
recorder?.run{
stop()
release()
}
recorder =null
state = State.AFTER_RECORDING
}
recorder를 멈추고 메모리를 해제해준다음에 null로 초기화를 해준다.
state를 바꿔준다.
media play의 기본적인 상태 diagram
private var player: MediaPlayer? = null
플레이어 프로퍼티를 선언해준다.
private fun startPlaying() {
player = MediaPlayer().apply {
setDataSource(recordingFilePath)
prepare()
}
player?.start()
state = State.ON_PLAYING
}
함수 위에서 쓰였던 외부 캐시 저장소의 path를 가져다쓴다.
prepare()을 사용한다.
state도 바꿔준다!
private fun stopPlaying(){
player?.release()
player =null
state = State.AFTER_RECORDING
}
플레이어는 stop를 해줄필요없이 바로 release해주는 것으로 멈출 수 있다. !!
플레이어도 멈추면 null로 초기화해준다.
그리고 state도 바꿔준다.
private var state = State.BEFORE_RECORDING
set(value){
field =value
resetButton.isEnabled = (value == State.AFTER_RECORDING) ||
(value == State.ON_PLAYING)
recordButton.updateIconWithState(value)
}
set을 이용해서 state가 바뀔때 마다 recordbutton의 icon을 바꿔준다.
그리고 State에 따라 resetButton이 사용될수 있게 없게를 정해주는 resetButton.isEnabled를 사용한다.
private fun bindViews(){
resetButton.setOnClickListener {
stopPlaying()
state =State.BEFORE_RECORDING
}
recordButton.setOnClickListener{
when(state){
State.BEFORE_RECORDING->{
startRecording()
}
State.ON_RECORDING->{
stopRecording()
}
State.AFTER_RECORDING->{
startPlaying()
}
State.ON_PLAYING->{
stopPlaying()
}
}
}
}
각각 recordButton의 icon도 바뀌었지만 그에 해당하는 기능도 연결해줘야하기때문에 recordButton의 onClickListener를 state로 나눠서 기능을 지정해준다.
state를 초기값으로 설정하하는 함수이다.
class MainActivity : AppCompatActivity() {
private val resetButton: Button by lazy {
findViewById(R.id.resetButton)
}
private val recordButton: RecordButton by lazy {
findViewById(R.id.recordButton)
}
//필요한 권한 선언
private val requiredPermissions = arrayOf(Manifest.permission.RECORD_AUDIO)
//초기 state 설정
private var state = State.BEFORE_RECORDING
set(value) {
field = value
resetButton.isEnabled = (value == State.AFTER_RECORDING) ||
(value == State.ON_PLAYING)
recordButton.updateIconWithState(value)
}
//recorder
private var recorder: MediaRecorder? = null
private val recordingFilePath: String by lazy {
"${externalCacheDir?.absolutePath}/recording.3gp"
}
//player
private var player: MediaPlayer? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
requestAudioPermission()
initViews()
bindViews()
initVariables()
}
private fun requestAudioPermission() {
requestPermissions(requiredPermissions, REQUEST_RECORD_AUDIO_PERMISSION)
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
//권한이 부여받은게 맞는지 check 권한부여받았으면 true 아니면 false
val audioRequestPermissionGranted =
requestCode == REQUEST_RECORD_AUDIO_PERMISSION &&
grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED
//권한이 부여되지않으면 어플 종료
if (!audioRequestPermissionGranted) {
finish()
}
}
private fun initViews() {
recordButton.updateIconWithState(state)
}
//녹음 할 수 있는 상태 만들기
private fun startRecording() {
recorder = MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
setOutputFile(recordingFilePath)
prepare()
}
recorder?.start()
state = State.ON_RECORDING
}
private fun stopRecording() {
recorder?.run {
stop()
release()
}
recorder = null
state = State.AFTER_RECORDING
}
private fun startPlaying() {
player = MediaPlayer().apply {
setDataSource(recordingFilePath)
prepare()
}
player?.start()
state = State.ON_PLAYING
}
private fun stopPlaying() {
player?.release()
player = null
state = State.AFTER_RECORDING
}
private fun bindViews() {
resetButton.setOnClickListener {
stopPlaying()
state =State.BEFORE_RECORDING
}
recordButton.setOnClickListener {
when (state) {
State.BEFORE_RECORDING -> {
startRecording()
}
State.ON_RECORDING -> {
stopRecording()
}
State.AFTER_RECORDING -> {
startPlaying()
}
State.ON_PLAYING -> {
stopPlaying()
}
}
}
}
private fun initVariables(){
state = State.BEFORE_RECORDING
}
companion object {
//permission code 선언
private const val REQUEST_RECORD_AUDIO_PERMISSION = 201
}
}
gif라 소리는 안들리지만 잘 작동한다!
녹음기와 재생기 녹음기에는 지원하는 코텍등이있어서 참고해서 녹음기를 만들어줘야한다.
recorder = MediaRecorder().apply {
setAudioSource(MediaRecorder.AudioSource.MIC)
setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP)
setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)
setOutputFile(recordingFilePath)
prepare()
}
recorder?.start()
state = State.ON_RECORDING
}
이런식으로 생성해서 사용할수 있다.
private fun startPlaying() {
player = MediaPlayer().apply {
setDataSource(recordingFilePath)
prepare()
}
player?.start()
state = State.ON_PLAYING
}
mediaplayer는 이런식으로
강의에서 약간 그냥 슉슉 넘어갔다
아무튼 데이터를 개조하고 싶을때 사용한다.
set()이나 get()을 이용해서 set() 내부에서 속성값을 직접 가져오거나 변경이 가능하다고 한다.
이 블로그 참조
설명을 잘해놓으셧다.
var은 변경 가능한 속성이기 때문에 getter와 setter 메소드를 가질수 있고
val 은 불변이기 때문에 getter메소드만 가질 수 있다.
class MyList {
var size: Int? = 0
val isEmpty: Boolean
get() = this.size == 0
}
이와 같은 함수가 다고 하면
Mylist 객체의 isEmpty라는 속성에 접근할 때에 Mylist객체의 size속성값이 0이면 true를 return 아니면 false를 return 하는 것이라고 한다.
class Person {
var name: String = "Not Assigned"
set(value) {
field = "Dev." + value
}
}
fun main(args: Array<String>) {
val person = Person()
person.name = "Ready"
println(person.name)
}
setter에선 Backing Field라고 불리는 field가 있다.
kotlin에선 클래스 안에서 직접적으로 field에 대해 선언할 수는 없지만 필요할떄 자동으로 field라는 식별자를 제공한다.
위의 코드에서 field가 말하는건 'name' 이다.
set(value)에서 value는 'name'의 값이다.
즉 위 코드를 실행하면 Dev.Ready가 출력된다. !!!
이런 중요한걸 몰랐다니...아무튼 잘 알아두자...
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
번외로 when(state) 문도 유용하게 쓰일거 같다.
state에 따라 실행하는 기능이 다르다거나..아무튼 디자인 패턴의 state 패턴이 떠오른다..
다음시간에는 녹음물 비쥬얼라이저!
모두 화이팅!!