Android Programming

Audio recorder

여우래비 2015. 6. 22. 11:34
반응형




여러가지 예제 프로그램을 이용해봤으나, 나의 앱 구조상 성공한 건 아래의 code. 

참고로 Fragment 액티비티에서 ExtAudioRecorder(오디오 레코딩) 액티비티를 사용하는 구조이다.


MainActivity.java : 메인 액티비티 (프래그먼트를 위한 껍데기)

Fragment.java : 프래그먼트 액티비티

ExtAudioRecorder.java : 오디오 레코딩 액티비티


Fragment.java


 public class Q11Fragment extends Fragment implements View.OnClickListener{

    private ExtAudioRecorder extAudioRecorder;

 

    public static Fragment newInstance() {

        Fragment fragment = new Fragment();


        return fragment;

    }


    public Fragment() {

        // Required empty public constructor

    }


    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);


    }


    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container,

                             Bundle savedInstanceState) {


        View view = inflater.inflate(R.layout.fragment, container, false);


        Button tv1 = (Button) view.findViewById(R.id.button_start);

        tv1.setOnClickListener(this);

        Button tv2 = (Button) view.findViewById(R.id.button_stop);

        tv2.setOnClickListener(this);

        Button tv3 = (Button) view.findViewById(R.id.button_play);

        tv3.setOnClickListener(this);

        Button tv4 = (Button) view.findViewById(R.id.button_play_ext);

        tv4.setOnClickListener(this);


        // Inflate the layout for this fragment

        return view;

    }


    @Override

    public void onClick(View v) {


        switch (v.getId()) {


            //녹음시작

            case R.id.button_start:

                extAudioRecorder = ExtAudioRecorder.getInstanse(true);

                extAudioRecorder.setOutputFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/test_record.mp3");

                extAudioRecorder.getMaxAmplitude();

                extAudioRecorder.prepare();

                extAudioRecorder.start();

                break;


            //녹음종료

            case R.id.button_stop:

                extAudioRecorder.stop();

                extAudioRecorder.release();


                break;


            //녹음내용 플레이

            case R.id.button_play:

                extAudioRecorder = ExtAudioRecorder.getInstanse(true);

                extAudioRecorder.getMaxAmplitude();

                extAudioRecorder.prepare();

                extAudioRecorder.release();

                extAudioRecorder.play(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/test_record.mp3");


                break;


            // 외부 파일 플레이 테스트용

            case R.id.button_play_ext:

                extAudioRecorder = ExtAudioRecorder.getInstanse(true);

                extAudioRecorder.getMaxAmplitude();

                extAudioRecorder.prepare();

                extAudioRecorder.release();

                extAudioRecorder.play(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/beep1.mp3");


                break;


        }

    }


}


AndroidManifest.xml (</application> 밑에 아래 세 줄 추가)   

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

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

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


ExtAudioRecorder.java (해외 사이트가 출처인데, 어디인지 잊어버렸다...-_-;; 기존출처 내용에 Play 메소드만 추가하였다.)

public class ExtAudioRecorder {

    private final static int[] sampleRates = {44100, 22050, 11025, 8000};

    private MediaPlayer player;

    String RECORDED_FILE = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/test_record.mp3";


    public static ExtAudioRecorder getInstanse(Boolean recordingCompressed)

    {

        ExtAudioRecorder result = null;


        if(recordingCompressed)

        {

            result = new ExtAudioRecorder( false,

                    AudioSource.MIC,

                    sampleRates[3],

                    AudioFormat.CHANNEL_IN_STEREO,

                    AudioFormat.ENCODING_PCM_16BIT);

        }

        else

        {

            int i=0;

            do

            {

                result = new ExtAudioRecorder( true,

                        AudioSource.MIC,

                        sampleRates[i],

                        AudioFormat.CHANNEL_IN_STEREO,

                        AudioFormat.ENCODING_PCM_16BIT);


            } while((++i<sampleRates.length) & !(result.getState() == ExtAudioRecorder.State.INITIALIZING));

        }

        return result;

    }


    /**

     * INITIALIZING : recorder is initializing;

     * READY : recorder has been initialized, recorder not yet started

     * RECORDING : recording

     * ERROR : reconstruction needed

     * STOPPED: reset needed

     */

    public enum State {INITIALIZING, READY, RECORDING, ERROR, STOPPED};


    public static final boolean RECORDING_UNCOMPRESSED = true;

    public static final boolean RECORDING_COMPRESSED = false;


    // The interval in which the recorded samples are output to the file

    // Used only in uncompressed mode

    private static final int TIMER_INTERVAL = 120;


    // Toggles uncompressed recording on/off; RECORDING_UNCOMPRESSED / RECORDING_COMPRESSED

    private boolean         rUncompressed;


    // Recorder used for uncompressed recording

    private AudioRecord audioRecorder = null;


    // Recorder used for compressed recording

    private MediaRecorder mediaRecorder = null;


    // Stores current amplitude (only in uncompressed mode)

    private int             cAmplitude= 0;


    // Output file path

    private String          filePath = null;


    // Recorder state; see State

    private State           state;


    // File writer (only in uncompressed mode)

    private RandomAccessFile randomAccessWriter;


    // Number of channels, sample rate, sample size(size in bits), buffer size, audio source, sample size(see AudioFormat)

    private short                    nChannels;

    private int                      sRate;

    private short                    bSamples;

    private int                      bufferSize;

    private int                      aSource;

    private int                      aFormat;


    // Number of frames written to file on each output(only in uncompressed mode)

    private int                      framePeriod;


    // Buffer for output(only in uncompressed mode)

    private byte[]                   buffer;


    // Number of bytes written to file after header(only in uncompressed mode)

    // after stop() is called, this size is written to the header/data chunk in the wave file

    private int                      payloadSize;


    /**

     *

     * Returns the state of the recorder in a RehearsalAudioRecord.State typed object.

     * Useful, as no exceptions are thrown.

     *

     * @return recorder state

     */

    public State getState()

    {

        return state;

    }


    /*

    *

    * Method used for recording.

    *

    */

    private AudioRecord.OnRecordPositionUpdateListener updateListener = new AudioRecord.OnRecordPositionUpdateListener()

    {

        public void onPeriodicNotification(AudioRecord recorder)

        {

            audioRecorder.read(buffer, 0, buffer.length); // Fill buffer

            try

            {

                randomAccessWriter.write(buffer); // Write buffer to file

                payloadSize += buffer.length;

                if (bSamples == 16)

                {

                    for (int i=0; i<buffer.length/2; i++)

                    { // 16bit sample size

                        short curSample = getShort(buffer[i*2], buffer[i*2+1]);

                        if (curSample > cAmplitude)

                        { // Check amplitude

                            cAmplitude = curSample;

                        }

                    }

                }

                else

                { // 8bit sample size

                    for (int i=0; i<buffer.length; i++)

                    {

                        if (buffer[i] > cAmplitude)

                        { // Check amplitude

                            cAmplitude = buffer[i];

                        }

                    }

                }

            }

            catch (IOException e)

            {

                Log.e(ExtAudioRecorder.class.getName(), "Error occured in updateListener, recording is aborted");

                //stop();

            }

        }


        public void onMarkerReached(AudioRecord recorder)

        {

            // NOT USED

        }

    };

    /**

     *

     *

     * Default constructor

     *

     * Instantiates a new recorder, in case of compressed recording the parameters can be left as 0.

     * In case of errors, no exception is thrown, but the state is set to ERROR

     *

     */

    public ExtAudioRecorder(boolean uncompressed, int audioSource, int sampleRate, int channelConfig, int audioFormat)

    {

        try

        {

            rUncompressed = uncompressed;

            if (rUncompressed)

            { // RECORDING_UNCOMPRESSED

                if (audioFormat == AudioFormat.ENCODING_PCM_16BIT)

                {

                    bSamples = 16;

                }

                else

                {

                    bSamples = 8;

                }


                if (channelConfig == AudioFormat.CHANNEL_IN_MONO)

                {

                    nChannels = 1;

                }

                else

                {

                    nChannels = 2;

                }


                aSource = audioSource;

                sRate   = sampleRate;

                aFormat = audioFormat;


                framePeriod = sampleRate * TIMER_INTERVAL / 1000;

                bufferSize = framePeriod * 2 * bSamples * nChannels / 8;

                if (bufferSize < AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat))

                { // Check to make sure buffer size is not smaller than the smallest allowed one

                    bufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, audioFormat);

                    // Set frame period and timer interval accordingly

                    framePeriod = bufferSize / ( 2 * bSamples * nChannels / 8 );

                    Log.w(ExtAudioRecorder.class.getName(), "Increasing buffer size to " + Integer.toString(bufferSize));

                }


                audioRecorder = new AudioRecord(audioSource, sampleRate, channelConfig, audioFormat, bufferSize);


                if (audioRecorder.getState() != AudioRecord.STATE_INITIALIZED)

                    throw new Exception("AudioRecord initialization failed");

                audioRecorder.setRecordPositionUpdateListener(updateListener);

                audioRecorder.setPositionNotificationPeriod(framePeriod);

            } else

            { // RECORDING_COMPRESSED

                mediaRecorder = new MediaRecorder();

                mediaRecorder.setAudioSource(AudioSource.MIC);

                mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);

                mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

            }

            cAmplitude = 0;

            filePath = null;

            state = State.INITIALIZING;

        } catch (Exception e)

        {

            if (e.getMessage() != null)

            {

                Log.e(ExtAudioRecorder.class.getName(), e.getMessage());

            }

            else

            {

                Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured while initializing recording");

            }

            state = State.ERROR;

        }

    }


    /**

     * Sets output file path, call directly after construction/reset.

     *

     //* @param output file path

     *

     */

    public void setOutputFile(String argPath)

    {

        try

        {

            if (state == State.INITIALIZING)

            {

                filePath = argPath;

                if (!rUncompressed)

                {

                    mediaRecorder.setOutputFile(filePath);

                }

            }

        }

        catch (Exception e)

        {

            if (e.getMessage() != null)

            {

                Log.e(ExtAudioRecorder.class.getName(), e.getMessage());

            }

            else

            {

                Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured while setting output path");

            }

            state = State.ERROR;

        }

    }


    /**

     *

     * Returns the largest amplitude sampled since the last call to this method.

     *

     * @return returns the largest amplitude since the last call, or 0 when not in recording state.

     *

     */

    public int getMaxAmplitude()

    {

        if (state == State.RECORDING)

        {

            if (rUncompressed)

            {

                int result = cAmplitude;

                cAmplitude = 0;

                return result;

            }

            else

            {

                try

                {

                    return mediaRecorder.getMaxAmplitude();

                }

                catch (IllegalStateException e)

                {

                    return 0;

                }

            }

        }

        else

        {

            return 0;

        }

    }



    /**

     *

     * Prepares the recorder for recording, in case the recorder is not in the INITIALIZING state and the file path was not set

     * the recorder is set to the ERROR state, which makes a reconstruction necessary.

     * In case uncompressed recording is toggled, the header of the wave file is written.

     * In case of an exception, the state is changed to ERROR

     *

     */

    public void prepare()

    {

        try

        {

            if (state == State.INITIALIZING)

            {

                if (rUncompressed)

                {

                    if ((audioRecorder.getState() == AudioRecord.STATE_INITIALIZED) & (filePath != null))

                    {

                        // write file header


                        randomAccessWriter = new RandomAccessFile(filePath, "rw");


                        randomAccessWriter.setLength(0); // Set file length to 0, to prevent unexpected behavior in case the file already existed

                        randomAccessWriter.writeBytes("RIFF");

                        randomAccessWriter.writeInt(0); // Final file size not known yet, write 0

                        randomAccessWriter.writeBytes("WAVE");

                        randomAccessWriter.writeBytes("fmt ");

                        randomAccessWriter.writeInt(Integer.reverseBytes(16)); // Sub-chunk size, 16 for PCM

                        randomAccessWriter.writeShort(Short.reverseBytes((short) 1)); // AudioFormat, 1 for PCM

                        randomAccessWriter.writeShort(Short.reverseBytes(nChannels));// Number of channels, 1 for mono, 2 for stereo

                        randomAccessWriter.writeInt(Integer.reverseBytes(sRate)); // Sample rate

                        randomAccessWriter.writeInt(Integer.reverseBytes(sRate*bSamples*nChannels/8)); // Byte rate, SampleRate*NumberOfChannels*BitsPerSample/8

                        randomAccessWriter.writeShort(Short.reverseBytes((short)(nChannels*bSamples/8))); // Block align, NumberOfChannels*BitsPerSample/8

                        randomAccessWriter.writeShort(Short.reverseBytes(bSamples)); // Bits per sample

                        randomAccessWriter.writeBytes("data");

                        randomAccessWriter.writeInt(0); // Data chunk size not known yet, write 0


                        buffer = new byte[framePeriod*bSamples/8*nChannels];

                        state = State.READY;

                    }

                    else

                    {

                        Log.e(ExtAudioRecorder.class.getName(), "prepare() method called on uninitialized recorder");

                        state = State.ERROR;

                    }

                }

                else

                {

                    mediaRecorder.prepare();

                    state = State.READY;

                }

            }

            else

            {

                Log.e(ExtAudioRecorder.class.getName(), "prepare() method called on illegal state");

                release();

                state = State.ERROR;

            }

        }

        catch(Exception e)

        {

            if (e.getMessage() != null)

            {

                Log.e(ExtAudioRecorder.class.getName(), e.getMessage());

            }

            else

            {

                Log.e(ExtAudioRecorder.class.getName(), "Unknown error occured in prepare()");

            }

            state = State.ERROR;

        }

    }


    /**

     *

     *

     *  Releases the resources associated with this class, and removes the unnecessary files, when necessary

     *

     */

    public void release()

    {

        if (state == State.RECORDING)

        {

            stop();

        }

        else

        {

            if ((state == State.READY) & (rUncompressed))

            {

                try

                {

                    randomAccessWriter.close(); // Remove prepared file

                }

                catch (IOException e)

                {

                    Log.e(ExtAudioRecorder.class.getName(), "I/O exception occured while closing output file");

                }

                (new File(filePath)).delete();

            }

        }


        if (rUncompressed)

        {

            if (audioRecorder != null)

            {

                audioRecorder.release();

            }

        }

        else

        {

            if (mediaRecorder != null)

            {

                mediaRecorder.release();

            }

        }

    }


    /**

     *

     *

     * Resets the recorder to the INITIALIZING state, as if it was just created.

     * In case the class was in RECORDING state, the recording is stopped.

     * In case of exceptions the class is set to the ERROR state.

     *

     */

    public void reset()

    {

        try

        {

            if (state != State.ERROR)

            {

                release();

                filePath = null; // Reset file path

                cAmplitude = 0; // Reset amplitude

                if (rUncompressed)

                {

                    audioRecorder = new AudioRecord(aSource, sRate, nChannels+1, aFormat, bufferSize);

                }

                else

                {

                    mediaRecorder = new MediaRecorder();

                    mediaRecorder.setAudioSource(AudioSource.MIC);

                    mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);

                    mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);

                }

                state = State.INITIALIZING;

            }

        }

        catch (Exception e)

        {

            Log.e(ExtAudioRecorder.class.getName(), e.getMessage());

            state = State.ERROR;

        }

    }


    /**

     *

     *

     * Starts the recording, and sets the state to RECORDING.

     * Call after prepare().

     *

     */

    public void start()

    {

        if (state == State.READY)

        {

            if (rUncompressed)

            {

                payloadSize = 0;

                audioRecorder.startRecording();

                audioRecorder.read(buffer, 0, buffer.length);

            }

            else

            {

                mediaRecorder.start();

            }

            state = State.RECORDING;

        }

        else

        {

            Log.e(ExtAudioRecorder.class.getName(), "start() called on illegal state");

            state = State.ERROR;

        }

    }


    /**

     *

     *

     *  Stops the recording, and sets the state to STOPPED.

     * In case of further usage, a reset is needed.

     * Also finalizes the wave file in case of uncompressed recording.

     *

     */

    public void stop()

    {

        if (state == State.RECORDING)

        {

            if (rUncompressed)

            {

                audioRecorder.stop();


                try

                {

                    randomAccessWriter.seek(4); // Write size to RIFF header

                    randomAccessWriter.writeInt(Integer.reverseBytes(36+payloadSize));


                    randomAccessWriter.seek(40); // Write size to Subchunk2Size field

                    randomAccessWriter.writeInt(Integer.reverseBytes(payloadSize));


                    randomAccessWriter.close();

                }

                catch(IOException e)

                {

                    Log.e(ExtAudioRecorder.class.getName(), "I/O exception occured while closing output file");

                    state = State.ERROR;

                }

            }

            else

            {

                mediaRecorder.stop();

            }

            state = State.STOPPED;

        }

        else

        {

            Log.e(ExtAudioRecorder.class.getName(), "stop() called on illegal state");

            state = State.ERROR;

        }

    }




    public void play(String path_Q11){


        if (player != null) {

            player.stop();

            player.release();

            player = null;

        }


        //Toast.makeText(getApplicationContext(), "녹음된 파일을 재생합니다.", Toast.LENGTH_LONG).show();

        try {

            player = new MediaPlayer ();


            player.setDataSource(path_Q11);

            player.prepare();

            player.start();

        } catch (Exception e) {

            Log.e("SampleAudioRecorder", "Audio play failed.", e);

        }


    }

    /*

     *

     * Converts a byte[2] to a short, in LITTLE_ENDIAN format

     *

     */

    private short getShort(byte argB1, byte argB2)

    {

        return (short)(argB1 | (argB2 << 8));

    }



}




반응형