본문 바로가기
언어 공부/Android

[Android] Opus, AudioRecord, AudioTrack을 까먹지 말자.

by 안다니. 2021. 1. 6.
반응형
private class AudioThread extends Thread {
        // Sample rate must be one supported by Opus.
        static final int SAMPLE_RATE = 8000;

        // Number of samples per frame is not arbitrary,
        // it must match one of the predefined values, specified in the standard.
        static final int FRAME_SIZE = 160;

        // 1 or 2
        static final int NUM_CHANNELS = 1;

        @Override
        public void run() {
            Process.setThreadPriority(Process.THREAD_PRIORITY_MORE_FAVORABLE);

            //녹음용 버퍼 사이즈 구하기 및 버퍼 초기화
            int minBufSize = AudioRecord.getMinBufferSize(SAMPLE_RATE,
                    NUM_CHANNELS == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT);

            // initialize audio recorder
            AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.MIC,
                    SAMPLE_RATE,
                    NUM_CHANNELS == 1 ? AudioFormat.CHANNEL_IN_MONO : AudioFormat.CHANNEL_IN_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    minBufSize);

            // init opus encoder
            OpusEncoder encoder = new OpusEncoder();
            encoder.init(SAMPLE_RATE, NUM_CHANNELS, OpusEncoder.OPUS_APPLICATION_VOIP);

            // init audio track STREAM_VOICE_CALL을 STREAM_MUSIC으로 하면 스피커로 바뀐다
            AudioTrack track = new AudioTrack(AudioManager.STREAM_VOICE_CALL,
                    SAMPLE_RATE,
                    NUM_CHANNELS == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    minBufSize,
                    AudioTrack.MODE_STREAM);

            // init opus decoder
            OpusDecoder decoder = new OpusDecoder();
            decoder.init(SAMPLE_RATE, NUM_CHANNELS);

            // start
            recorder.startRecording();
            track.play();

            byte[] inBuf = new byte[FRAME_SIZE * NUM_CHANNELS * 2];
            byte[] encBuf = new byte[1024];
            short[] outBuf = new short[FRAME_SIZE * NUM_CHANNELS];

            try {
                while (!Thread.interrupted()) {
                    // Encoder must be fed entire frames.
                    int to_read = inBuf.length;
                    int offset = 0;
                    while (to_read > 0) {
                        int read = recorder.read(inBuf, offset, to_read);
                        if (read < 0) {
                            throw new RuntimeException("recorder.read() returned error " + read);
                        }
                        to_read -= read;
                        offset += read;
                    }

                    int encoded = encoder.encode(inBuf, FRAME_SIZE, encBuf);

                    Log.v(TAG, "Encoded " + inBuf.length + " bytes of audio into " + encoded + " bytes");

                    byte[] encBuf2 = Arrays.copyOf(encBuf, encoded);

                    int decoded = decoder.decode(encBuf2, outBuf, FRAME_SIZE);

                    Log.v(TAG, "Decoded back " + decoded * NUM_CHANNELS * 2 + " bytes");

                    track.write(outBuf, 0, decoded * NUM_CHANNELS);
                }
            } finally {
                recorder.stop();
                recorder.release();
                track.stop();
                track.release();
            }
        }
    }

 

반응형

  위의 소스코드는 Opus 오픈 소스를 가져왔으며 MainActivity에 작성되어 있는 AudioThread 부분이다. 내가 필요했던 건 목소리가 스피커폰으로 출력이 되게끔 하는 것. 그러기 위해서는 위의 소스코드중 이 부분을 변경해야 한다.

 

 // init audio track STREAM_VOICE_CALL을 STREAM_MUSIC으로 하면 스피커로 바뀐다
            AudioTrack track = new AudioTrack(AudioManager.STREAM_MUSIC,
                    SAMPLE_RATE,
                    NUM_CHANNELS == 1 ? AudioFormat.CHANNEL_OUT_MONO : AudioFormat.CHANNEL_OUT_STEREO,
                    AudioFormat.ENCODING_PCM_16BIT,
                    minBufSize,
                    AudioTrack.MODE_STREAM);

 

  STREAM_MUSIC은 스피커로 출력해주는 것을 말하며 다른 방법으로는 AudioManger 객체를 선언해서 하는 방법도 있는데, 나는 저런 방식으로 되어 있어서 저렇게 변경을 했다.

 

  또 출력해주는 부분이 AudioTrack이라는 걸 알았으며, AudioRecord는 말 그대로 녹음을 하는 것이다. 즉 입력을 하는 부분이라고 할 수 있다.  

 

  스피커 출력을 하고, 마이크로 말하면, Example 코드는 말하는 걸 그대로 돌려주는 TalkBack이라 에코가 생기게 된다. 이러한 에코를 방지하기 위해선, 어차피 서버로 값을 주고 받을 것이기 때문에,

 

입력을 AudioRecord recorder = new AudioRecord(MediaRecorder.AudioSource.VOICE_COMMUNICATION, 으로 변경을 했다. 사실 맞는지 아닌지 모르지만

 

  그래들 문제로 부터 시작된 오늘 하루 여정이 너무 길었다 ㅠㅠ

반응형

댓글