標籤:

Android下音頻tinyalsa(tinymix/tinycap/tinyplay/tinypcminfo)

audio代碼比較複雜,除了音頻參數,我們平時客制化的地方不多。所以沒有太深入了解。建議先抽空看看如下代碼:kernel,Linux alsa架構:kernel-3.10/sound/soc/mediatek/kernel-3.10/Documentation/sound/alsa/soc/Android 上層alsa介面external/tinyalsa/hal:vendor/mediatek/proprietary/platform/common/hardware/audio/vendor/mediatek/proprietary/platform/mt6735/hardware/audio/andorid audio flinger:frameworks/av/services/audioflinger/tinyalsa位於Android源碼的external/tinyalsa位置。關於tinyalsa,tinyalsa是Google在Android 4.0之後推的基於alsa內核的用戶層音頻介面。在Android 4.0之前還一直是使用這alsa-lib介面。Google之所以推出tinyalsa,我認為有可能是因為alsa使用了GPL許可證的緣故,也有可能是因為alsa-lib的庫過於複雜繁瑣大部分功能在Android平台沒有實際實用意義卻依然佔用屈指可數的內存空間。關於alsa在Android中,在Android 4.0及之後只要你願意還是可以使用原版alsa的,因為內核中依然是使用alsa的驅動,只需要把alsa的用戶層介面alsa-lib移植到源碼中即可。tinyalsa中主要的頭文件和數據結構如下,通過ioctrl和內核的alsa驅動交互。

