ANDROID音頻系統散記之二:resample-1(SRC)

Android上的resample處理

默認的情況下,Android放音的採樣率固定爲44.1khz,錄音的採樣率固定爲8khz,因此底層的音頻設備驅動只需設置好這兩個固定的採樣率。如果上層傳過來的採樣率與其不符的話,則Android Framework層會對音頻流做resample(重採樣)處理。

Resample的大致流程如下:

 
AudioResample作爲最基本的類,回放和錄音resample最終都會調用到這個類;有興趣可以研究下resample的算法和實現,這裏不闡述。
AudioMixer僅僅提供給回放使用的,這個類功能不僅僅是resample了,混音、音量設置都由這個類實現的。

錄音:
1、 AudioFlinger::RecordThread是錄音線程類,每當有錄音請求時,進入AudioFlinger::openInput創建這個線程;
2、 在創建這個線程的同時,調用readInputParameters,檢查上層傳過來的錄音採樣率是否與底層音頻接口固定的錄音採樣率一致;如果不一致,則調用AudioResampler::create創建一個resampler;
3、 關於AudioFlinger::RecordThread::ThreadLoop,當錄音工作線程啓動後,會不斷循環該ThreadLoop方法,主要是:1)讀取底層音頻設備獲取錄音數據mBytesRead = mInput->read(mRsmpInBuffer, mInputBytes); 2)對錄音數據做重採樣 mResampler->resample(mRsmpOutBuffer, framesOut, this);

放音:
1、 AudioFlinger::MixerThread是默認的放音線程,派生自PlaybackThread,由AudioFlinger::openOutput負責創建;
2、 MixerThread創建時,1)調用readOutputParameters獲取底層音頻接口固定的放音採樣率;2)創建一個AudioMixer;
3、 關於AudioFlinger::MixerThread:: ThreadLoop,當放音工作線程啓動後,會不斷循環該TreadLoop方法,主要是:1)混合各track音頻數據mAudioMixer->process(); 2)將混合後的音頻數據寫到底層音頻設備int bytesWritten = (int)mOutput->write(mMixBuffer, mixBufferSize);

Android默認的採樣率


之前有提及Android默認情況下,放音採樣率是44.1khz,錄音採樣率是8khz,分別通過AudioStreamIn::sampleRate()和AudioStreamOut::sampleRate()獲得。

一步一步跟下去,會發現是alsa_default.cpp裏面固定好的:
  1. // 放音參數配置   
  2. static alsa_handle_t _defaultsOut = {  
  3.     module      : 0,  
  4.     devices     : AudioSystem::DEVICE_OUT_ALL,  
  5.     curDev      : 0,  
  6.     curMode     : 0,  
  7.     handle      : 0,  
  8.     format      : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT  
  9.     channels    : 2,  
  10.     sampleRate  : DEFAULT_SAMPLE_RATE, // 放音採樣率,固定爲44.1khz  
  11.     latency     : 200000, // Desired Delay in usec  
  12.     bufferSize  : DEFAULT_SAMPLE_RATE / 5, // Desired Number of samples  
  13.     modPrivate  : 0,  
  14. };  
  15.   
  16.   
  17. // 錄音參數配置   
  18. static alsa_handle_t _defaultsIn = {  
  19.     module      : 0,  
  20.     devices     : AudioSystem::DEVICE_IN_ALL,  
  21.     curDev      : 0,  
  22.     curMode     : 0,  
  23.     handle      : 0,  
  24.     format      : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT  
  25.     channels    : 1,  
  26.     sampleRate  : AudioRecord::DEFAULT_SAMPLE_RATE, // 錄音採樣率,固定爲8khz  
  27.     latency     : 250000, // Desired Delay in usec  
  28.     bufferSize  : 2048, // Desired Number of samples  
  29.     modPrivate  : 0,  
  30. };  
// 放音參數配置
static alsa_handle_t _defaultsOut = {
    module      : 0,
    devices     : AudioSystem::DEVICE_OUT_ALL,
    curDev      : 0,
    curMode     : 0,
    handle      : 0,
    format      : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
    channels    : 2,
    sampleRate  : DEFAULT_SAMPLE_RATE, // 放音採樣率,固定爲44.1khz
    latency     : 200000, // Desired Delay in usec
    bufferSize  : DEFAULT_SAMPLE_RATE / 5, // Desired Number of samples
    modPrivate  : 0,
};


