【Alsa】播放声⾳和录⾳详细流程
linux中,⽆论是oss还是alsa体系,录⾳和放⾳的数据流必须分析清楚。先分析alsa驱动层,然后关联到alsa库层和应⽤层。
⼆,链接分析:
1)链路⼀
usr/src/linux-source-3.0.0/sound/core/pcm_native.c⽂件中注册部分.mmap = snd_pcm_mmap调⽤snd_pcm_mmap_data(substream, file, area);
该⽅法中进⼀步调⽤substream->ops->mmap(substream, area);
根据./soc/pxa/pxa3xx-pcm.c⽂件中.mmap = pxa3xx_pcm_mmap,可知dma_mmap_writecombine(, ,runtime->dma_addr,);函数被调⽤。
soc/pxa/pxa3xx-pcm.c⽂件中pxa3xx_pcm_hw_params()函数会创建链表,根据
dma_buff_phys = runtime->dma_addr;
dma_desc->dsadr = dma_buff_phys;可知runtime->dma_addr为dma内存端地址,且此地址由alsa库层传递进来。⼜根据
dma_desc->dtadr = prtd->params->dev_addr和soc/pxa/pxa3xx-ac97.c⽂件中
.dev_addr = __PREG(PCDR),可知dma外设端地址为ac97控制器中fifo读写寄存器PCDR。⾄此,第⼀条链路建⽴完毕:FIFO通过DMA和内存交互。
2)链路⼆
ac97接⼝或者i2s(Inter—IC Sound)或者pcm接⼝可以将cpu和codec(wm9714/alc5620/alc5621)连接起来。
配置好格式:
pcm接⼝必须配置采样率、采样位数、通道数和传送格式;
i2s接⼝必须配置采样率、采样位数、通道数和对齐⽅式;
ac97接⼝⽐较灵活,可以认为cpu这端不⽤配置,只需要在codec端配置就⾏了。当然,电源、时钟、IO任何数字芯⽚都得配置。
最后不能混淆数据接⼝和控制接⼝的慨念,i2s和pcm只能传输⾳频数据,访问codec的寄存器必须通过i2c等控制接⼝,ac97接⼝分时传输控制和数据。
codec中的adc/dac通过ac97等接⼝同cpu的fifo交互数据。第⼆条链路建⽴完毕。
3)链路三
alsa_lib源码中pcm.c⽂件中snd_pcm_readi(,buffer,size)调⽤pcm_local.h⽂件中_snd_pcm_readi(,buffer,size);
进⼀步调⽤pcm->fast_ops->readi(pcm->fast_op_arg, buffer, size);
根据pcm_hw.h⽂件中.readi = snd_pcm_hw_readi可知,ioctl(fd, SNDRV_PCM_IOCTL_READI_FRAMES, &xferi);被调⽤。
内核中,根据/soc/pcm_native.c⽂件中.unlocked_ioctl = snd_pcm_capture_ioctl,可知snd_pcm_capture_ioctl1被调⽤,根据
SNDRV_PCM_IOCTL_READI_FRAMES参数可知snd_pcm_lib_read(substream, xferi.buf, xferi.frames);被调⽤,
最终snd_pcm_lib_read1(,,,,snd_pcm_lib_read_transfer)被调⽤。根据transfer被调⽤可知snd_pcm_lib_read_transfer被调⽤,然后调⽤copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)),可知,将dma端内存的数据拷贝到alsa_lib提供的⼀个指针所指的内存,alsa 库函数snd_pcm_readi、snd_pcm_writei实现了内存到内存的交互,或者近似地认为是内存到⾳频⽂件的交互。⾄此最后⼀条链路建⽴完毕。
三,执⾏分析:
录⾳:mic phone接到codec,经过adc变成数字信号,经过链路⼆中ac97等接⼝存储到cpu的fifo中,经过链路⼀中的dma传输存储到内存,经过链路三中alsa_lib中snd_pcm_readi接⼝传给录⾳软件,经过编码,进⽽形成⾳频⽂件。
放⾳:播放软件将⾳频⽂件解码,并通过链路三中snd_pcm_writei接⼝逐渐传递到和dma相关的内存,经过链路⼆中dma传递给cpu的fifo,再经过ac97等接⼝传递给dac,最后传给连接在codec上的speaker。
ac97四,总结:
1)ac97(声卡标准)数据传输颇复杂,分时复⽤,cpu端fifo和codec端adc/dac关系要对应好。⽐如,
cpu端的pcm left fifo占⽤slot3(CPU 中扩展插槽),那么adc(Analog to Digital 模数变换)只有配置成slot3才能把数据传递给它,如果配置成slot6,那就传给cpu的mic in fifo 了。录⾳单声道通常选择slot6,录⾳双声道通常两个adc分别选择slot3和slot4。
2)wav⾳频⽂件⼤⼩计算:要测试录⾳是否丢祯,就必然要计算⽂件⼤⼩,通常的⽅法是:根据录⾳时间,⽤公式:
录⾳时间(单位s)x采样率x(采样位数/8)x通道数。
⽐如:录⾳时间5秒,采样率8kHz,位数16位,通道数1,那么5x8000x(16/8)x1=80k,实际的wav⽂件⼤⼩稍⼤于80k就对了。还有⼀种计算⽂件⼤⼩的⽅法:通常⾳频系统要⽤dma,也会⽤到dma中断,可以在dma中断中打印计数,次数xdma中断周期字节就⾏了。
3)数据交换的⼤⼩问题:
链路⼀中DMA传输必须和FIFO的特性匹配:若FIFO位宽是16位,深度是16,并且半满时向DMA发出请求(握⼿),则链表式DMA必须配置成传输位宽16位,1次突发16字节,才能保证不丢失位数和数据个数。
链路⼆中cpu端FIFO位数要和codec端adc/dac采样位数匹配,i2s/pcm接⼝可以配置成⼀样的值,⽐如
16位,ac97接⼝复杂⼀点,cpu端不⽤配置,那么采样位数是多少呢?若cpu端fifo⼀个声道位宽16位,codec端adc/dac位宽18位,ac97通道20位,则传输到fifo端就被截取到有效的16位,整体采样位数16位,adc/dac的性能没有充分发挥⽽已。
链路三中snd_pcm_readi、snd_pcm_writei函数第三个参数表⽰读写数据的⼤⼩,单位是帧,不是字节。双声道16位格式⼀帧⼤⼩为4字节。
发布评论