音頻系統(tǒng),Alsa 里面的buff 是怎么計(jì)算的?

Linux ALSA 圖解
我在MTK平臺(tái)下調(diào)試音頻ALSA
我們知道聲音是模擬信號(hào),模擬信號(hào)轉(zhuǎn)成數(shù)字信號(hào)就一定有大小,既然有大小,那我們就需要開(kāi)辟內(nèi)存來(lái)保存這些數(shù)據(jù)。
---- 我們知道,視頻流的一幀就是一張圖像 ------ 但是音頻不一樣,音頻的一幀不能表示是一句話(huà),因?yàn)槟悴恢酪粋€(gè)人說(shuō)話(huà)的時(shí)間長(zhǎng)度。
java讀取音頻代碼
int?bufferLen?=?mSampleRateInHz?*?channels?*?2?/?10;?//?100ms?data
????????????????//int?bufferLen?=?2048;
????????????????if?((channels?==?1)?&&?(mSampleRateInHz?==?32000)?&&?(mAudioSource?==?6)?&&?(mMaxChannels?>?0))
????????????????{?//?get?raw?data
????????????????????bufferLen?=?16000?*?mMaxChannels?*?2?/?10;?//?100ms?data
????????????????}
????????????????byte[]?buffer?=?new?byte[bufferLen];
????????????????mAudioRecorder?=?new?AudioRecord(mAudioSource,?mSampleRateInHz,?mChannelConfig,
????????????????????????????AudioFormat.ENCODING_PCM_16BIT,?bufferLen?*?10);?//1?sec?buffer
??????????????????while(true)
??????????????????{
???????????????????size?=?mAudioRecorder.read(buffer,?0,?bufferLen);?//100ms
??????????????????}
tinycap 里面的 pcm_read 代碼
int?pcm_read(struct?pcm?*pcm,?void?*data,?unsigned?int?count)
{
????struct?snd_xferi?x;
????if?(!(pcm->flags?&?PCM_IN))
????????return?-EINVAL;
????x.buf?=?data;
????x.frames?=?count?/?(pcm->config.channels?*
????????????????????????pcm_format_to_bits(pcm->config.format)?/?8);
????for?(;;)?{
????????if?(!pcm->running)?{
????????????if?(pcm_start(pcm)?0)?{
????????????????fprintf(stderr,?"start?error");
????????????????return?-errno;
????????????}
????????}
????????if?(ioctl(pcm->fd,?SNDRV_PCM_IOCTL_READI_FRAMES,?&x))?{
????????????pcm->prepared?=?0;
????????????pcm->running?=?0;
????????????if?(errno?==?EPIPE)?{
????????????????????/*?we?failed?to?make?our?window?--?try?to?restart?*/
????????????????pcm->underruns++;
????????????????continue;
????????????}
????????????return?oops(pcm,?errno,?"cannot?read?stream?data");
????????}
????????return?0;
????}
}
提煉下重點(diǎn)
我們?cè)诶锩婵吹囊粋€(gè)英文單詞 frames ,frames 相當(dāng)一幀的數(shù)據(jù)。
----但是這個(gè)一幀跟視頻流里面的一張圖像又不是一個(gè)概念,這里的一幀相當(dāng)于聲音里面的最小計(jì)量單位。

解釋一下上面的圖片
假設(shè)我們使用的是一個(gè)立體聲 16位 16k的音頻流,不管是錄音還是播放都一樣,那么
立體聲 = 2通道 1次ADC轉(zhuǎn)換樣本數(shù)據(jù)是 16bits = 2bytes 1個(gè)幀 代表 所有通道的ADC轉(zhuǎn)換數(shù)據(jù)。那么我們現(xiàn)在是雙通道,所以 1幀 = (通道數(shù)) * (樣本大小bytes) = 2 * 2 = 4bytes 為了能支持2 * 16k的采樣率,系統(tǒng)必須支持如下的速度 bsp_rate = (通道數(shù)) * (1個(gè)樣本長(zhǎng)度) * (采樣率) = 1幀 * 采樣率 = 2 * 2 *16k = 64000bytes/sec(秒)
假設(shè)現(xiàn)在 alsa每秒中斷DMA一次。那么我們每秒都需要64000bytes數(shù)據(jù)準(zhǔn)備好,才能滿(mǎn)足一個(gè) 雙通道 16 位 16k的音頻流。
如果半秒中斷一次,那么每次中斷就是 64000bytes/ 2 = 32000bytes 如果我們100ms 產(chǎn)生一次中斷,那么每次中斷就是 64000bytes / 10 = 6400bytes
我們可以通過(guò)設(shè)置period size 來(lái)控制pcm中斷的產(chǎn)生。
反推一下
---- 如果我們?cè)O(shè)置一個(gè)16位雙通道16k的音頻流, 并且每次都有1600幀數(shù)據(jù)
---- 4 byte * 1600frams = 6400字節(jié)
---- 一次中斷會(huì)需要6400字節(jié)的數(shù)據(jù)
----那么他就是100ms中斷一次「看上面的推斷」。
alsa會(huì)自己適應(yīng)實(shí)際的buffer_size 和period_size,根據(jù)請(qǐng)求的通道數(shù),和他們其他的一些屬性。
把音頻格式轉(zhuǎn)換成bits的代碼
unsigned?int?pcm_format_to_bits(enum?pcm_format?format)
{
????switch?(format)?{
????case?PCM_FORMAT_S32_LE:
????case?PCM_FORMAT_S24_LE:
????????return?32;
????case?PCM_FORMAT_S24_3LE:
????????return?24;
????default:
????case?PCM_FORMAT_S16_LE:
????????return?16;
????};
}
我們會(huì)使用這個(gè)函數(shù)拿到對(duì)應(yīng)格式的音頻bit「正常是16bit 和 32bit」,但是,我們讀寫(xiě)數(shù)據(jù)是字節(jié)對(duì)齊bytes對(duì)齊的,這也是我們看到很多地方有除以8這個(gè)操作的原因。
?x.buf?=?data;
?x.frames?=?count?/?(pcm->config.channels?*
????????????????????????pcm_format_to_bits(pcm->config.format)?/?8);
測(cè)試的小程序
---- 用來(lái)錄音的測(cè)試程序
#include?
#define?UNUSED(x)?(void)(x)
int?main(int?argc,?char?**argv)?{
????UNUSED(argc);
????UNUSED(argv);
????char?*cmd?=?"tinycap?/sdcard/1.pcm?-D?0?-d?3?-r?16000?-c?2?-b?16";
????char?buf[256];
????FILE?*fp?=?popen(cmd,?"r");
????for?(int?i=0;?i<16;?i++)?{
????????int?result?=?fread(buf,?1,?sizeof(buf),?fp);
????????printf("read?%d?bytes\n",?result);
????}
????pclose(fp);
????return?0;
}
這個(gè)程序會(huì)打開(kāi)聲卡 0 第 3 個(gè)pcm通路去錄音。
我們這里沒(méi)有設(shè)置「-p」這個(gè)屬性,加上這個(gè)屬性之后,就會(huì)知道在1秒內(nèi)的中斷次數(shù),從而知道1秒內(nèi)的音頻大小了。當(dāng)然了,我們不設(shè)置,也會(huì)有一個(gè)默認(rèn)值的。

