基礎(chǔ)知識(shí)
PCM(Pulse Code Modulation),脈沖編碼調(diào)制。人耳聽到的是模擬信號(hào),PCM是把聲音從模擬信號(hào)轉(zhuǎn)化為數(shù)字信號(hào)的技術(shù)。簡單來說就是一種無壓縮編碼
采樣頻率、量化精度(采樣位數(shù))和聲道數(shù):
-
采樣頻率
是設(shè)備一秒鐘內(nèi)對(duì)模擬信號(hào)的采樣次數(shù),在主流的采集卡上分為:(8Khz的電話采樣率就可以達(dá)到人的對(duì)話程度)
22.05KHz:無線電廣播;
44.1KHz:音頻 CD,MP3等;
48KHz:miniDV、數(shù)字電視、DVD、電影和專業(yè)音頻。
采樣位數(shù)
它是用來衡量聲音波動(dòng)變化的一個(gè)參數(shù),也可以說是聲卡的分辨率。它的數(shù)值越大,分辨率也就越高,所發(fā)出聲音的能力越強(qiáng)。
在計(jì)算機(jī)中采樣位數(shù)一般有8位和16位之分,8位不是說把縱坐標(biāo)分成8份,而是分成2的8次方即256份; 同理16位是把縱坐標(biāo)分成2的16次方65536份。聲道數(shù)
即聲音的通道的數(shù)目。有單聲道和立體聲之分,單聲道的聲音只能使用一個(gè)喇叭發(fā)聲(有的也處理成兩個(gè)喇叭輸出同一個(gè)聲道的聲音),立體聲的PCM可以使兩個(gè)喇叭都發(fā)聲(一般左右聲道有分工) ,更能感受到空間效果。
單聲道 采樣數(shù)據(jù)為8位的短整數(shù)(short);
雙聲道 采樣數(shù)據(jù)為16位的整數(shù),(int),高八位(左聲道)和低八位(右聲道)分別代表兩個(gè)聲道。存儲(chǔ)量= (采樣頻率 * 采樣位數(shù) * 聲道 * 時(shí)間)/8 (單位:字節(jié)數(shù))。
[時(shí)長]s * [采樣率]Hz * [采樣位數(shù)]bit * [聲道數(shù)] / 8 = [文件大小]byte
某音頻信號(hào)是采樣率為8kHz、聲道數(shù)、位寬為16bit,時(shí)長為1s,則音頻數(shù)據(jù)的大小為:
1 * 8000 * 16 *2 = 256000 bit / 8 = 32000 byte / 1024 = 31.25 KB
AudioRecorder
是 Android 中一種音頻采集的方式,另外一種是 MediaRecorder
MediaRecorder:錄制的音頻文件是經(jīng)過壓縮后的,需要設(shè)置編碼器。并且錄制的音頻文件可以用系統(tǒng)自帶的Music播放器播放。
優(yōu)點(diǎn):官方提供 API
缺點(diǎn): 不能實(shí)時(shí)處理音頻,輸出格式不多,且PCM可以處理生成
AudioRecord: 錄制的是PCM格式的音頻文件,需要用AudioTrack來播放。
優(yōu)點(diǎn):可以實(shí)時(shí)獲取音頻的數(shù)據(jù)做到邊錄邊播放,可以對(duì)獲取的音頻做處理,壓縮,傳輸?shù)?br>
缺點(diǎn):輸出的是原始數(shù)據(jù) PCM 所以播放器不能播放,需要通過AudioTrack處理
開始采集
采集音頻的步驟:
1.配置 AudioRecorder 構(gòu)造函數(shù)的參數(shù)
2.初始化緩沖區(qū)
3.開始采集 ,子線程里將緩沖區(qū)的數(shù)據(jù)取出,寫入文件流
4.停止采集,釋放資源
從 AudioRecord 的構(gòu)造函數(shù)開始
audioSource: 音頻采集的輸入源,可選的值以常量的形式定義在 MediaRecorder.AudioSource 類中,例如:MIC(由手機(jī)麥克風(fēng)輸入),VOICE_COMMUNICATION(用于VoIP應(yīng)用)等等。
sampleRateInHz:采樣率,注意,目前44100Hz是唯一可以保證兼容所有Android手機(jī)的采樣率。
channelConfig: 通道數(shù)的配置,可選的值以常量的形式定義在 AudioFormat 類中,常用的是CHANNEL_IN_MONO(單通道),CHANNEL_IN_STEREO(雙通道)
audioFormat: 返回的音頻數(shù)據(jù)的格式,可選的值也是以常量的形式定義在 AudioFormat 類中,常用的是 ENCODING_PCM_16BIT(16bit),ENCODING_PCM_8BIT(8bit),注意,前者是可以保證兼容所有Android手機(jī)的。
bufferSizeInBytes: AudioRecord 內(nèi)部的音頻緩沖區(qū)的大小,該緩沖區(qū)的值不能低于一幀“音頻幀”(Frame)的大小
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes)
throws IllegalArgumentException {
this((new AudioAttributes.Builder())
.setInternalCapturePreset(audioSource)
.build(),
(new AudioFormat.Builder())
.setChannelMask(getChannelMaskFromLegacyConfig(channelConfig,
true/*allow legacy configurations*/))
.setEncoding(audioFormat)
.setSampleRate(sampleRateInHz)
.build(),
bufferSizeInBytes,
AudioManager.AUDIO_SESSION_ID_GENERATE);
}
配置
參數(shù)配置:
/**
* 采樣率。現(xiàn)在能夠保證在所有設(shè)備上使用的采樣率是44100Hz
*/
public static final int SAMPLE_RATE_INHZ = 44100;
/**
* 輸入聲道數(shù)。CHANNEL_IN_MONO and CHANNEL_IN_STEREO. 其中CHANNEL_IN_MONO是可以保證在所有設(shè)備能夠使用的。
*/
public static final int CHANNEL_CONFIG = AudioFormat.CHANNEL_IN_MONO;
/**
* 返回的音頻數(shù)據(jù)的格式。 ENCODING_PCM_8BIT, ENCODING_PCM_16BIT, and ENCODING_PCM_FLOAT.
*/
public static final int ENCODING_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
緩沖區(qū):
AudioRecord 提供了一個(gè)類為我們計(jì)算最小緩沖區(qū),參數(shù)就是我們上面配置的 采樣率,聲道, 返回的音頻數(shù)據(jù)的格式
int minBufferSize = AudioRecord.getMinBufferSize(Config.SAMPLE_RATE_INHZ, Config.CHANNEL_CONFIG, Config.ENCODING_FORMAT);
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
switch (channelConfig) {
case AudioFormat.CHANNEL_IN_DEFAULT: // AudioFormat.CHANNEL_CONFIGURATION_DEFAULT
case AudioFormat.CHANNEL_IN_MONO:
case AudioFormat.CHANNEL_CONFIGURATION_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_IN_STEREO:
case AudioFormat.CHANNEL_CONFIGURATION_STEREO:
case (AudioFormat.CHANNEL_IN_FRONT | AudioFormat.CHANNEL_IN_BACK):
channelCount = 2;
break;
case AudioFormat.CHANNEL_INVALID:
default:
loge("getMinBufferSize(): Invalid channel configuration.");
return ERROR_BAD_VALUE;
}
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
if (size == 0) {
return ERROR_BAD_VALUE;
}
else if (size == -1) {
return ERROR;
}
else {
return size;
}
}
開始采集,子線程讀數(shù)據(jù)寫入文件流
直接調(diào)用創(chuàng)建好的 AudioRecorder 對(duì)象的 startRecording();
//開始采集
mAudioRecorder.startRecording();
//需要再子線程里面調(diào)用(讀和存)
AudioRecord.read(byte[] audioData, int offsetInBytes, int sizeInBytes);
FileOutputStream 的 write()
停止采集
mAudioTrack.stop();
mAudioRecorder.release();
AudioTrack
開始播放
開始播放步驟:
1.配置參數(shù)
2.配置緩沖區(qū)
3.開啟子線程,把緩沖區(qū)讀數(shù)據(jù)轉(zhuǎn)換成輸入流,再調(diào)用AudioTrack讀 write()寫入數(shù)據(jù),最后調(diào)用 play()
4.結(jié)束釋放資源
配置參數(shù):
參數(shù)和 AudioRecorder 差不多,有區(qū)別的就是 AudioTrack 是輸出聲道,還要播放的類型,和播放的模式
streamType:播放的類型,都定義在 AudioManager 類中
mode: 播放的模式, MODE_STATIC, MODE_STREAM 兩種
兩者的區(qū)別
/** 在播放之前必須把數(shù)據(jù)全部加載完成
* Creation mode where audio data is transferred from Java to the native layer
* only once before the audio starts playing.
*/
public static final int MODE_STATIC = 0;
/**
*可以一邊錄音一邊播放
* Creation mode where audio data is streamed from Java to the native layer
* as the audio is playing.
*/
public static final int MODE_STREAM = 1;
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode)
throws IllegalArgumentException {
this(streamType, sampleRateInHz, channelConfig, audioFormat,
bufferSizeInBytes, mode, AudioManager.AUDIO_SESSION_ID_GENERATE);
}
緩沖區(qū)
和上面的 AudioRecorder 的配置一樣
開啟子線程,把緩沖區(qū)數(shù)據(jù)轉(zhuǎn)換成輸入流,再調(diào)用AudioTrack讀 write()寫入數(shù)據(jù),最后調(diào)用 play()
int readCount = inStream.read(mBuffer);
mAudioTrack.write(mBuffer, 0, readCount);
mAudioTrack.play();
停止釋放資源
mAudioTrack.stop();
mAudioTrack.release();