天天看點

Android 音頻錄制 AudioRecord,MediaRecorder的使用及播放

本章參考位址:https://blog.csdn.net/zxyudia/article/details/68948751,謝謝這位仁兄的文章。

在他基礎上新增了下列功能:

  • 增加了權限的标注
  • 修複 AudioRecord錄制的音頻播放時沒反應的問題。
  • 整理出完整代碼并上傳到GitHub上。

AudioRecord和MediaRecorder兩種都可以錄制音頻,MediaRecorder已實作大量的封裝,操作起來更加簡單,

而AudioRecord使用起來更加靈活,能實作更多的功能。

1.AudioRecord(基于位元組流錄音)

優點:可以實作語音的實時處理,進行邊錄邊播,對音頻的實時處理。

缺點:輸出的是PCM的語音資料,如果儲存成音頻檔案是不能被播放器播放的。要用到AudioTrack這個去進行處理。

2.MediaRecorder(基于檔案錄音)

已內建了錄音,編碼,壓縮等,支援少量的音頻格式檔案。

優點:封裝度很高,操作簡單

缺點:無法實作實時處理音頻,輸出的音頻格式少。

  • 首先将權限加上
<!-- 允許程式錄制音頻-->
    <uses-permission android:name="android.permission.RECORD_AUDIO" />
   <!-- 寫SD卡權限-->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
           
  • AudioRecord的功能實作:

開始錄音代碼實作

/**
     * 開始錄音
     *
     * @return
     */
    private boolean dostart() {
        try {
            //記錄開始錄音時間
            startRecorderTime = System.currentTimeMillis();

            //建立錄音檔案
            mAudioRecordFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() +
                    "/recorderdemo/" + System.currentTimeMillis() + ".pcm");
            if (!mAudioRecordFile.getParentFile().exists()) {
                mAudioRecordFile.getParentFile().mkdirs();
                mAudioRecordFile.createNewFile();
            }

            //建立檔案輸出流
            mFileOutputStream = new FileOutputStream(mAudioRecordFile);
            //配置AudioRecord
            int audioSource = MediaRecorder.AudioSource.MIC;
            //所有android系統都支援
            int sampleRate = ;
            //單聲道輸入
            int channelConfig = AudioFormat.CHANNEL_IN_MONO;
            //PCM_16是所有android系統都支援的
            int autioFormat = AudioFormat.ENCODING_PCM_16BIT;
            //計算AudioRecord内部buffer最小
            int minBufferSize = AudioRecord.getMinBufferSize(sampleRate, channelConfig, autioFormat);
            //buffer不能小于最低要求,也不能小于我們每次我們讀取的大小。
            mAudioRecord = new AudioRecord(audioSource, sampleRate, channelConfig, autioFormat, Math.max(minBufferSize, BUFFER_SIZE));


            //開始錄音
            mAudioRecord.startRecording();

            //循環讀取資料,寫入輸出流中
            while (mIsRecording) {
                //隻要還在錄音就一直讀取
                int read = mAudioRecord.read(mBuffer, , BUFFER_SIZE);
                if (read <= ) {
                    return false;
                } else {
                    mFileOutputStream.write(mBuffer, , read);
                }

            }

            //退出循環,停止錄音,釋放資源
            stopRecorder();

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        } finally {
            if (mAudioRecord != null) {
                mAudioRecord.release();
            }
        }
        return true;
    }
           

停止錄音代碼實作

/**
     * 停止錄音
     * @return
     */
    private boolean doStop() {
        //停止錄音,關閉檔案輸出流
        mAudioRecord.stop();
        mAudioRecord.release();
        mAudioRecord = null;
        Log.i("Tag8", "go here");
        //記錄結束時間,統計錄音時長
        stopRecorderTime = System.currentTimeMillis();
        //大于3秒算成功,在主線程更新UI
        final int send = (int) (stopRecorderTime - startRecorderTime) / ;
        if (send > ) {
            mHandler.post(new Runnable() {
                @Override
                public void run() {
                    tv_stream_msg.setText("錄音成功:" + send + "秒");
                    bt_stream_recorder.setText("開始錄音");
                    Log.i("Tag8", "go there");
                }
            });
        } else {
            recorderFail();
            return false;
        }
        return true;
    }
           

播放音頻檔案代碼實作

