ALSA框架介绍——音频通路
                                                  陈金泉 2011-3-3
内容:
1、简单介绍android音频状态与音频通路、codec。
2、通过ALSA的controls控制codec实现通路切换。
2.1、control
2.2、widget
2.3、audio_map
2.4、通路切换
2.5、alsa_amixer


1、简单介绍android音频状态与音频通路、codec、声卡注册
1.1、android音频状态与音频通路
Android的音频通路管理主要是在AudioPolicyManager中完成的,包括音量管理,音频策略(strategy)管理,输入输出设备管理。
Android音频模式状态图:
Android的音频模式状态包括:NORMAL、IN_CALL、RINGTONE。
AudioPolicyManagermPhoneState成员变量记录了当前音频模式状态,在音频通路切换时的设备选择时会使用到。
在这3种音频模式状态下一共有10种音频数据流,定义如下:
        VOICE_CALL      = 0,
        SYSTEM          = 1,
        RING            = 2,
        MUSIC            = 3,
        ALARM            = 4,
        NOTIFICATION    = 5,
        BLUETOOTH_SCO    = 6,
        ENFORCED_AUDIBLE = 7,
        DTMF            = 8,
        TTS              = 9,
 
Android把10种stream type归纳为4种路由策略getStrategy(stream_type)根据stream_type,返回对应的routing_strategy值,也就是返回stream_type对应的路由策略。
AudioPolicyManager中有两个成员变量:mAvailableOutputDevices和mAvailableInputDevices,他们记录了当前可用的输入和输出设备
getDeviceForStrategy()则结合routing_strategymPhoneState以及mAvailableOutputDevicesmAvailableInputDevices返回可用的device。
所以android设置音频通路时会先根据getStrategy(stream_type)getDeviceForStrategy()函数获取相应的输出、输入设备,然后通过setOutputDevice(mHardwareOutput, newDevice)函数调用到alsa_default.cpp中的s_route、s_open函数控制到底层。
s_open函数用会调用deviceName(handle, devices, mode),这个函数会根据Devices的值来组合成字符串。然后通过snd_pcm_open获取f中相应的pcm配置里面的controls,并对调用到codec驱动当中对codec进行配置。
例子:
Music(NORMAL) -> STRATEGY_MEDIA ->  DEVICE_OUT_WIRED_HEADSET
-> AndroidPlayback_Headset_normal
Ring(RINGTONE) ->  SONIFICATION -> DEVICE_OUT_SPEAKERDEVICE_OUT_WIRED_HEADSET
->  AndroidPlayback_Speaker_Headset_ringtone

1.2、codec
Codec中文译名是编译码器,由英文编码器(coder)和译码器(decoder)两词的词头组成的缩略语。指的是数字通信中具有编码、译码功能的器件。
Wm8900芯片内部框图:
Wm8900内部包括有:ADC、DAC、Output Mixer、Input Mixer、Input PGA、LINEOUT2、HPOUT等
音频输入口:INPUT1、INPUT2、INPUT3、I2S_DAC
音频输入口:HPOUT、LINEOUT1、LINEOUT2、I2S_ADC
Codec输入输出选择、音量控制、各个部分的power会都会通过control、widgets注册到不同的list中供ALSA和用户使用。

2、通过ALSA的controls控制codec实现通路切换。
2.1、control
Codec中会把一部分功能的设置信息,以control的形式通过snd_soc_add_controls函数添加到声卡中,我们可以通过f去控制这些control。
如:
SOC_SINGLE("Left Input PGA Switch", WM8900_REG_LINVOL, 6, 1, 1),
#define SOC_SINGLE(xname, reg, shift, max, invert) \
{    .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
    .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
    .put = snd_soc_put_volsw, \
    .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
(1)iface 字段定义了control 的类型,形式为SNDRV_CTL_ELEM_IFACE_XXX,通常是MIXER
(2)name 是名称标识字符串,control 的名称非常重要,因为control 的作用由名称来区分。name 定义的标准是“SOURCE DIRECTION FUNCTION”即“源、方向、功能”
(3)info函数可以得到对应control的详细信息。
(4)get()函数用于得到control 的目前值并返回用户空间。
(5)put()函数用于从用户空间写入值,如果值被改变,该函数返回1,否则返回0;如果发生错误,该函数返回错误码。
(6)private_value保存了改control用于设置codec的详细信息。这些也就是与codec直接相关的信息。
reg表示寄存器地址;shift表示数据偏移量;max表示数据最大值;invert表示是否倒置。
所有的control都是通过    snd_soc_add_controls(codec, wm8900_snd_controls, ARRAY_SIZE(wm8900_snd_controls))函数添加到声卡中。
headset

2.2、widget
Widget可以认为是codec内部各个部件,需要根据audio_map对应的path来调用。Widget的管理和控制都是在soc-dapm.c中去实现的。
通过snd_soc_dapm_new_controls函数添加到codec->dapm_widgets中。
SND_SOC_DAPM_PGA("LINEOUT1L PGA", WM8900_REG_POWER2, 8, 0, NULL, 0),
SND_SOC_DAPM_PGA会直接把widget对应的信息添加到codec->dapm_widgets,包括id、reg、shift、max等。id用来区分不同的widget类型,不同的id控制方式也不同。
codec通过    snd_soc_add_controlswm8900_add_widgets两个函数把来添加control、wid
gets到不同的list里。
wm8900_add_widgets中除了把wm8900_dapm_widgets结构体里面的所有widgets信息添加到list里,还通过snd_soc_dapm_add_routes函数把audio_map里的信息添加到list里。

2.3、audio_map
2.3.1、audio_map介绍
重点说下audio_map,因为audio_map是一个帮助我们切换通路的很重要的部分。我们可以根据audio_map来控制codec实现通路而不用去了解codec具体内部需要怎么配置寄存器来实现连接。这样就可以不用怎么去了解codec芯片。
audio_map的类型snd_soc_dapm_route定义为:
struct snd_soc_dapm_route {
    const char *sink;
    const char *control;
    const char *source;
};
可以理解为:目的地,控制条件,源头。
Codec的通路(连接方式)非常多,以WM8900为例举个例子。从HP_L输出(目的地sink)的信号可能来自MIC、DAC(源头source),而这个取决于codec的配置(控制条件control)。
每个通路在codec内部又被分成了好几个部分,也就是前面说到的widgets,每个widget都可以做为一个数据流的源头或者目的地。
这些widgets可能的path都在audio_map罗列出来了。
如下是WM8900一部分audio_map
/* Outputs */
{"LINEOUT1L", NULL, "LINEOUT1L PGA"},
{"LINEOUT1L PGA", NULL, "Left Output Mixer"},
{"LINEOUT1R", NULL, "LINEOUT1R PGA"},
{"LINEOUT1R PGA", NULL, "Right Output Mixer"},
{"LINEOUT2L PGA", NULL, "Left Output Mixer"},
{"LINEOUT2 LP", "Disabled", "LINEOUT2L PGA"},
{"LINEOUT2 LP", "Enabled", "Left Output Mixer"},