“Of course you can just use it as a tool, but some people are interestedin how it works, and want to change it, and it’s not doesn’t have to bejust a tool.” – Dr. Seymour Papert
TinyAlsa是 Android 默認的 alsalib, 封裝了內(nèi)核 ALSA 的接口,用于簡化用戶空間的 ALSA 編程。
TinyAlsa的設計目標是:
可以看出,TinyAlsa是個很輕量級的庫,很容易讓我們一窺究竟。
在include/asoundlib.h
頭文件里聲明了TinyAlsa的所有接口。
/* Open and close a stream */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 API*/int pcm_write(struct pcm *pcm, const void *data, unsigned int count);int pcm_read(struct pcm *pcm, void *data, unsigned int count);int pcm_mmap_write(struct pcm *pcm, const void *data, unsigned int count);int pcm_set_avail_min(struct pcm *pcm, int avail_min);/* MIXER API */mixer_ctl_get_XX(struct mixer_ctl *ctl);mixer_ctl_set_XX(struct mixer_ctl *ctl);
頭文件沒有暴露struct pcm
的內(nèi)部成員,這意味著調(diào)用者只能使用它的API對音頻設備訪問。在播放操作時,pcm_mmap_write()
比pcm_write()
減少了內(nèi)存拷貝的開銷,但由于它需要增加系統(tǒng)調(diào)用來同步數(shù)據(jù)指針,在緩沖區(qū)小的情況下,效率不一定總是更高,但它提供了更好的靈活性。
合理的pcm_config
可以做到更好的低時延和功耗,移動設備的開發(fā)優(yōu)為敏感。
struct pcm_config { unsigned int channels; unsigned int rate; unsigned int period_size; unsigned int period_count; enum pcm_format format; unsigned int start_threshold; unsigned int stop_threshold; unsigned int silence_threshold; int avail_min;};
解釋一下結(jié)構(gòu)中的各個參數(shù),每個參數(shù)的單位都是frame(1幀 = 通道*采樣位深
):
period_size
. 每次傳輸?shù)臄?shù)據(jù)長度。值越小,時延越小,cpu占用就越高。period_count
. 緩之沖區(qū)period的個數(shù)。緩沖區(qū)越大,發(fā)生XRUN的機會就越少。format
. 定義數(shù)據(jù)格式,如采樣位深,大小端。start_threshold
. 緩沖區(qū)的數(shù)據(jù)超過該值時,硬件開始啟動數(shù)據(jù)傳輸。如果太大,從開始播放到聲音出來時延太長,甚至可導致太短促的聲音根本播不出來;如果太小,又可能容易導致XRUN.stop_threshold
. 緩沖區(qū)空閑區(qū)大于該值時,硬件停止傳輸。默認情況下,這個數(shù)為整個緩沖區(qū)的大小,即整個緩沖區(qū)空了,就停止傳輸。但偶爾的原因?qū)е戮彌_區(qū)空,如CPU忙,增大該值,繼續(xù)播放緩沖區(qū)的歷史數(shù)據(jù),而不關閉再啟動硬件傳輸(一般此時有明顯的聲音卡頓),可以達到更好的體驗。silence_threshold
. 這個值本來是配合stop_threshold
使用,往緩沖區(qū)填充靜音數(shù)據(jù),這樣就不會重播歷史數(shù)據(jù)了。但如果沒有設定silence_size
,這個值會生效嗎?求解??avail_min
. 緩沖區(qū)空閑區(qū)大于該值時,pcm_mmap_write()
才往緩沖寫數(shù)據(jù)。這個值越大,往緩沖區(qū)寫入數(shù)據(jù)的次數(shù)就越少,面臨XRUN的機會就越大。Androidsamsung tuna 設備在screen_off時增大該值以減小功耗,在screen_on時減小該值以減小XRUN的機會。在不同的場景下,合理的參數(shù)就是在性能、時延、功耗等之間達到較好的平衡。
有朋友問為什么在pcm_write()/pcm_mmap_write()
,而不在pcm_open()
調(diào)用pcm_start()
? 這是因為音頻流與其它的數(shù)據(jù)不同,實時性要求很高。作為TinyAlsa的實現(xiàn)者,不能假定在調(diào)用者open
之后及時的write
數(shù)據(jù),所以只能在有數(shù)據(jù)寫入的時候start
設備了。
Mixer的實現(xiàn)很明了,通過ioctl()
調(diào)用訪問kcontrols
.
TinyAlsa說復雜的操作應該由上層來處理,可能指的是重采樣、插件功能等。但由于 不能直接訪問聲卡設備,以至于要添加一些功能,如非阻塞打開聲卡等,只能修改它的 源代碼才能實現(xiàn)。導出pcm的文件描述符就有那么難么
聯(lián)系客服