/**
     * 播放音頻檔案
     *
     * @param audioFile
     */
    private void doPlay(File audioFile) {
        if (audioFile != null) {
            Log.i("Tag8", "go there");
            //配置播放器
            //音樂類型,揚聲器播放
            int streamType = AudioManager.STREAM_MUSIC;
            //錄音時采用的采樣頻率,是以播放時同樣的采樣頻率
            int sampleRate = ;
            //單聲道,和錄音時設定的一樣
            int channelConfig = AudioFormat.CHANNEL_OUT_MONO;
            //錄音時使用16bit,是以播放時同樣采用該方式
            int audioFormat = AudioFormat.ENCODING_PCM_16BIT;
            //流模式
            int mode = AudioTrack.MODE_STREAM;

            //計算最小buffer大小
            int minBufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);

            //構造AudioTrack  不能小于AudioTrack的最低要求,也不能小于我們每次讀的大小
            AudioTrack audioTrack = new AudioTrack(streamType, sampleRate, channelConfig, audioFormat,
                    Math.max(minBufferSize, BUFFER_SIZE), mode);
            audioTrack.play();
            //從檔案流讀資料
            FileInputStream inputStream = null;
            try {
                //循環讀資料,寫到播放器去播放
                inputStream = new FileInputStream(audioFile);

                //循環讀資料,寫到播放器去播放
                int read;
                //隻要沒讀完,循環播放
                while ((read = inputStream.read(mBuffer)) > ) {
                    Log.i("Tag8", "read:" + read);
                    int ret = audioTrack.write(mBuffer, , read);
                    //檢查write的傳回值,處理錯誤
                    switch (ret) {
                        case AudioTrack.ERROR_INVALID_OPERATION:
                        case AudioTrack.ERROR_BAD_VALUE:
                        case AudioManager.ERROR_DEAD_OBJECT:
                            playFail();
                            return;
                        default:
                            break;
                    }
                }
                //播放結束
                audioTrack.stop();
            } catch (Exception e) {
                e.printStackTrace();
                //讀取失敗
                playFail();
            } finally {
                mIsPlaying = false;
                //關閉檔案輸入流
                if (inputStream != null) {
                    closeStream(inputStream);
                }
                //播放器釋放
                resetQuietly(audioTrack);
            }

            //循環讀資料,寫到播放器去播放


            //錯誤處理,防止閃退

        }
    }
           

釋放資源代碼

@Override
    protected void onDestroy() {
        super.onDestroy();
        if (mExecutorService != null) {
            mExecutorService.shutdownNow();
        }
        if (mAudioRecord != null) {
            mAudioRecord.stop();
            mAudioRecord.release();
            mAudioRecord = null;
        }
    }
           
  • MediaRecord錄制音頻

啟動錄音代碼實作

/**
     * 啟動錄音
     *
     * @return
     */

    private boolean doStart() {

        try {
            //建立MediaRecorder
            mMediaRecorder = new MediaRecorder();
            //建立錄音檔案
            mRecorderFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
                    + "/recorderdemo/" + System.currentTimeMillis() + ".m4a");
            if (!mRecorderFile.getParentFile().exists()) mRecorderFile.getParentFile().mkdirs();
            mRecorderFile.createNewFile();


            //配置MediaRecorder

            //從麥克風采集
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);

            //儲存檔案為MP4格式
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);

            //所有android系統都支援的适中采樣的頻率
            mMediaRecorder.setAudioSamplingRate();

            //通用的AAC編碼格式
            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);

            //設定音質頻率
            mMediaRecorder.setAudioEncodingBitRate();

            //設定檔案錄音的位置
            mMediaRecorder.setOutputFile(mRecorderFile.getAbsolutePath());


            //開始錄音
            mMediaRecorder.prepare();
            mMediaRecorder.start();
            startRecorderTime = System.currentTimeMillis();

        } catch (Exception e) {
            Toast.makeText(MediaRecordActivity.this, "錄音失敗,請重試", Toast.LENGTH_SHORT).show();
            return false;
        }


        //記錄開始錄音時間,用于統計時長,小于3秒中,錄音不發送

        return true;
    }
           

結束錄音代碼實作

/**
     * 關閉錄音
     *
     * @return
     */
    private boolean doStop() {
        try {
            mMediaRecorder.stop();
            stopRecorderTime = System.currentTimeMillis();
            final int second = (int) (stopRecorderTime - startRecorderTime) / ;
            //按住時間小于3秒鐘,算作錄取失敗,不進行發送
            if (second < ) return false;
            mHander.post(new Runnable() {
                @Override
                public void run() {
                    tv_sendmsg.setText("錄制成功:" + second + "秒");
                }
            });

        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }
           

播放音頻代碼實作

/**
     * 播放音頻檔案
     * @param audioFile
     */
    private void doPlay(File audioFile) {
        try {
            //配置播放器 MediaPlayer
            mediaPlayer = new MediaPlayer();
            //設定聲音檔案
            mediaPlayer.setDataSource(audioFile.getAbsolutePath());
            //配置音量,中等音量
            mediaPlayer.setVolume(,);
            //播放是否循環
            mediaPlayer.setLooping(false);

            //設定監聽回調 播放完畢
            mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
                @Override
                public void onCompletion(MediaPlayer mp) {
                    stopPlayer();
                }
            });

            mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
                @Override
                public boolean onError(MediaPlayer mp, int what, int extra) {
                    stopPlayer();
                    Toast.makeText(MediaRecordActivity.this,"播放失敗",Toast.LENGTH_SHORT).show();
                    return true;
                }
            });

            //設定播放
            mediaPlayer.prepare();
            mediaPlayer.start();

            //異常處理,防止閃退

        } catch (Exception e) {
            e.printStackTrace();
            stopPlayer();
        }


    }
           

完整demo源碼已上傳至GitHub,

位址:https://github.com/DevelopWb/Audio_Record