pcm設備,通過閱讀tinyalsa的代碼和查看Android下的音頻設備節點,可知在Android中一個pcm設備最多可有一個mixer設備"/dev/snd/controlC%u"(一般是controlC0)和32個/dev/snd/pcmC%uD%uc(一般是pcmC0D0c)、/dev/snd/pcmC%uD%u%p(一般是pcmC0D0p),pcm設備中的C代表card,D代表device,c代表capture,p代表playback。當我們新增一個pcm音效卡C的值會+1,D還是從0開始,可能只有c(pcmC1D0c 例如麥克風),可能只有p(pcmC1D0p 例如音響),可能同時存在c和p(pcmC1D0cpcmC1D0p)。tinyalsa的對外提供的頭文件就我上圖提到的一個"asoundlib.h",提供最基礎的pcm和mixer操作。實現文件為pcm.c(實現pcm api)和mixer.c(實現mixer api)。根據asoundlib.h編寫了四個小工具tinypcminfo tinyplay tinycap tinymix,這四個小工具作為系統命令存放在系統中,可以很方便的使用。tinyasla作為精簡版的alsa-lib可能會有人想把它移植到Linux使用,tinyasla依賴的庫有libcutils && libutils,如果能把依賴的這兩個庫的一些方法使用Linux介面實現那麼剩下的問題應該不大了吧,這個僅僅是我的猜想。tinypcminfo的實現文件tinypcminfo.c (查看pcm設備能力)tinyplay的實現文件tinyplay.c(使用pcm設備播放wav格式的音頻文件)tinycap的實現文件tinycap.c(使用pcm設備採集pcm格式的碼流,並保存為wav格式的文件)tinymix的實現文件tinymix.c(對pcm設備的控制,包括音量調節、設備切換)這幾個命令使用時可以先使用tinypcminfo查看pcm設備的能力,要不當我們使用其他三個命令時使用了不合理的配置會出現parameter invalid的錯誤。PCM API/* 對pcm設備節點的操作 */1struct pcm *pcm_open(unsigned int card, unsigned int device, unsigned int flags, struct pcm_config *config);1int pcm_close(struct pcm *pcm);1int pcm_is_ready(struct pcm *pcm);11/* 獲取pcm設備的能力 */1struct pcm_params *pcm_params_get(unsigned int card, unsigned int device, unsigned int flags);1void pcm_params_free(struct pcm_params *pcm_params);1unsigned int pcm_params_get_min(struct pcm_params *pcm_params, enum pcm_param param);1unsigned int pcm_params_get_max(struct pcm_params *pcm_params, enum pcm_param param);11/* 配置pcm設備capture和playback的規格 */1int pcm_get_config(struct pcm *pcm, struct pcm_config *config);1int pcm_set_config(struct pcm *pcm, struct pcm_config *config);11/* 返回調用tinyalsa最後的錯誤信息 */1const char *pcm_get_error(struct pcm *pcm);11/* 設置pcm設備採集和播放的位數,位數越高越接近真實聲音 */1unsigned int pcm_format_to_bits(enum pcm_format format);11/* pcm設備的內置緩衝之間大小、幀數、時間的轉換 */1unsigned int pcm_get_buffer_size(struct pcm *pcm);1unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames);1unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes);1unsigned int pcm_get_latency(struct pcm *pcm); // in ms11/* 返回下一幀的有效幀指針和該幀時間戳,時間戳有CLOCK_MONOTONIC和CLOCK_REALTIME可選,裡面使用的是CLOCK_REALTIME */1/* 我認為應該需要兩個時間戳,一個用於播放的時間戳(CLOCK_MONOTONIC)不受系統時間的影響,一個用於記錄當前音頻採集的時間戳(CLOCK_REALTIME) */1int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, struct timespec *tstamp);11/* 通過FIFO把數據寫入硬體用於playback或者從硬體中讀取capture數據 */1int pcm_write(struct pcm *pcm, const void *data, unsigned int count);1int pcm_read(struct pcm *pcm, void *data, unsigned int count);11/* 這是一個可選的和hardware通信的方式。 */1int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);1int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count);1int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, unsigned int *frames);1int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames);1int pcm_start(struct pcm *pcm);1int pcm_stop(struct pcm *pcm);1int pcm_wait(struct pcm *pcm, int timeout);1int pcm_set_avail_min(struct pcm *pcm, int avail_min);1對於pcm設備的操作只需要注意所操作的設備是否存在,以及設備的能力問題不要設置設備所不能及的能力設備就可以正常工作了,其實就是需要注意channels、rate、format、period_size和period_count。MIXER API/* 對mixer設備的操作 */1struct mixer *mixer_open(unsigned int card);1void mixer_close(struct mixer *mixer);11/* 獲取mixer設備信息name */1const char *mixer_get_name(struct mixer *mixer);11/* 獲取mixer設備的控制句柄 struct mixer_ctl */1unsigned int mixer_get_num_ctls(struct mixer *mixer);1struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id);1struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name);11/* 取mixer設備的控制信息 */1const char *mixer_ctl_get_name(struct mixer_ctl *ctl);1enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl);1const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl);1unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl);1unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl);1const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl, unsigned int enum_id);11/* Some sound cards update their controls due to external events,1* such as HDMI EDID byte data changing when an HDMI cable is1* connected. This API allows the count of elements to be updated.1*/1void mixer_ctl_update(struct mixer_ctl *ctl);11/* 設置和獲取可控制的信息,方式有比例、數組、範圍、固定值 */1/* id通過mixer_get_num_ctls獲得 */1int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id);1int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent);11int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id);1int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count);1int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value);1int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count);1int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string);11int mixer_ctl_get_range_min(struct mixer_ctl *ctl);1int mixer_ctl_get_range_max(struct mixer_ctl *ctl);1MIXER使用,有哪些屬性,幹什麼用的,看名字就已經很清楚了。// 原始狀態1root@pone:/data/local/tmp # tinymix -D 1 1Mixer name: "mixer_name_xx"1Number of controls: 21ctl type num name value10 BOOL 1 Mic Capture Switch Off11 INT 1 Mic Capture Volume 25611// 詳細信息1root@pone:/data/local/tmp # tinymix -D 1 0 1Mic Capture Switch: Off1root@pone:/data/local/tmp # tinymix -D 1 1 1Mic Capture Volume: 256 (range 0->256)11// 設置1root@pone:/data/local/tmp # tinymix -D 1 0 1 1root@pone:/data/local/tmp # tinymix -D 1 1 25011// 修改後的狀態 1root@pone:/data/local/tmp # tinymix -D 1 1Mixer name: "mixer_name_xx"1Number of controls: 21ctl type num name value10 BOOL 1 Mic Capture Switch On11 INT 1 Mic Capture Volume 2501擴展:1. 什麼是pcmhttp://en.wikipedia.org/wiki/Pulse-code_modulation2. 音頻採樣http://baike.baidu.com/link?url=b9CLvaPgU4A0e0X3y5cLU4ADOI0eBuUeCys05m5unE7G-0eR2sQMBVhNxkCEvOf-Ot2CLAw2wWX5ZLhqi3D2t_#1目前Linux中主流的音頻體系結構是ALSA(Advanced Linux Sound Architecture),ALSA在內核驅動層提供了alsa-driver,在應用層提供了alsa-lib,應用程序只需要調用alsa-lib提供的API就可以完成對底層硬體的操作。說的這麼好,但是Android中沒有使用標準的ALSA,而是一個ALSA的簡化版叫做tinyalsa。Android中使用tinyalsa控制管理所有模式的音頻通路,我們也可以使用tinyalsa提供的工具進行查看、調試。編譯tinyalsa後生成四個小工具:[objc]view plaincopytinymixtinyplaytinycaptinypcminfo編譯命令:[objc]view plaincopymmmexternal/tinyalsa/

