마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

오디오레코드클래스를 이용한 녹음파일 저장

0 추천

pcm파일을 wav파일로 바꿔서 외부음악 어플을 이용하여 들으려고 했으나 동작하지 않았습니다.

고수님들의 많은도움 부탁드립니다!!

package org.techtown.audiorecoder;

import android.app.Activity;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioRecord;
import android.media.AudioTrack;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Environment;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import org.techtown.audiorecoder.R;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

public class MainActivity extends AppCompatActivity {

    private final int mBufferSize = 1024;

    private final int mBytesPerElement = 2;


// 설정할 수 있는 sampleRate, AudioFormat, channelConfig 값들을 정의

    private final int[] mSampleRates = new int[]{44100, 22050, 11025, 8000};

    private final short[] mAudioFormats = new short[]{AudioFormat.ENCODING_PCM_16BIT, AudioFormat.ENCODING_PCM_8BIT};

    private final short[] mChannelConfigs = new short[]{AudioFormat.CHANNEL_IN_STEREO, AudioFormat.CHANNEL_IN_MONO};


// 위의 값들 중 실제 녹음 및 재생 시 선택된 설정값들을 저장

    private int mSampleRate;

    private short mAudioFormat;

    private short mChannelConfig;


    private AudioRecord mRecorder = null;

    private Thread mRecordingThread = null;

    private Button mRecordBtn, mPlayBtn;

    private boolean mIsRecording = false;           // 녹음 중인지에 대한 상태값

    private String mPath = "";                      // 녹음한 파일을 저장할 경로


    String AUDIO_RECORER_FILE_EXT_WAV = ".wav";


    @Override

// Layout을 연결하고 각 Button의 OnClickListener를 연결

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);


        mRecordBtn = (Button) findViewById(R.id.start);

        mPlayBtn = (Button) findViewById(R.id.play);


        mRecordBtn.setOnClickListener(btnClick);

        mPlayBtn.setOnClickListener(btnClick);

    }


// 녹음을 수행할 Thread를 생성하여 녹음을 수행하는 함수

    private void startRecording() {

        mRecorder = findAudioRecord();

        mRecorder.startRecording();

        mIsRecording = true;

        mRecordingThread = new Thread(new Runnable() {


            @Override

            public void run() {

                writeAudioDataToFile();

            }

        }, "AudioRecorder Thread");

        mRecordingThread.start();

    }


// 녹음을 하기 위한 sampleRate, audioFormat, channelConfig 값들을 설정


    private AudioRecord findAudioRecord() {

        for (int rate : mSampleRates) {

            for (short format : mAudioFormats) {

                for (short channel : mChannelConfigs) {

                    try {

                        int bufferSize = AudioRecord.getMinBufferSize(rate, channel, format);

                        if (bufferSize != AudioRecord.ERROR_BAD_VALUE) {

                            mSampleRate = rate;

                            mAudioFormat = format;

                            mChannelConfig = channel;


                            AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, mSampleRate, mChannelConfig, mAudioFormat, bufferSize);


                            if (recorder.getState() == AudioRecord.STATE_INITIALIZED) {

                                return recorder;    // 적당한 설정값들로 생성된 Recorder 반환

                            }

                        }

                    } catch (Exception e) {

                        e.printStackTrace();

                    }

                }

            }

        }

        return null;                     // 적당한 설정값들을 찾지 못한 경우 Recorder를 찾지 못하고 null 반환

    }


