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

來源:互聯網
上載者:User
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裡面固定好的:[cpp]
view plaincopy

  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. };  

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

ALSA中resample處理??

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

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

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

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


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

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

[cpp]
view plaincopy
  1. err = snd_pcm_hw_params_set_rate_near(handle->handle, hardwareParams,  
  2.             &requestedRate, 0);  

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

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

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

2011/10/21

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

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

[plain]
view plaincopy
  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)同時,寫入映射記憶體。  


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

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

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裡面固定好的:[cpp]
view plaincopy

  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. };  

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

ALSA中resample處理??

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

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

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

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


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

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

[cpp]
view plaincopy
  1. err = snd_pcm_hw_params_set_rate_near(handle->handle, hardwareParams,  
  2.             &requestedRate, 0);  

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

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

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

2011/10/21

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

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

[plain]
view plaincopy
  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)同時,寫入映射記憶體。  


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

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

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.