본문 바로가기

Android/Basic

[Android] 음성 녹음하기

이번엔 음성을 녹음하는 간단한 앱 예제 (급해서 대충 빨리 씀)


* 오디오 녹음을 위한 과정

1. 미디어 리코더 객체 생성 

 - 오디오 녹음을 위해 미디어리코더 객체를 new 연산자를 이용하여 만듬

2. 오디오 입력 및 출력 형식 설정

 - 오디오 정보를 입력받을 데이터 소스와 함께 출력 형식을 설정함

3. 오디오 인코더와 파일 지정

 - 오디오 파일을 만들 때 필요한 인코더(Encoder)와 함께 파일 이름을 지정함

4. 녹음 시작

 - 녹음을 시작하면 오디오 파일이 만들어지고 인코딩된 바이트 스트림이 저장됨

5. 매니페스트에 권한 설정

 - 애플리케이션에서 녹음을 하려면 RECORD_AUDIO 권한이 있어야 하므로 추가


0. Manifest 에 권한 추가 (위험권한이므로 별도의 check 필요. http://one-delay.tistory.com/10?category=766130 참고)

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


1. 간단한 xml 레이아웃

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<Button
android:id="@+id/play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="27dp"
android:text="재생" />

<Button
android:id="@+id/pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="110dp"
android:text="일시정지" />

<Button
android:id="@+id/restart"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="190dp"
android:text="재시작" />

<Button
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/restart"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:text="정지" />

<Button
android:id="@+id/record"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="116dp"
android:text="녹음하기" />

<Button
android:id="@+id/recordStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_marginBottom="51dp"
android:text="녹음중지" />
</RelativeLayout>


2. 메인액티비티 (설명은 주석)

public class MainActivity extends AppCompatActivity {
MediaRecorder recorder;
String filename;

MediaPlayer player;
int position = 0; // 다시 시작 기능을 위한 현재 재생 위치 확인 변수

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

permissionCheck();

File sdcard = Environment.getExternalStorageDirectory();
File file = new File(sdcard, "recorded.mp4");
filename = file.getAbsolutePath();
Log.d("MainActivity", "저장할 파일 명 : " + filename);

findViewById(R.id.play).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
playAudio();
}
});

findViewById(R.id.pause).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
pauseAudio();
}
});

findViewById(R.id.restart).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
resumeAudio();
}
});

findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stopAudio();
}
});

findViewById(R.id.record).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
recordAudio();
}
});

findViewById(R.id.recordStop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
stopRecording();
}
});
}

private void recordAudio() {
recorder = new MediaRecorder();

/* 그대로 저장하면 용량이 크다.
* 프레임 : 한 순간의 음성이 들어오면, 음성을 바이트 단위로 전부 저장하는 것
* 초당 15프레임 이라면 보통 8K(8000바이트) 정도가 한순간에 저장됨
* 따라서 용량이 크므로, 압축할 필요가 있음 */
recorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 어디에서 음성 데이터를 받을 것인지
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 압축 형식 설정
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);

recorder.setOutputFile(filename);

try {
recorder.prepare();
recorder.start();

Toast.makeText(this, "녹음 시작됨.", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}

private void stopRecording() {
if (recorder != null) {
recorder.stop();
recorder.release();
recorder = null;
Toast.makeText(this, "녹음 중지됨.", Toast.LENGTH_SHORT).show();
}
}

private void playAudio() {
try {
closePlayer();

player = new MediaPlayer();
player.setDataSource(filename);
player.prepare();
player.start();

Toast.makeText(this, "재생 시작됨.", Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}

private void pauseAudio() {
if (player != null) {
position = player.getCurrentPosition();
player.pause();

Toast.makeText(this, "일시정지됨.", Toast.LENGTH_SHORT).show();
}
}

private void resumeAudio() {
if (player != null && !player.isPlaying()) {
player.seekTo(position);
player.start();

Toast.makeText(this, "재시작됨.", Toast.LENGTH_SHORT).show();
}
}

private void stopAudio() {
if (player != null && player.isPlaying()) {
player.stop();

Toast.makeText(this, "중지됨.", Toast.LENGTH_SHORT).show();
}
}

public void closePlayer() {
if (player != null) {
player.release();
player = null;
}
}

public void permissionCheck(){
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED
|| ContextCompat.checkSelfPermission(this, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO}, 1);
}
}
}


권한체크 메서드를 보면 2개의 권한을 받고있는데, 오디오 녹음 권한 하나만 해도 될 것이다. (오디오 권한만 받으면 쓰기권한도 자동으로 부여된다.)

 그리고 중요한것은 오디오 권한을 저렇게 따로 받지않으면 recorder.setAudioSource(MediaRecorder.AudioSource.MIC); 메서드에서 오류가 발생한다.

에뮬레이터의 경우 제대로 동작하지 않는다.


녹음 파일 저장은 시간 관계상 나중에 해보는 것으로, 갖다 붙이기만 하고 끝내겠다.

ContentValues values = new ContentValues(10);
values.put(MediaStore.MediaColumns.TITLE, "Recorded");
values.put(MediaStore.Audio.Media.ALBUM, "Audio Album");
values.put(MediaStore.Audio.Media.ARTIST, "Mike");
values.put(MediaStore.Audio.Media.DISPLAY_NAME, "Recorded Audio");
values.put(MediaStore.Audio.Media.IS_RINGTONE, 1);
values.put(MediaStore.Audio.Media.IS_MUSIC, 1);
values.put(MediaStore.MediaColumns.DATE_ADDED, System.currentTimeMillis()/1000);
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/mp4");
values.put(MediaStore.Audio.Media.DATA, RECORDED_FILE);

Uri audioUri = getContentResolver().insert(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, values);

내용 제공자에 새로운 값을 추가하기 위해 ContentValues 객체에 필요한 정보를 put() 메소드로 넣은 후 insert() 메소드를 이용해 이 객체를 추가합니다.

이때 설정하는 정보들 중에서 MediaStore.MediaColumns.MIME_TYPE은 미디어 파일의 포맷을 의미합니다.

여기에서는 "audio/mp4"로 지정합니다.

MediaStore.MediaColumns.DATA의 경우에는 저장된 녹음 파일을 의미하며, 녹음 파일의 이름을 지정하면 됩니다.

미디어 앨범에서 음성 파일에 대한 내용 제공자의 URI는 MediaStore.Audio.Media.EXTERNAL_CONTENT_URI이므로 ContentValues 객체를 추가할 때 이 URI를 사용합니다.

녹음하려면 RECORD_AUDIO 권한이 필요합니다.

또한 SD 카드에 접근하기 위한 두 가지 권한도 필요합니다.

AndroidManifest.xml 파일을 열고 다음과 같이 권한을 추가합니다.

- 출처 : 부스트코스 강의

'Android > Basic' 카테고리의 다른 글

[Android] 트윈 애니메이션  (0) 2018.09.07
[Android] 스레드 애니메이션  (0) 2018.09.07
[Android] 동영상 재생하기  (0) 2018.08.28
[Android] 음악 재생하기  (0) 2018.08.28
[Android] 카메라로 사진 촬영하기  (2) 2018.08.27