Android 에서 제공하는 Recording API 중, 지난번에 보았던 AudioRecorder 말고 더 편리한 MediaRecorder 가 있습니다.
AudioRecorder 는 오디오만 레코딩 가능 하지만, MediaRecorder 의 경우 Audio 및 Video 컨텐츠의 레코딩이 가능 합니다.
다만 인코딩 된 파일을 받기 때문에 AudioRecorder 처럼 PCM Data 를 바로 받아올 수는 없는 단점이 있습니다.
본인이 구현하려는 레코더의 사용 용도에 맞게 AudioRecorder/MediaRecorder 를 선택하여 사용하시면 되겠습니다.
Android Developer 의 MediaRecorder API Guide 를 참고하면, MediaRecorder 는 아래와 같은 state machine 을 갖습니다.
따라서 MediaRecorder API 를 사용할 때는 아래의 State 를 잘 따라서 코딩해야 합니다. 예를들어 Initail 상태에서는 바로 Prepared 상태로 갈 수 없고, Initailized 와 DataConfigured 상태를 거쳐야지 Prepared 상태가 될 수 있습니다.
이를 어기게 되면 StateIllegalException 이 발생하고, 원하는 동작을 얻을 수 없으니 조심해야 합니다.
Android Developer 사이트에서 설명하는 일반적인 Recording Flow 는 아래 코드와 같습니다.
각 단계별로 실제 레코딩시 일어나는 동작들에 대해 살펴보도록 하겠습니다.
MediaRecorder recorder =newMediaRecorder(); recorder.setAudioSource(MediaRecorder.AudioSource.MIC); //----------- (1) recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); // -- (2) recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // ------(3) recorder.setOutputFile(PATH_NAME); recorder.prepare(); // -----------------------------------------------(4) recorder.start();// Recording is now started ----------------------(5) ... recorder.stop(); recorder.reset();// You can reuse the object by going back to setAudioSource() step recorder.release();// Now the object cannot be reused
(1) AudioSource 설정
AudioSource 의 종류는 아래 표에 정리되어 있습니다.
DEFAULT
0
Default audio source
MIC
1
Microphone audio source
VOICE_UPLINK
2
Voice call uplink (TX) audio source
VOICE_DOWNLINK
3
Voice call downlink (RX) audio source
VOICE_CALL
4
Voice call uplink + downlink audio source
CAMCORDER
5
Microphone audio source tuned for video recording. wit the same orientation as the camera if available
VOICE_RECOGNITION
6
Microphone audio source tuned for voice recognition
VOICE_COMMUNICATION
7
Microphone audio source tuned for voice communications such as VoIP
REMOTE_SUBMIX
8
Audio source for a submix for audio streams to be presented remotely. An application can use this audio source to capture a mix of audio streams that should be transmitted to a remote receiver such as a Wifi display
UNPROCESSED
9
Microphone audio source tuned for unprocessed sound if available.
RADIO_TUNER
1998
Audio source for capturing broadcast radio tuner output
HOTWORD
1999
Audio source for preemptible, low-priority software hotword detection
보통은 마이크로 들어오는 음성을 많이 녹음하기 때문에 MIC 값을 많이 사용하고, 용도에 맞게 그 외의 값들을 설정할 수 있습니다.
제일 아래 RADIO_TUNER 와 HOTWORD 는 System API 로 일반 Application 에서는 사용 불가 합니다.
setAudioSource 함수가 호출되지 않을 경우에는 output file 에 audio track 이 포함되지 않습니다. 즉, 오디오가 녹음되지 않는다는 이야기입니다.
오디오를 녹음하고 싶으면 setAudioSource 함수를 prepare 전에 꼭 호출해 주어야 합니다.
(2) Output Format 설정
레코딩 후 실제 파일로 저장할 때, 저장할 파일의 format 을 설정해 줄 수 있습니다.
DEFAULT
0
THREE_GPP
1
MPEG_4
2
AMR_NB
3
AMR_WB
4
AAC_ADIF
5
AAC_ADTS
6
OUTPUT_FORMAT_RTP_AVP
7
MPEG_2_TS
8
WEBM
9
비디오 포맷으로 가장 많이 사용되는 THREE_GPP 혹은 MPEG_4 가 있고, 오디오만 녹음 할 경우에는 오디오 전용 파일포맷인 AMR 이나 AAC 가 많이 사용 됩니다.
setOutputFormat() 함수는 setAudioSource()/setVideoSource() 호출 이후 / prepare() 이전에 호출되어야 합니다.
(3) AudioEncoder 설정
Audio Recording 시 Audio Encoder 를 설정해 주는 함수 입니다. 이 함수를 호출하지 않을 경우에 녹음된 파일에 audido track 은 포함되지 않습니다.
즉, 이 함수도 setAudioSource 와 동일하게 오디오를 녹음하고 싶다면 setOutputFormat() 이후 / prepare() 이전에 꼭 호출해 주어야 합니다.
DEFAULT
0
AMR_NB
1
AMR_WB
2
AAC
3
HE_AAC
4
AAC_ELD
5
VORBIS
6
(4) prepare
prepare 단계는 앞서 설정한 설정값들로 recording 을 준비하는 단계 입니다.
때문에 이 함수는 audio/video source와 audio/video encoder 를 모두 설정하고 file format 을 확정 지은 후에 불려야 합니다.
이 함수가 불리면 위의 State Machine 에서 recorder 는 prepared 상태가 되어 start 나 reset 을 호출할 수 있습니다.
(5) start
start 함수를 호출하면 정해놓은 audio/video source 에서 실제 레코딩을 시작하게 되고, state 는 Recording 상태가 됩니다.
start 이후 원하는 시점에 stop 을 호출하면, 그 시점까지 레코딩이 진행 됩니다.
이 외에도 API Level 24 부터 제공되는 pause() / resume() 함수를 이용해 레코딩을 잠시 멈추는 것이 가능합니다.
stop() 과 다르게 pause() 함수로 레코딩을 멈추면 기존의 configuration 은 유지된 채로 MediaRecorder 가 잠시 멈추게 됩니다.
이 상태에서 resume() 함수로 레코딩을 다시 시작할 수 있습니다. 당연한 이야기지만 paused 상태에서 레코딩 되는 내용은 버려지게 됩니다.
developer 사이트를 참고하면 위의 간단한 API 이외에도 MediaRecorder 에서 제공하는 다양한 API 들을 볼 수 있으니 참고하시면 되겠습니다.
Java 와의 호환성이 Kotlin 의 큰 장점으로 부곽되는 만큼 Java 와 비교하게 되는 부분이 있습니다.
Kotlin 에서 Class는 Java와 같이 아래처럼 사용합니다.
class Person{ }
그러나 Java 와 다르게 Body 가 없이 아래와 같이 사용될 수도 있습니다.
class Person
Kotlin에서는 primary constructor 과 하나 이상의 secondary constructor 를 가질 수 있습니다.
primary constructor 는 class 선언시 함께 가능합니다.
constructor 키워드는 생략 가능합니다.
annotation 이나 접근자(private, pubilc 등) 와 함께 사용되는 경우 생략할 수 없습니다.
class Person constructor(firstName: String) {
}
class Person(firstName: String) {
}
primary constructor
primary constructor 는 어떤 코드도 포함하지 않으므로 initializer block 을 사용하여 초기화 코드를 구현하면 됩니다.
constructor 가 불리면 객체 생성이 되면서 initializer block 이 불립니다.
primary constructor 의 parameter 는 class 내 어디서든(init 블럭이나 속성값 초기화) 사용 가능합니다.
class Person(name: String) {
val nameInfo = "Name : $name".also(::println)
init {
println("First initializer block that prints ${name}")
}
val secondProperty = "Second property: ${name.length}".also(::println)
init {
println("Second initializer block that prints ${name.length}")
}
}
secondary constructor
constructor 키워드가 반드시 사용되어야 합니다.
class Person {
constructor(parent: Person) {
parent.children.add(this)
}
}
primary constructor 가 있는 경우 모든 second constructor는 primary constructor 에게 위임해야 합니다. 동일 클래스 내에서 다른 생성자로의 위임은 this 키워드를 사용합니다.
initializer block의 코드는 primary constructor 의 일부가 된다. primary constructor 로의 위임은 secondary constructor 코드 실행 전에 실행되므로 모든 initializer block 의 코드는 가장 먼저 실행됩니다.
class Constructors {
init {
println("Init block") // 먼저 실행됨
}
constructor(i: Int) {
println("Constructor") // 나중에 실행됨
}
}
class의 instance 생성하기
class의 instance 를 생성하는데 JAVA에서는 new 키워드를 사용했다면, Kotlin 에서는 constructor 호출만 하면 됩니다.
val invoice = Invoice()
val customer = Customer("Joe Smith")
class 상속받기
Kotlin 에서 class 를 상속받기 위해서는 아래와 같이 사용할 수 있습니다.
C++과 같이 : 기호를 사용하는데 super class의 클래스명에 괄호기호가 붙는 점이 다르네요
물론 함수 override 도 아래와 같이 할 수 있습니다.
open annotation은 class로부터의 상속이 가능하다는 의미입니다. Kotlin에서는 모든 class가 default로 final 로 선언됩니다
open annotation을 사용하는 경우에만 상속이 가능합니다.
method 의 경우에도 open annotation이 있는 method 만 override 가능합니다.
open class Base {
open fun v() {}
fun nv() {}
}
class Derived() : Base() {
override fun v() {}
}
변수의 경우 val 변수(Read only) 를 var 변수로 재정의 할 수 있지만, 반대는 안됩니다.
interface Foo {
val count: Int
}
class Bar1(override val count: Int) : Foo
class Bar2 : Foo {
override var count: Int = 0
}
지난 2017년 5월 Google I/O 에서 Kotlin이 안드로이드의 공식 개발 언어로 발표되었습니다.
Kotlin 공식 사이트를 보면 아래와 같이 메인에서 소개하고 있습니다.
말 그대로 Java와 Android 에서 100% 호환가능하며 여러 platform 에서 동작하는 언어라고 정의하고 있는데요.
Kotlin은 Anroid Studio 개발사인 JetBrain 社 에서 만든 언어로 Kotlin/JVM, Kotlin/JS, Kotlin/Native 등 여러 환경에서 동작을 합니다.
Java 와 100% 호환이 되므로 Android의 API 들을 그대로 사용할 수 있고, Ant, Maven, Gradle과 같은 빌드 시스템을 사용할 수 있어 기존의 Android 개발자들이 낯설치 않게 사용 가능할 것이라 생각됩니다.
Java 코드를 Kotlin코드로 변환하는 도구를 제공하고 있으며, Java 로 개발하는 것보다 비약적으로 코드의 양이 줄어들고 여러 개발자들의 번거로움을 덜어줄 수 있도록 NullPointerException에 대한 안정성이 증가하였습니다.
Kotlin 에 대해 기본문법에 대해 좀더 알아보겠습니다.
1. Kotlin 은 문장 끝에 세미콜론이 없다.
C, C++, Java 많은 언어들이 세미콜론으로 문장의 끝을 알리는데요. Kotlin의 문장 끝에는 세미콜론이 없습니다.
한 줄에 여러 문장을 표현할 때만 세미콜론을 이용합니다.
2. 전역함수 사용이 가능하다
함수가 반드시 클래스 내에 있지 않습니다.
3. new 키워드 삭제
4. 함수 선언은 [접근지정자][inline][final] fun 함수명(매개변수1이름: 매개변수1타입, 매개변수2이름: 매개변수2타입) :반환형의 형태로 한다.
5. val과 var
val은 읽기전용 변수(final 개념) 이며 var는 읽기&쓰기가 가능한 변수를 뜻합니다.
변수선언과 동시에 초기화를 하면 타입을 알아서 추론하기 때문에 변수의 타입이 생략 가능합니다.
아래의 3개 sample 코드를 통해 1~5번까지의 문법 설명이 가능하다.
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main)
sayHello("GoodFortune")
val ck = ClassKotlin("GoodFortune"); val ck2 = ClassKotlin("GoodManner", "01055563262"); var ck3 = ClassKotlin("GoodMan", "01044778855", "ourhome") } }
KotlinTest.kt
fun sayHello(who:String){ val TAG: String = "KotlinTest" Log.d(TAG, "sayHello "+who) }
ClassKotlin.kt
class ClassKotlin(var name: String, var phone: String="", var addr: String ="") { }
6. Nullable
Kotlin 의 가장 큰 장점 중 하나라고 할 수 있습니다. Kotlin에서는 NullPointException 이 발생하지 않는다고 하니 많은 분들이 반가워 할 것 같습니다.
Kotlin 에서는 변수에 null을 저장하고 싶으면 타입명 뒤에 "?"를 붙여 null을 가질 수 있는 변수임을 알려줍니다.
Nullable은 null을 가질 수 있는 변수 타입을 말합니다. (그렇지 않은 변수는 Non-Null 이라고 합니다)
Non-Null 변수인 경우 값이 null 이 아님을 보장할 수 있으므로 Null 체크가 필요없으며 NullPointerException도 발생하지 않습니다.
Nullable 변수에 대해서는 null 체크가 없으면 컴파일러에 의해 오류가 발생합니다.
아래 sample code 에서 checkphone 은 Nullable 변수이므로 checkphone.length 를 사용시 compile error 를 발생시킵니다.
class ClassKotlin(var name: String, var phone: String="", var addr: String ="") { fun checkInformation(){ var checkname: String = name var checkphone: String? = phone if ( checkname.length > 0 ) { // OK }
if ( checkphone.length > 0) { // compile error }
} }
7. When 문
switch 문의 변형이라고도 볼 수 있는데요. switch-case 에서는 하나의 변수값에 대해 integer 형으로만 구분할 수 있는데 when 문은 아래와 같이 다양하게 사용될 수 있습니다.
when{ name.equals("GoodFortune") -> Log.d(TAG, "GoodTortune") phone.length < 9 -> Log.d(TAG, "phone number is short") }
간단한 문법에 대해서 알아보았는데, 다음번엔 Android Studio 3.0 에서 직접 사용하는 방법에 대해서도 알아보려고 합니다.
Android Studio 3.0 에서 JAVA 코드를 Kotlin으로 변환도 가능하다고 하네요^^