// 실제 녹음한 data를 file에 쓰는 함수

    private void writeAudioDataToFile() {

        String sd = Environment.getExternalStorageDirectory().getAbsolutePath();

        mPath = sd + "/record_audiorecord.wav";


        short sData[] = new short[mBufferSize];

        FileOutputStream fos = null;


        try {

            fos = new FileOutputStream(mPath);

            while (mIsRecording) {

                mRecorder.read(sData, 0, mBufferSize);

                byte bData[] = short2byte(sData);

                fos.write(bData, 0, mBufferSize * mBytesPerElement);

            }

            fos.close();

        } catch (FileNotFoundException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }


// short array형태의 data를 byte array형태로 변환하여 반환하는 함수


    private byte[] short2byte(short[] sData) {

        int shortArrsize = sData.length;

        byte[] bytes = new byte[shortArrsize * 2];

        for (int i = 0; i < shortArrsize; i++) {

            bytes[i * 2] = (byte) (sData[i] & 0x00FF);

            bytes[(i * 2) + 1] = (byte) (sData[i] >> 8);

            sData[i] = 0;

        }

        return bytes;

    }


// 녹음을 중지하는 함수

    private void stopRecording() {

        if (mRecorder != null) {

            mIsRecording = false;

            mRecorder.stop();

            mRecorder.release();

            mRecorder = null;

            mRecordingThread = null;

        }

    }


// 녹음할 때 설정했던 값과 동일한 설정값들로 해당 파일을 재생하는 함수


    private void playWaveFile() {

        int minBufferSize = AudioTrack.getMinBufferSize(mSampleRate, mChannelConfig, mAudioFormat);

        AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_VOICE_CALL, mSampleRate, mChannelConfig, mAudioFormat, minBufferSize, AudioTrack.MODE_STREAM);

        int count = 0;

        byte[] data = new byte[mBufferSize];


        try {

            FileInputStream fis = new FileInputStream(mPath);

            DataInputStream dis = new DataInputStream(fis);

            audioTrack.play();


            while ((count = dis.read(data, 0, mBufferSize)) > -1) {

                audioTrack.write(data, 0, count);

            }

            audioTrack.stop();

            audioTrack.release();

            dis.close();

            fis.close();

        } catch (FileNotFoundException e) {

            e.printStackTrace();

        } catch (IOException e) {

            e.printStackTrace();

        }

    }


    private View.OnClickListener btnClick = new View.OnClickListener() {


        @Override

        public void onClick(View v) {

            switch (v.getId()) {

// 녹음 버튼일 경우 녹음 중이지 않을 때는 녹음 시작, 녹음 중일 때는 녹음 중지로 텍스트 변경

                case R.id.start:

                    if (mIsRecording == false) {

                        startRecording();

                        mIsRecording = true;

                        mRecordBtn.setText("Stop Recording");

                    } else {

                        stopRecording();

                        mIsRecording = false;

                        mRecordBtn.setText("Start Recording");

                    }

                    break;

                case R.id.play:

// 녹음 파일이 없는 상태에서 재생 버튼 클릭 시, 우선 녹음부터 하도록 Toast 표시

                    if (mPath.length() == 0 || mIsRecording) {

                        Toast.makeText(MainActivity.this, "Please record, first.", Toast.LENGTH_SHORT).show();

                        return;

                    }

// 녹음된 파일이 있는 경우 해당 파일 재생

                    playWaveFile();

                    break;

            }

        }


    };


    @Override

    public boolean onKeyDown(int keyCode, KeyEvent event) {

        if (keyCode == KeyEvent.KEYCODE_BACK) {

            finish();

        }

        return super.onKeyDown(keyCode, event);

    }

 

안린이 님이 2019년 10월 11일 질문

1개의 답변

0 추천

wav 파일은 아래 링크와 같은 구조로 되어 있습니다.

https://crystalcube.co.kr/123

하지만 구현 하신 프로그램은  확장자만 wav 일 뿐 실제 데이터는  PCM 정보만 들어가 있기 때문에,

외부 프로그램에선, PCM의 샘플링 레이트, 채널 count  등의 정보를 알 방법이 없어,  재생이 안되는 것 입니다.

링크에 있는 구조대로 헤더 등의 정보를 추가해서 저장하시는 것도 방법이긴 한데.. 용량이 어마어마 하게 저장되고, 구현도 복잡해서 꼭 wav로 저장할 필요가 없다면 MediaMuxer를 사용해서  mp4(mpa)  - aac 파일로 저장하시는걸 권장합니다.

익명사용자 님이 2019년 10월 11일 답변
...