// 錄音參數配置
static alsa_handle_t _defaultsIn = {
    module      : 0,
    devices     : AudioSystem::DEVICE_IN_ALL,
    curDev      : 0,
    curMode     : 0,
    handle      : 0,
    format      : SND_PCM_FORMAT_S16_LE, // AudioSystem::PCM_16_BIT
    channels    : 1,
    sampleRate  : AudioRecord::DEFAULT_SAMPLE_RATE, // 錄音採樣率,固定爲8khz
    latency     : 250000, // Desired Delay in usec
    bufferSize  : 2048, // Desired Number of samples
    modPrivate  : 0,
};

網上有談到Android這種音頻resample方式導致音質變差,我想修改起來應該也不是很難,以後找機會實踐一下。思路:先嚐試用上層傳過來的錄音採樣率來設置底層音頻設備,如果設置成功則不需要resample,不成功才使用默認的採樣率。放音暫不能這樣改,因爲放音可能要混合多個track的數據,而各個track的採樣率不一定是一樣的。


ALSA中resample處理??


這章節我是用問號的,因爲在這裏我的確困惑了,先細細道來。

一般來說,resample的流程是這樣:

  1. +---------------------+               +-------------------------------+  
  2. |   app sample rate   |               | Android Framework sample rate |  
  3. |      16khz          | <--resample-- |            8khz               | <--ALSA interface  
  4. +---------------------+               +-------------------------------+  
+---------------------+               +-------------------------------+
|   app sample rate   |               | Android Framework sample rate |
|      16khz          | <--resample-- |            8khz               | <--ALSA interface
+---------------------+               +-------------------------------+

那麼ALSA interface用8khz的採樣率進行錄音就好。


但事實上我發現底層音頻設備用其他的採樣率也是可以的。比如說44.1khz,我測量到I2S的ADCLRC是44.1khz並且CODEC的寄存器也是設置爲44.1khz錄音採樣率,就這樣Android應用錄出來的聲音也是正常的。

可明明在alsa_default.cpp中設置的採樣率是8khz的,而且設置成功:

  1. err = snd_pcm_hw_params_set_rate_near(handle->handle, hardwareParams,  
  2.             &requestedRate, 0);  
err = snd_pcm_hw_params_set_rate_near(handle->handle, hardwareParams,
            &requestedRate, 0);

返回來的requestedRate是8000,這是Android Framework固定的錄音採樣率。

那麼到底在哪裏進行了44.1khz->8khz的resample處理?唯一是在alsa-lib或alsa-driver裏面了,到目前爲止我還未找到相關代碼。


--------------------------------------------------------------------------------------------------------------------

2011/10/21

近期瑣事頗多,找房子租房子搬家一團糟,昨天終於全部搞定了。

回到正題,有關alsa的resample處理,應該是在alsa-lib中實現的。摘錄別人的分析,如下:

  1. snd_pcm_mmap_write_areas()函數循環寫入數據,直到數據爲空,首先將找到映射內存pcm->running_areas的地址,然後調用snd_pcm_areas_copy()進行數據轉換,如sample rate、channels等(如果源數據和硬件支持格式一致,就簡單地通過memcpy拷貝數據),轉換成硬件支持格式對應的數據後,調用snd_pcm_mmap_commit()將轉換後的數據寫入映射內存。寫入由snd_pcm_dmix_mmap_commit()完成,在對數據進行混音(do_mix_areas)同時,寫入映射內存。  
snd_pcm_mmap_write_areas()函數循環寫入數據,直到數據爲空,首先將找到映射內存pcm->running_areas的地址,然後調用snd_pcm_areas_copy()進行數據轉換,如sample rate、channels等(如果源數據和硬件支持格式一致,就簡單地通過memcpy拷貝數據),轉換成硬件支持格式對應的數據後,調用snd_pcm_mmap_commit()將轉換後的數據寫入映射內存。寫入由snd_pcm_dmix_mmap_commit()完成,在對數據進行混音(do_mix_areas)同時,寫入映射內存。


我不能保證以上分析是否完全正確(事實上我跟蹤了一下,是有些出入)。由於alsa-lib太過臃腫複雜,我也懶得去細細分析,以後如果有空去研究的話,會對這裏的說法作一些更正。

拋棄alsa-lib,其實我們還是有其他選擇的,還記得ANDROID2.3音頻系統HAL提到的mini alsa-lib(android2.3.1-gingerbread/device/samsung/crespo/libaudio)嗎?我們可以從這裏分析,由於篇幅可能比較長,就獨立成章吧,待整理。。。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章