1. 程式人生 > >安卓實現錄音機的錄音功能

安卓實現錄音機的錄音功能

最近專案中需要做一個錄音機的功能,就去研究了一下,安卓中使用錄音功能使用的是MediaRecorder類,這個類在android.media包下。
該類的使用方法和MediaPlayer非常相似。

首先先初始化:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECORD_VIDEO" />
<uses-permission android:name="android.permission.RECORD_AUDIO"
/> 在清單檔案中新增上以上許可權 MediaRecorder mRecorder = new MediaRecorder(); //將mRecorder設定成空閒狀態 mRecorder.reset(); //設定要用於錄製的音訊源。 如果沒有這個方法呼叫,輸出檔案不會包含音軌。(注意該方法必須在setOutputFormat()方法之前) mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); //設定錄製時產生的輸出檔案的格式。(該方法不能再prepare()方法之後呼叫,不然會丟擲IllegalStateException異常) mRecorder.setOutputFormat
(MediaRecorder.OutputFormat.RAW_AMR); //設定要生成的輸出檔案的路徑。(簡單的說就是生成一個什麼檔名,放在哪裡) mRecorder.setOutputFile(mFileName); //我這裡的mFileName mFileName = getExternalCacheDir().getAbsolutePath(); mFileName += "/" + System.currentTimeMillis(); //找到絕對路徑,檔名取名為系統當前時間。(這裡注意呼叫setOutputFile()方法一定要在setOutputFormat()方法之後,在prepare()之前); //設定完路徑之後呼叫該方法 //設定要用於錄製的音訊編碼器。 如果沒有這個方法呼叫,輸出檔案不會包含音軌。 mRecorder.setAudioEncoder
(MediaRecorder.AudioEncoder.AMR_NB); //準備錄音機開始捕獲和編碼資料。(也就是準備狀態)這裡需要try catch一個IOException。 mRecorder.prepare(); //開始錄音 mRecorder.start();

以上就是錄音的全部程式碼,具體的內容註釋已經也已經寫得很清楚了。
呼叫了start方法只後就會看到在該路徑下生成了一個不斷變大的檔案,因為你錄的時間越長,當然檔案肯定也就越大了。
開啟DDMS試圖:

這裡寫圖片描述
我這裡開始了6次,所以就生成了6個檔案。

停止的話,呼叫stop()方法,
    release()方法是用來釋放掉mRecorder的一切資源,為了不浪費記憶體。
    if (mRecorder != null) {
    mRecorder.stop();
    mRecorder.release();
    mRecorder = null;
    }

呼叫了stop()方法後又重新呼叫start()方法會發現又生成了一個新的檔案。

當然我們做錄音機功能肯定是需要暫停的,怎麼辦呢???
我們找會發現有個pause()方法,如果需要暫停的

//顧名思義這方法就是暫停的意思,當然需要在呼叫了start();方法之後呼叫。
mRecorder.pause();

暫停了之後想要接著錄的話怎麼辦呢???
我們會發現又有個mRecorder.resume();方法。

//該方法也就是重新開始了意思,在呼叫了pause()方法後呼叫,如果在start()方法後呼叫,也沒什麼關係,只是不會有效果
mRecorder.resume();

以為到這裡就完了吧???NO NO NO,你會發現暫停這裡有個大坑,因為pause()方法是在API 25(安卓版本7.1.1)的時候才出來的,而resume()方法是在API 19(安卓版本4.4)的時候出來的,如果我們的程式需要向下相容的話怎麼辦呢,這個暫停的功能不就沒法實現了。(但是,你會發現在7.1.1之前也有暫停的功能的)他們是如何做到的呢???

當然他們肯定也不是因為能提前用API,他們也是點選了暫停之後呼叫stop();方法,每次點選開始的時候都生成了一個新的檔案,只不過他們把那些錄音檔案拼接起來當你點選儲存的時候拼接成了一個錄音檔案而已。

首先先建立一個集合用來裝每段錄音檔案的路徑

private ArrayList<String> list = new ArrayList<>();

每次點選開始的時候把檔案路徑add進集合中(也就是上面的mFileName)
因為每次點選開始的時候都給新的檔案取當前的時間的毫秒值,所以不會出現檔名重複而覆蓋掉的情況。

mFileName = getExternalCacheDir().getAbsolutePath();
                mFileName += "/" + System.currentTimeMillis();
                list.add(mFileName);
                startRecording();

    /**
     * 開始錄音
     */
    private void startRecording() {
        if (mRecorder == null) {
            mRecorder = new MediaRecorder();
        }
        mRecorder.reset();
        mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mRecorder.setOutputFormat(MediaRecorder.OutputFormat.RAW_AMR);
        mRecorder.setOutputFile(mFileName);
        mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        try {
            mRecorder.prepare();
        } catch (IOException e) {
            Log.e(TAG, e.toString());
        }
        mRecorder.start();
    }

當你點選儲存的時候呢,只需要把list集合給傳入以下方法中。

//第一個引數就是list集合,第二個引數就是合併後的檔名
public void getInputCollection(List list, String mMinute1) {

        // 建立音訊檔案,合併的檔案放這裡
        file1 = new File(getExternalCacheDir().getAbsolutePath(), mMinute1);
        FileOutputStream fileOutputStream = null;

        if (!file1.exists()) {
            try {
                file1.createNewFile();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            fileOutputStream = new FileOutputStream(file1);

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //list裡面為暫停錄音 所產生的 幾段錄音檔案的名字,中間幾段檔案的減去前面的6個位元組標頭檔案
        for (int i = 0; i < list.size(); i++) {
            File file = new File((String) list.get(i));
            Log.d("list的長度", list.size() + "");
            try {
                FileInputStream fileInputStream = new FileInputStream(file);
                byte[] myByte = new byte[fileInputStream.available()];
                //檔案長度
                int length = myByte.length;

                //標頭檔案
                if (i == 0) {
                    while (fileInputStream.read(myByte) != -1) {
                        fileOutputStream.write(myByte, 0, length);
                    }
                }

                //之後的檔案,去掉標頭檔案就可以了
                else {
                    while (fileInputStream.read(myByte) != -1) {

                        fileOutputStream.write(myByte, 6, length - 6);
                    }
                }

                fileOutputStream.flush();
                fileInputStream.close();
                System.out.println("合成檔案長度:" + file1.length());

            } catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }


        }
        //結束後關閉流
        try {
            fileOutputStream.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        //千萬記住這裡需要把集合給清空不然,下次錄的時候會把上次錄的給帶上了
        deleteListRecord();
    }

    /**
     * 合成一個檔案後,刪除之前暫停錄音所儲存的零碎合成檔案
     */
    private void deleteListRecord() {
        for (int i = 0; i < list.size(); i++) {
            File file = new File(list.get(i));
            if (file.exists()) {
                file.delete();
            }
        }
        //正在暫停後,繼續錄音的這一段音訊檔案
    }

這樣你就會發現在該路徑下新生成了一個錄音檔案,該錄音檔案就是你合併後想要的檔案。
(如有錯誤,及時提出修改。謝謝!!!)