天天看點

Android Tinyalsa

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驅動互動。

Android Tinyalsa

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( pcmC1D0c    pcmC1D0p   )。

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裝置節點的操作 */              struct pcm *pcm_open(unsigned int card, unsigned int device, unsigned int flags, struct pcm_config *config);              int pcm_close(struct pcm *pcm);              int pcm_is_ready(struct pcm *pcm);                  /* 擷取pcm裝置的能力 */              struct pcm_params *pcm_params_get(unsigned int card, unsigned int device, unsigned int flags);              void pcm_params_free(struct pcm_params *pcm_params);              unsigned int pcm_params_get_min(struct pcm_params *pcm_params, enum pcm_param param);              unsigned int pcm_params_get_max(struct pcm_params *pcm_params, enum pcm_param param);                  /* 配置pcm裝置capture和playback的規格 */              int pcm_get_config(struct pcm *pcm, struct pcm_config *config);              int pcm_set_config(struct pcm *pcm, struct pcm_config *config);                  /* 傳回調用tinyalsa最後的錯誤資訊 */              const char *pcm_get_error(struct pcm *pcm);                  /* 設定pcm裝置采集和播放的位數,位數越高越接近真實聲音 */              unsigned int pcm_format_to_bits(enum pcm_format format);                  /* pcm裝置的内置緩沖之間大小、幀數、時間的轉換 */              unsigned int pcm_get_buffer_size(struct pcm *pcm);              unsigned int pcm_frames_to_bytes(struct pcm *pcm, unsigned int frames);              unsigned int pcm_bytes_to_frames(struct pcm *pcm, unsigned int bytes);              unsigned int pcm_get_latency(struct pcm *pcm); // in ms                  /* 傳回下一幀的有效幀指針和該幀時間戳,時間戳有CLOCK_MONOTONIC和CLOCK_REALTIME可選,裡面使用的是CLOCK_REALTIME */              /* 我認為應該需要兩個時間戳,一個用于播放的時間戳(CLOCK_MONOTONIC)不受系統時間的影響,一個用于記錄目前音頻采集的時間戳(CLOCK_REALTIME) */              int pcm_get_htimestamp(struct pcm *pcm, unsigned int *avail, struct timespec *tstamp);                  /* 通過FIFO把資料寫入硬體用于playback或者從硬體中讀取capture資料 */              int pcm_write(struct pcm *pcm, const void *data, unsigned int count);              int pcm_read(struct pcm *pcm, void *data, unsigned int count);                  /* 這是一個可選的和hardware通信的方式。 */              int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);              int pcm_mmap_read(struct pcm *pcm, void *data, unsigned int count);              int pcm_mmap_begin(struct pcm *pcm, void **areas, unsigned int *offset, unsigned int *frames);              int pcm_mmap_commit(struct pcm *pcm, unsigned int offset, unsigned int frames);              int pcm_start(struct pcm *pcm);              int pcm_stop(struct pcm *pcm);              int pcm_wait(struct pcm *pcm, int timeout);              int pcm_set_avail_min(struct pcm *pcm, int avail_min);           

對于pcm裝置的操作隻需要注意所操作的裝置是否存在,以及裝置的能力問題不要設定裝置所不能及的能力裝置就可以正常工作了,其實就是需要注意channels、rate、format、period_size和period_count。

MIXER API

/* 對mixer裝置的操作 */              struct mixer *mixer_open(unsigned int card);              void mixer_close(struct mixer *mixer);                  /* 擷取mixer裝置資訊name */              const char *mixer_get_name(struct mixer *mixer);                  /* 擷取mixer裝置的控制句柄 struct mixer_ctl */              unsigned int mixer_get_num_ctls(struct mixer *mixer);              struct mixer_ctl *mixer_get_ctl(struct mixer *mixer, unsigned int id);              struct mixer_ctl *mixer_get_ctl_by_name(struct mixer *mixer, const char *name);                  /* 取mixer裝置的控制資訊 */              const char *mixer_ctl_get_name(struct mixer_ctl *ctl);              enum mixer_ctl_type mixer_ctl_get_type(struct mixer_ctl *ctl);              const char *mixer_ctl_get_type_string(struct mixer_ctl *ctl);              unsigned int mixer_ctl_get_num_values(struct mixer_ctl *ctl);              unsigned int mixer_ctl_get_num_enums(struct mixer_ctl *ctl);              const char *mixer_ctl_get_enum_string(struct mixer_ctl *ctl, unsigned int enum_id);                  /* Some sound cards update their controls due to external events,              * such as HDMI EDID byte data changing when an HDMI cable is              * connected. This API allows the count of elements to be updated.              */              void mixer_ctl_update(struct mixer_ctl *ctl);                  /* 設定和擷取可控制的資訊,方式有比例、數組、範圍、固定值 */              /* id通過mixer_get_num_ctls獲得 */              int mixer_ctl_get_percent(struct mixer_ctl *ctl, unsigned int id);              int mixer_ctl_set_percent(struct mixer_ctl *ctl, unsigned int id, int percent);                  int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id);              int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count);              int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value);              int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count);              int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string);                  int mixer_ctl_get_range_min(struct mixer_ctl *ctl);              int mixer_ctl_get_range_max(struct mixer_ctl *ctl);           

MIXER使用,有哪些屬性,幹什麼用的,看名字就已經很清楚了。

// 原始狀态              [email protected]:/data/local/tmp # tinymix -D 1                                                     Mixer name: 'mixer_name_xx'              Number of controls: 2              ctl	type	num	name                                     value              0	BOOL	1	Mic Capture Switch                       Off              1	INT	1	Mic Capture Volume                           256                  // 詳細資訊              [email protected]:/data/local/tmp # tinymix -D 1 0                                                   Mic Capture Switch: Off              [email protected]:/data/local/tmp # tinymix -D 1 1                                                   Mic Capture Volume: 256 (range 0->256)                  // 設定              [email protected]:/data/local/tmp # tinymix -D 1 0 1                                                 [email protected]:/data/local/tmp # tinymix -D 1 1 250                  // 修改後的狀态                  [email protected]:/data/local/tmp # tinymix -D 1                                                     Mixer name: 'mixer_name_xx'              Number of controls: 2              ctl	type	num	name                                     value              0	BOOL	1	Mic Capture Switch                       On              1	INT	1	Mic Capture Volume                           250           

擴充: 1. 什麼是pcm http://en.wikipedia.org/wiki/Pulse-code_modulation 2. 音頻采樣 http://baike.baidu.com/link?url=b9CLvaPgU4A0e0X3y5cLU4ADOI0eBuUeCys05m5unE7G-0eR2sQMBVhNxkCEvOf-Ot2CLAw2wWX5ZLhqi3D2t_#1