alsa lib中ttable相关学习

alsa lib中ttable相关学习

最近做项目遇到一个问题,单声道设备偶尔会不出声音。
测量波形发现,正常情况下单声道数据在左声道,出问题的时候,数据在右声道。
后端设备只处理了左声道数据,未处理右声道,因此导致不出声音。

这个单声道设备用的是imx6中的esai接口。
将esai接口相关寄存器dump出来进行确认,完全没问题。
推断有两种可能,一种是imx6本身的bug,程序将数据写在了左声道,而imx6把数据放在右声道发送了;
另一种是软件bug,即软件将数据写在了右声道。

第一种可能只能找硬件厂商进行确认。
第二种可能是软件问题,我们就有办法调查了。
esai接口有多个输出端,单只有一个时钟,这就需要组装成一套完整的数据,即各输出端口的左右声道都有数据,然后把数据进行传输。
怎么组装呢?可以用alsa lib的配置文件,这就涉及到了ttable。

下面详细看看alsa lib配置文件是怎么使用esai端口的。
首先用到了bindings,假设使用了三个传输端口:tx0, tx1, tx3,一个可能的bindings配置如下:
    bindings {
        0 0
        1 1
        2 2
        3 3
        4 4
        5 5
    }
其中前一列为alsa中使用的channels,从代码中看,其被称为slave channels。
后面一列是esai端口使用的channels,使用了三个输出端口,每个端口都要左右声道,所以共有6个channels.
这6个channels与输出端口是怎样对应的呢?
没有找到理论说明,根据实际情况,发现其中0、1、2为左声道,3、4、5为右声道。
0、3对应tx0,1、4对应tx1,2、5对应tx3.
也就是说总的channel数是使用输出端口的两倍。
前半部分是左声道,后半部分是右声道。
声道与端口是按顺序依次匹配。
前面的bindings配置只是简单做了个映射。
使用slave channels时,仍然要注意0、3对应一个端口,1、4对应一个端口,2、5对应一个端口。
也可以将配置修改为如下:
    bindings {
        0 0
        1 3
        2 1
        3 4
        4 2
        5 5
    }
这样在使用slave channels时,0、1对应一个端口,2、3对应一个端口,4、5对应一个端口。

知道了bindings是怎么回事,下面来看ttable。
bindings一般用在dmix plugin中,如:
pcm.playback {
    type dmix
...
    slave {            
        pcm "hw:1,2"
...
    }
    bindings {
        0 0
        1 1
        2 2
        3 3
        4 4
        5 5
    }
}
在playback上使用ttable再包一层plug,即可实现声道的拆分。如:
pcm.stereo {
    type plug
    slave {
        pcm "dshare_playback"
        channels 6
    }
    ttable.0.1 16
    ttable.1.4 16
}
上述配置的意思是,将stereo device的0、1声道分布映射到slave channels中的1、4声道。
从前文可知,1、4声道对应是的是tx1,也就是说stereo device的数据最终传输到了tx1上。
在实际应用中,有时只需要一个声道,也就是单声道设备。如:
pcm.mono {
    type plug
    slave {
        pcm "dshare_playback"
        channels 6
    }
    ttable.0.2 16
}
将mono的0channel映射到slave channels中的2声道上,即tx2的左声道。

配置基本上知道了,ttable配置如何被代码使用的呢?
调用关系:
snd_pcm_plug_insert_plugins -> snd_pcm_plug_change_channels -> snd_pcm_route_open -> route_load_ttable
其中snd_pcm_plug_change_channels中会alloc一个ttable,并根据plug->ttable或者配置对ttable进行赋值。
route_load_ttable中会遍历所有在使用的slave channels,若有client channle与其对应,将其数据操作函数指针赋值为:snd_pcm_route_convert1_many,
否则赋值为snd_pcm_route_convert1_zero。
写数据的函数调用关系:
snd_pcm_route_write_areas -> snd_pcm_route_convert
snd_pcm_route_convert中会遍历所有的slave channels,若是未使用的channel,直接调用snd_pcm_route_convert1_zero;否则调用dest channels对应的数据处理函数。
也就是route_load_ttable中赋值的函数指针:snd_pcm_route_convert1_many或者snd_pcm_route_convert1_zero。
结合前面的分析可知,遍历所有的slave channels,如果有client channel与该slave channel对应,则调用函数snd_pcm_route_convert1_many对数据处理。
否则调用snd_pcm_route_convert1_zero对数据进行处理。
snd_pcm_route_convert1_zero中直接调用了snd_pcm_area_silence,也就是说填写了silence数据。

你可能感兴趣的:(alsa lib中ttable相关学习)