下面依次演示一下四個小工具的使用:(以下使用聯芯科技的LC1860平台配合LC1160電源+音頻晶元,截圖及演示效果均來自M7301P5測試機)1, tinypcminfotinypcminfo用於查看pcm通道的相關信息輸入:[objc]view plaincopytinypcminfo-D/proc/asound/cards結果如下:

從上面獲得的信息中可以知道PCM的採樣率,通道個數,採樣點數等信息。其中 –D 後邊跟的參數為音效卡文件,一般位於/proc/asound/cards。可以使用[objc]view plaincopycat/proc/asound/cards查看當前音效卡

2, tinymix如下圖所示,直接輸入tinymix可以得到音頻通路相關的各項配置參數。也可以通過添加參數修改其中的配置,如希望提高ADC1 Gain值到110,輸入tinymix 12 110即可。

單獨查看上述信息比較難以梳理,配合音頻通路圖會更加清晰。

上圖中紅色字體標明了LC1160與麥克風、耳機等硬體設備的連接關係。(註:M73xx項目由於內部ClassD不滿足要求,喇叭連在了AUX通路上)各個通路上的增益調節部分使用綠色字體標出了與tinymix中選項的對應關係。圖中加號與Mux是通路選擇開關,對應tinymix中的其它的選項,用於在各種模式下切換音頻通道。此部分比較多沒有在圖中一一標出,但根據已知的通路名是比較容易對應的。圖中黃色箭頭標出的是通話時下行音頻數據流,從PCM介面進入到LC1160,然後經過MonoDAC進行數模轉化後送到聽筒。圖中紫色箭頭標出的是通話時上行音頻數據流,從主MIC採集聲音後,經過ADC1進行模數轉換後由PMC的DO線送出3, tinyplaytinyplay是一個簡易的音樂播放器,一般用於播放測試。tinyplay只能播放wav原始格式的音樂,不能進行Mp3等格式的解碼,支持44.1kHz,48kHz採樣率的wav音樂。在調用tinyplay播放音樂之前需要先使用tinymix切換好音頻通路:[objc]view plaincopytinymix0I2SR//選擇StereoDACR的音源為i2stinymix1I2SL//選擇StereoDACL的音源為i2stinymix200//將StereoDAC左右聲道的mute關閉tinymix241//開啟喇叭的外部PA晶元tinymix401//將StereoDACR的聲音路由到AUX口輸出(因為實驗機器喇叭掛載AUX介面上)tinymix411//將StereoDACR的聲音路由到AUX口輸出(因為實驗機器喇叭掛載AUX介面上)tinyplayz.wav

4, tinycaptinycap是一個簡易的錄音軟體,一般用於錄音測試。在調用tinycap錄音之前需要先調整好音頻通路:[objc]view plaincopytinymix1430//mic1volumetinymix191//mic1boostontinymix261//adc1->mic1tinymix50ADC1//i2sRout->adc1tinymix51ADC1//i2sLout->adc2echo"0xfb0x01">/sys/devices/platform/comip_codec/lc1160_reg//biaspoweronecho"0xad0x08">/sys/devices/platform/comip_codec/lc1160_reg//adc1enableecho"0xac0x01">/sys/devices/platform/comip_codec/lc1160_reg//mic1pgaenableecho"0x3b0xcc">/sys/devices/platform/comip_codec/lc1160_reg//ldoecho2>/sys/bus/i2c/drivers/fm2018/0-0060/mode//bypass外部的回聲消除音頻晶元(M730x項目特有)tinycap/sdcard/Music/l.wav錄音結束通過ctrl+C強行退出即可,之後在/sdcard/Music/路徑下查看到l.wav音頻文件

使用adb pull到本地電腦中,使用goldwave播放、查看。[objc]view plaincopyadbpull/sdcard/Music/l.wavd:

另外:LC1160的寄存器是分頁的,即同一個地址上存在兩個不同含義的寄存器,通過控制0xFC寄存器中的值來切換到第二功能頁[objc]view plaincopyecho"0xfc0x01">/sys/devices/platform/comip_codec/lc1160_regcat/sys/devices/platform/comip_codec/lc1160_regecho"0xfc0x00">/sys/devices/platform/comip_codec/lc1160_reg
推薦閱讀:

Android UI 設計工具 (Photoshop, Android 4.4, Nexus 4)
極光日報 第 169 期 | 2017 / 5 / 4
Android 的 Rom 如何有效的推廣,小米的 Rom 推廣有何特點和值得學習的地方?
[Android插件化]移動開發國人與國外開發方式的區別?

TAG:Android | 音頻 |