1. 程式人生 > >【android開發】實現語音資料實時採集/播放

【android開發】實現語音資料實時採集/播放

今天無意中看到一篇關於android實現語音資料實時採集/播放的文章,感覺寫得非常棒,挺全面的,所以特地轉載了,還有其實還可以根據這篇部落格內容考慮下視訊資料實時採集、播放的實現。部落格原文地址http://blog.csdn.net/lantingshuxu/article/details/53520316

最近做的專案是和語音實時採集併發送,對方實時接收並播放相關,下面記錄下實現的核心程式碼。 
很多Android開發者應該知道android有個MediaRecorder物件和MediaPlayer物件,用於錄製和播放音訊。這個弊端在於他們不能實時採集併發送出去,所以,我們只能使用AudioRecord和AudioTrack來實現。 
記得申明許可權:

<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" >
  • 1
  • 2
  • 1
  • 2

一、AudioRecord實現核心程式碼介紹如下: 
1、先申明相關錄製配置引數

private AudioRecord audioRecord;// 錄音物件
private int frequence = 8000;// 取樣率 8000
private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;// 定義取樣通道
private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;// 定義音訊編碼(16位) private byte[] buffer = null;// 錄製的緩衝陣列
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

2、在開始錄製前,我們需要初始化AudioRecord類。

// 根據定義好的幾個配置,來獲取合適的緩衝大小
// int bufferSize = 800;
int bufferSize = AudioRecord.getMinBufferSize(frequence,
        channelInConfig, audioEncoding);
// 例項化AudioRecord
audioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, frequence, channelInConfig, audioEncoding, bufferSize); // 定義緩衝陣列 buffer = new byte[bufferSize];
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

3、準備開始錄製,使用迴圈不斷讀取資料。

audioRecord.startRecording();// 開始錄製
isRecording = true;// 設定錄製標記為true

// 開始錄製
while (isRecording) {
// 錄製的內容放置到了buffer中,result代表儲存長度
int result = audioRecord.read(buffer, 0, buffer.length);
/*.....result為buffer中錄製資料的長度(貌似基本上都是640)。
剩下就是處理buffer了,是傳送出去還是直接播放,這個隨便你。*/
}

//錄製迴圈結束後,記得關閉錄製!!
if (audioRecord != null) {
    audioRecord.stop();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

二、AudioTrack程式碼實現介紹如下: 
1、宣告播放相關配置。

private AudioTrack track = null;// 錄音檔案播放物件
private int frequence = 8000;// 取樣率 8000
private int channelInConfig = AudioFormat.CHANNEL_CONFIGURATION_MONO;// 定義取樣通道
private int audioEncoding = AudioFormat.ENCODING_PCM_16BIT;// 定義音訊編碼(16位)
private int bufferSize = -1;// 播放緩衝大小
  • 1
  • 2
  • 3
  • 4
  • 5
  • 1
  • 2
  • 3
  • 4
  • 5

2、初始化AudioTrack物件(初始化一次,該物件可重複使用)

// 獲取緩衝 大小
bufferSize = AudioTrack.getMinBufferSize(frequence, channelInConfig,
        audioEncoding);
// 例項AudioTrack
track = new AudioTrack(AudioManager.STREAM_MUSIC, frequence,
        channelInConfig, audioEncoding, bufferSize,
        AudioTrack.MODE_STREAM);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3、使用AudioTrack播放語音資料。

//將語音資料寫入即可。
track.write(dataArray, buffer, len);
  • 1
  • 2
  • 1
  • 2

問題一: 
由於目前的專案是實時採集,實時傳送,所以需要考慮到包的大小,經測試,我們使用160個byte作為一個包傳遞可以做到比較良好的播放效果(也就是將一份buffer拆分成四個傳送)。處理程式碼如下:

// 將資料通過監聽介面回調出去
if (audioRecordingCallback != null) {
    int offset = result % MAX_DATA_LENGTH > 0 ? 1 : 0;
    //將一個buffer拆分成幾份小資料包 MAX_DATA_LENGTH 為包的最大byte數
    for (int i = 0; i < result / MAX_DATA_LENGTH + offset; i++) {
        int length = MAX_DATA_LENGTH;
        if ((i + 1) * MAX_DATA_LENGTH > result) {
            length = result - i * MAX_DATA_LENGTH;
        }
    //寫到回撥介面
    audioRecordingCallback.onRecording(buffer, i
            * MAX_DATA_LENGTH, length);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

問題二: 
有時候傳輸的過來播放聲音會一卡一卡的,為了解決這樣的問題,暫時使用了語音雙緩衝機制來解決,問題優化很明顯。程式碼和示意圖如下: 
雙緩衝示意圖