【Android Audio 入门 三】--- KControl 介绍

【Android Audio 入门 三】--- KControl 介绍

    • 三、KControl 介绍
      • 3.1 KControl 介绍
        • 3.1.1 snd_kcontrol_new 结构体
        • 3.1.2 kcontrol 宏定义
      • 3.2 如何调用kcontrol ( /dev/snd/controlC%u )
      • 3.3 IOCTL,snd_ctl_ioctl() 函数介绍
      • 3.4 SNDRV_CTL_IOCTL_ELEM_INFO 获得所有 kcontrl 列表
      • 3.5 hal层如何使用kcontrol
      • 3.6 tinymix 介绍


接着前面我们写的文章《【Android Audio 入门 二】— /dev/snd下的pcm节点 创建 及 open 过程代码分析》


看到一位大佬的博客,对Audio分析的非常详细,记录下网址:
ALSA https://blog.csdn.net/sepnic/article/category/777239
Android Audio https://blog.csdn.net/sepnic/article/category/778492


三、KControl 介绍

3.1 KControl 介绍

控制接口对于许多开关(switch)和调节器(slider)应用广泛,它能被用户空间存取,从而读写CODEC相关寄存器。snd kcontrol的主要用于mixer。
它用snd_kcontrol_new结构体描述。

3.1.1 snd_kcontrol_new 结构体

其结构体介绍如下:

  1. snd_ctl_elem_iface_t iface
    ifcase 字段 定义了 Control 的类型,形式为 snd_ctl_elem_iface_t ,
    对于Mixer 是 snd_ctl_elem_iface_mixer,对于不属于 mixer 的全局控制,使用 card ;
    如果关联到某设备,则是 PCM、RAWMINI、TIMER 或 SEQUENCER。在这里,我主要关注 mixer 。

  2. name
    这个字段是名称标识,这个字段非常重要,因为在conrol中主要是由名称来区分的。
    如果 kcontrol 名称相同,则使用 index 来区分。

    name 的定义标准是“ source direction function ” 即 “ 源 方向 功能”,
    source 定义了 control 的源,如“Master” ,“PCM” 等。
    Direction 则为 “Playback” , “Capture” 等,如果 direction 忽略,意味着 playback 和 capture 是双向的;
    function 则可以是 “switch” ,“volume”,和 “Route” 等。

    上层也可以根据 num id 来找到对应的control,snd_ctl_find_id() 也是优先判断 上层是否传递了num id,
    是则直接返回这个 num id 对应的control 。
    用户层设置 num id 和 control 的关联时,可以用 alsa-lib 的 snd_mixer_elem_set_enum_item 函数。
    snd_kconrol_new 结构体并没有 numid 这个成员,是因为numid 是系统自动管理的,原则是该 control 的注册顺序,
    保存到 snd_ctl_elem_value 结构体中。

  3. access
    access 字段是访问控制权限。snd_ctl_elem_acces_read 意味着是只读,这时 put() 函数不必实现。
    snd_ctl_elem_access_write 意味着是只写,这时 get() 函数不必实现。
    若 control 值频繁变化,则需定义 volatile 标志。
    当 control 处于非激活状态时,应设置 inactive 标志。

其结构体描述如下:

@ \kernel\msm-3.18\include\sound\control.h

struct snd_kcontrol_new {
	snd_ctl_elem_iface_t iface;	/* interface identifier */
	unsigned int device;		/* device/client number */
	unsigned int subdevice;		/* subdevice (substream) number */
	const unsigned char *name;	/* ASCII name of item */
	unsigned int index;		/* index of item */
	unsigned int access;		/* access rights */
	unsigned int count;		/* count of same elements */
	snd_kcontrol_info_t *info;
	snd_kcontrol_get_t *get;
	snd_kcontrol_put_t *put;
	union {
		snd_kcontrol_tlv_rw_t *c;
		const unsigned int *p;
	} tlv;
	unsigned long private_value;
};

3.1.2 kcontrol 宏定义

如下是,一个codec 中 kcontrol 的定义代码示例:

@ \kernel\msm-3.18\sound\soc\codecs\88pm860x-codec.c

static const struct snd_kcontrol_new pm860x_snd_controls[] = {
	SOC_DOUBLE_R_TLV("ADC Capture Volume", PM860X_ADC_ANA_2, PM860X_ADC_ANA_3, 6, 3, 0, adc_tlv),
	SOC_DOUBLE_TLV("AUX Capture Volume", PM860X_ADC_ANA_3, 0, 3, 7, 0, aux_tlv),
	SOC_SINGLE_TLV("MIC1 Capture Volume", PM860X_ADC_ANA_2, 0, 7, 0, mic_tlv),
	SOC_SINGLE_TLV("MIC3 Capture Volume", PM860X_ADC_ANA_2, 3, 7, 0, mic_tlv),
	SOC_DOUBLE_R_EXT_TLV("Sidetone Volume", PM860X_SIDETONE_L_GAIN, PM860X_SIDETONE_R_GAIN, 0, ARRAY_SIZE(st_table)-1,
			     0, snd_soc_get_volsw_2r_st, snd_soc_put_volsw_2r_st, st_tlv),
	SOC_SINGLE_TLV("Speaker Playback Volume", PM860X_EAR_CTRL_1, 0, 7, 0, out_tlv),
	SOC_DOUBLE_R_TLV("Line Playback Volume", PM860X_LO1_CTRL, PM860X_LO2_CTRL, 0, 7, 0, out_tlv),
	SOC_DOUBLE_R_TLV("Headset Playback Volume", PM860X_HS1_CTRL, PM860X_HS2_CTRL, 0, 7, 0, out_tlv),
	SOC_DOUBLE_R_EXT_TLV("Hifi Left Playback Volume", PM860X_HIFIL_GAIN_LEFT, PM860X_HIFIL_GAIN_RIGHT, 0, 63, 0,
			     snd_soc_get_volsw_2r_out,
			     snd_soc_put_volsw_2r_out, dpga_tlv),
	SOC_DOUBLE_R_EXT_TLV("Hifi Right Playback Volume", PM860X_HIFIR_GAIN_LEFT, PM860X_HIFIR_GAIN_RIGHT, 0, 63, 0,
			     snd_soc_get_volsw_2r_out,
			     snd_soc_put_volsw_2r_out, dpga_tlv),
	SOC_DOUBLE_R_EXT_TLV("Lofi Playback Volume", PM860X_LOFI_GAIN_LEFT, PM860X_LOFI_GAIN_RIGHT, 0, 63, 0,
			     snd_soc_get_volsw_2r_out,
			     snd_soc_put_volsw_2r_out, dpga_tlv),
	SOC_ENUM("Headset1 Operational Amplifier Current", pm860x_hs1_opamp_enum),
	SOC_ENUM("Headset2 Operational Amplifier Current", pm860x_hs2_opamp_enum),
	SOC_ENUM("Headset1 Amplifier Current", pm860x_hs1_pa_enum),
	SOC_ENUM("Headset2 Amplifier Current", pm860x_hs2_pa_enum),
	SOC_ENUM("Lineout1 Operational Amplifier Current", pm860x_lo1_opamp_enum),
	SOC_ENUM("Lineout2 Operational Amplifier Current", pm860x_lo2_opamp_enum),
	SOC_ENUM("Lineout1 Amplifier Current", pm860x_lo1_pa_enum),
	SOC_ENUM("Lineout2 Amplifier Current", pm860x_lo2_pa_enum),
	SOC_ENUM("Speaker Operational Amplifier Current", pm860x_spk_ear_opamp_enum),
	SOC_ENUM("Speaker Amplifier Current", pm860x_spk_pa_enum),
	SOC_ENUM("Earpiece Amplifier Current", pm860x_ear_pa_enum),
};

可以看出,在 alsa 代码中,定义kcontrol 是通过宏控来实现的:

  1. SOC_SINGLE_TLV
    SOC_SINGLE_TLV(“MIC1 Capture Volume”, PM860X_ADC_ANA_2, 0, 7, 0, mic_tlv),

    结合以下的宏代码原型,可以看出要定义的宏控属性如下:
    xname = “MIC1 Capture Volume”; 该kcontrol 的名字为 MIC1 Capture Volume
    reg = PM860X_ADC_ANA_2; #define PM860X_ADC_ANA_2 0xd1,要写的寄存器地址为 0xD1
    shift = 0; 寄存器偏移 0 bit
    max = 7; 最大的值是 7 ,也就是说这个寄存器可以写的值是 0 - 7
    invert = 0; 是否需要位反转
    tlv_array = mic_tlv; 写入的值的数组为 mic_tlv

    总结上面的意思就是:
    上层在调用 “MIC1 Capture Volume” 这个名字的 kcontrol 时,就会往 0xD1 这个寄存器写入 0-7 的值 ,要写的的数据在 mic_tlv 数组中。

SOC_SINGLE_TLV("MIC1 Capture Volume", PM860X_ADC_ANA_2, 0, 7, 0, mic_tlv),

@ \kernel\msm-3.18\include\sound\soc.h

#define SOC_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
	.tlv.p = (tlv_array), \
	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
	.put = snd_soc_put_volsw, \
	.private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }

#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \
	SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable)

#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert, xautodisable) \
	((unsigned long)&(struct soc_mixer_control) \
	{.reg = xreg, .rreg = xreg, .shift = shift_left, \
	.rshift = shift_right, .max = xmax, .platform_max = xmax, \
	.invert = xinvert, .autodisable = xautodisable})
  1. SOC_DOUBLE_TLV
SOC_DOUBLE_TLV("AUX Capture Volume", PM860X_ADC_ANA_3, 0, 3, 7, 0, aux_tlv),

@ \kernel\msm-3.18\include\sound\soc.h

#define SOC_DOUBLE_TLV(xname, reg, shift_left, shift_right, max, invert, tlv_array) \
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
	.tlv.p = (tlv_array), \
	.info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
	.put = snd_soc_put_volsw, \
	.private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
					  max, invert, 0) }
  1. SOC_DOUBLE_R_TLV
SOC_DOUBLE_R_TLV("ADC Capture Volume", PM860X_ADC_ANA_2, PM860X_ADC_ANA_3, 6, 3, 0, adc_tlv),

@ \kernel\msm-3.18\include\sound\soc.h

#define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
		 SNDRV_CTL_ELEM_ACCESS_READWRITE,\
	.tlv.p = (tlv_array), \
	.info = snd_soc_info_volsw, \
	.get = snd_soc_get_volsw, .put = snd_soc_put_volsw, \
	.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
					    xmax, xinvert) }
  1. SOC_DOUBLE_R_EXT_TLV
SOC_DOUBLE_R_EXT_TLV("Hifi Left Playback Volume", PM860X_HIFIL_GAIN_LEFT, PM860X_HIFIL_GAIN_RIGHT, 0, 63, 0,
			     snd_soc_get_volsw_2r_out,
			     snd_soc_put_volsw_2r_out, dpga_tlv),
			     
@ \kernel\msm-3.18\include\sound\soc.h

#define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\
	 xhandler_get, xhandler_put, tlv_array) \
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
	.access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
		 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
	.tlv.p = (tlv_array), \
	.info = snd_soc_info_volsw, \
	.get = xhandler_get, .put = xhandler_put, \
	.private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
					    xmax, xinvert) }
  1. SOC_ENUM
SOC_ENUM("Headset1 Amplifier Current", pm860x_hs1_pa_enum),

@ \kernel\msm-3.18\include\sound\soc.h

#define SOC_ENUM(xname, xenum) \
{	.iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname,\
	.info = snd_soc_info_enum_double, \
	.get = snd_soc_get_enum_double, .put = snd_soc_put_enum_double, \
	.private_value = (unsigned long)&xenum }

通过前面的宏控,可以看分别定义了 get 和 put 两个函数,后面,
分别在读 和 写 时就会调用对应的 get 和 put 函数。


3.2 如何调用kcontrol ( /dev/snd/controlC%u )

kcontrol 主要用于mixer,可以对audio 整个过程进行控制,
其实对对应的就是操作 /dev/snd/controlC%u 节点。

在 mixer_open() 函数中,主要工作如下:

  1. 根据声卡号来拼凑 kcontrol 节点的字符串名字,并打开节点
  2. 通过 ioctrl 获取 所有 支持的Kcontrol 的字符串列表
  3. 分配 Mixer 的内存
  4. 获得 Mixer 结构体中的 card_info 信息
  5. 取出kontrol的id 存入 ei 中
  6. 获取到所有的 kcontrol list

代码如下:

@ \src\external\tinyalsa\mixer.c

struct mixer *mixer_open(unsigned int card)
{
    struct snd_ctl_elem_list elist;
    struct snd_ctl_elem_info tmp;
    struct snd_ctl_elem_id *eid = NULL;
    struct mixer *mixer = NULL;
    unsigned int n, m;
    int fd;
    char fn[256];
	// 1. 根据声卡号来拼凑 kcontrol 节点的字符串名字,并打开节点
    snprintf(fn, sizeof(fn), "/dev/snd/controlC%u", card);
    fd = open(fn, O_RDWR);

	// 2. 通过 ioctrl 获取 所有 支持的Kcontrol 的数量
    memset(&elist, 0, sizeof(elist));
    if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
        goto fail;
	// 3. 分配 Mixer 的内存,用于保存 kernel kontrol 信息的结构体
    mixer = calloc(1, sizeof(*mixer));
    mixer->ctl = calloc(elist.count, sizeof(struct mixer_ctl));
    mixer->elem_info = calloc(elist.count, sizeof(struct snd_ctl_elem_info));

	// 4. 获得 Mixer 结构体中的 card_info 信息
    if (ioctl(fd, SNDRV_CTL_IOCTL_CARD_INFO, &mixer->card_info) < 0)
        goto fail;
	// 临时存储空间分配空间
    eid = calloc(elist.count, sizeof(struct snd_ctl_elem_id));


    mixer->count = elist.count;
    mixer->fd = fd;
    elist.space = mixer->count;
    elist.pids = eid;
    if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_LIST, &elist) < 0)
        goto fail;

    for (n = 0; n < mixer->count; n++) {
        struct snd_ctl_elem_info *ei = mixer->elem_info + n;
        ei->id.numid = eid[n].numid;
        // 5. 取出kontrol的id 存入 ei  中
        if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, ei) < 0)  
            goto fail;
        mixer->ctl[n].info = ei;
        mixer->ctl[n].mixer = mixer;
        if (ei->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED) {
            char **enames = calloc(ei->value.enumerated.items, sizeof(char*));
 
            mixer->ctl[n].ename = enames;
            for (m = 0; m < ei->value.enumerated.items; m++) {
                memset(&tmp, 0, sizeof(tmp));
                tmp.id.numid = ei->id.numid;
                tmp.value.enumerated.item = m;
                // 6. 获取到所有的 kcontrol list 
                if (ioctl(fd, SNDRV_CTL_IOCTL_ELEM_INFO, &tmp) < 0)
                    goto fail;
                enames[m] = strdup(tmp.value.enumerated.name);
                if (!enames[m])
                    goto fail;
            }
        }
    }
    free(eid);
    return mixer;

3.3 IOCTL,snd_ctl_ioctl() 函数介绍

在 ioctl 中通过 解析上层传递下来的cmd,执行相应的函数,做相应的操作。

@ \src\kernel\msm-3.18\sound\core\control.c
static long snd_ctl_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	ctl = file->private_data;
	card = ctl->card;
	switch (cmd) {
	case SNDRV_CTL_IOCTL_PVERSION:
		return put_user(SNDRV_CTL_VERSION, ip) ? -EFAULT : 0;
	case SNDRV_CTL_IOCTL_CARD_INFO:
		return snd_ctl_card_info(card, ctl, cmd, argp);
	case SNDRV_CTL_IOCTL_ELEM_LIST:
		return snd_ctl_elem_list(card, argp);
	case SNDRV_CTL_IOCTL_ELEM_INFO:
		return snd_ctl_elem_info_user(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_READ:
		return snd_ctl_elem_read_user(card, argp);
	case SNDRV_CTL_IOCTL_ELEM_WRITE:
		return snd_ctl_elem_write_user(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_LOCK:
		return snd_ctl_elem_lock(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_UNLOCK:
		return snd_ctl_elem_unlock(ctl, argp);
	case SNDRV_CTL_IOCTL_ELEM_ADD:
		return snd_ctl_elem_add_user(ctl, argp, 0);
	case SNDRV_CTL_IOCTL_ELEM_REPLACE:
		return snd_ctl_elem_add_user(ctl, argp, 1);
	case SNDRV_CTL_IOCTL_ELEM_REMOVE:
		return snd_ctl_elem_remove(ctl, argp);
	case SNDRV_CTL_IOCTL_SUBSCRIBE_EVENTS:
		return snd_ctl_subscribe_events(ctl, ip);
	case SNDRV_CTL_IOCTL_TLV_READ:
		return snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_READ);
	case SNDRV_CTL_IOCTL_TLV_WRITE:
		return snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_WRITE);
	case SNDRV_CTL_IOCTL_TLV_COMMAND:
		return snd_ctl_tlv_ioctl(ctl, argp, SNDRV_CTL_TLV_OP_CMD);
	case SNDRV_CTL_IOCTL_POWER:
		return -ENOPROTOOPT;
	case SNDRV_CTL_IOCTL_POWER_STATE:
#ifdef CONFIG_PM
		return put_user(card->power_state, ip) ? -EFAULT : 0;
#else
		return put_user(SNDRV_CTL_POWER_D0, ip) ? -EFAULT : 0;
#endif
	}
	// 如果不是前的面的这些命令,则遍历
	list_for_each_entry(p, &snd_control_ioctls, list) {
		err = p->fioctl(card, ctl, cmd, arg);
	}
	dev_dbg(card->dev, "unknown ioctl = 0x%x\n", cmd);
	return -ENOTTY;
}


3.4 SNDRV_CTL_IOCTL_ELEM_INFO 获得所有 kcontrl 列表

@ \src\kernel\msm-3.18\sound\core\control.c

static int snd_ctl_elem_info_user(struct snd_ctl_file *ctl, struct snd_ctl_elem_info __user *_info)
{
	struct snd_ctl_elem_info info; // 定义局部变量用于保存 kcontrol list

	copy_from_user(&info, _info, sizeof(info);
		
	result = snd_power_wait(ctl->card, SNDRV_CTL_POWER_D0);
	result = snd_ctl_elem_info(ctl, &info);
	
	copy_to_user(_info, &info, sizeof(info);
	
	return result;
}

接下来,通过 snd_ctl_elem_info() 函数来获取 info

static int snd_ctl_elem_info(struct snd_ctl_file *ctl, struct snd_ctl_elem_info *info)
{
	struct snd_card *card = ctl->card;
	struct snd_kcontrol *kctl;
	struct snd_kcontrol_volatile *vd;

	kctl = snd_ctl_find_id(card, &info->id);

	result = kctl->info(kctl, info);  // 
	
	return result;
}

调用kcontrl 的info 函数 来获取 所有kcontrol
static int snd_ctl_elem_add(struct snd_ctl_file *file,
			    struct snd_ctl_elem_info *info, int replace)
{
	if (info->type == SNDRV_CTL_ELEM_TYPE_ENUMERATED)
		kctl.info = snd_ctl_elem_user_enum_info;
	else
		kctl.info = snd_ctl_elem_user_info;
		
	kctl.get = snd_ctl_elem_user_get;
	kctl.put = snd_ctl_elem_user_put;
	kctl.tlv.c = snd_ctl_elem_user_tlv;
	_kctl = snd_ctl_new(&kctl, access);
}


static int snd_ctl_elem_user_enum_info(struct snd_kcontrol *kcontrol,
				       struct snd_ctl_elem_info *uinfo)
{
	struct user_element *ue = kcontrol->private_data;
	const char *names;
	unsigned int item;

	item = uinfo->value.enumerated.item;

	*uinfo = ue->info;

	item = min(item, uinfo->value.enumerated.items - 1);
	uinfo->value.enumerated.item = item;

	names = ue->priv_data;
	for (; item > 0; --item)
		names += strlen(names) + 1;
	strcpy(uinfo->value.enumerated.name, names);

	return 0;
}

kcontrol 定义的代码在: @\kernel\msm-3.18\sound\soc\msm\qdsp6v2\msm-pcm-routing-v2.c


3.5 hal层如何使用kcontrol

如下,
通过 mixer_get_ctl_by_name 就能通过 字符串获得对应的ctrl。
通过 mixer_ctl_set_value(ctl, 0, false); 设置对应值

@ \src\hardware\qcom\audio\hal\audio_extn\usb.c

static const char * const usb_sidetone_enable_str[] = {
    "Sidetone Playback Switch",
    "Mic Playback Switch",
};
static const char * const usb_sidetone_volume_str[] = {
    "Sidetone Playback Volume",
    "Mic Playback Volume",
};

static void usb_get_sidetone_mixer(struct usb_card_config *usb_card_info)
 {
    struct mixer_ctl *ctl;
    unsigned int index;

    usb_card_info->usb_snd_mixer = mixer_open(usb_card_info->usb_card);
    for (index = 0; index < sizeof(usb_sidetone_enable_str)/sizeof(usb_sidetone_enable_str[0]); index++) {
        ctl = mixer_get_ctl_by_name(usb_card_info->usb_snd_mixer,usb_sidetone_enable_str[index]);
        if (ctl) {
            usb_card_info->usb_sidetone_index[USB_SIDETONE_ENABLE_INDEX] = index;
            /* Disable device sidetone by default */
            mixer_ctl_set_value(ctl, 0, false);
            break;
        }
    }
    for (index = 0;
         index < sizeof(usb_sidetone_volume_str)/sizeof(usb_sidetone_volume_str[0]);
         index++) {
        ctl = mixer_get_ctl_by_name(usb_card_info->usb_snd_mixer,usb_sidetone_volume_str[index]);
        if (ctl) {
            usb_card_info->usb_sidetone_index[USB_SIDETONE_VOLUME_INDEX] = index;
            usb_card_info->usb_sidetone_vol_min = mixer_ctl_get_range_min(ctl);
            usb_card_info->usb_sidetone_vol_max = mixer_ctl_get_range_max(ctl);
            break;
        }
    }
    return;
}

相关的函数如下

@\src\external\tinyalsa\include\tinyalsa\asoundlib.h

int mixer_ctl_get_value(struct mixer_ctl *ctl, unsigned int id);
int mixer_ctl_is_access_tlv_rw(struct mixer_ctl *ctl);
int mixer_ctl_get_array(struct mixer_ctl *ctl, void *array, size_t count);
int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value);
int mixer_ctl_set_array(struct mixer_ctl *ctl, const void *array, size_t count);
int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string);

其原型如下:
int mixer_ctl_set_enum_by_string(struct mixer_ctl *ctl, const char *string)
{
    num_enums = ctl->info->value.enumerated.items;
    for (i = 0; i < num_enums; i++) {
        if (!strcmp(string, ctl->ename[i])) {
            memset(&ev, 0, sizeof(ev));
            ev.value.enumerated.item[0] = i;
            ev.id.numid = ctl->info->id.numid;
            ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
            return 0;
        }
}

@ \src\external\tinyalsa\mixer.c
int mixer_ctl_set_value(struct mixer_ctl *ctl, unsigned int id, int value)
{
    memset(&ev, 0, sizeof(ev));
    ev.id.numid = ctl->info->id.numid;
    ret = ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_READ, &ev);

    switch (ctl->info->type) {
    case SNDRV_CTL_ELEM_TYPE_BOOLEAN:
        ev.value.integer.value[id] = !!value;
        break;

    case SNDRV_CTL_ELEM_TYPE_INTEGER:
        ev.value.integer.value[id] = value;
        break;

    case SNDRV_CTL_ELEM_TYPE_ENUMERATED:
        ev.value.enumerated.item[id] = value;
        break;

    case SNDRV_CTL_ELEM_TYPE_BYTES:
        ev.value.bytes.data[id] = value;
        break;

    default:
        return -EINVAL;
    }
    return ioctl(ctl->mixer->fd, SNDRV_CTL_IOCTL_ELEM_WRITE, &ev);
}

可以发现最终是通过 ioctl 来 SNDRV_CTL_IOCTL_ELEM_WRITE 来设置值的。

@ \src\kernel\msm-3.18\sound\core\control.c
	case SNDRV_CTL_IOCTL_ELEM_WRITE:
		return snd_ctl_elem_write_user(ctl, argp);

static int snd_ctl_elem_write_user(struct snd_ctl_file *file, struct snd_ctl_elem_value __user *_control)
{
	struct snd_ctl_elem_value *control;
	struct snd_card *card;
	
	control = memdup_user(_control, sizeof(*control));
	card = file->card;

	result = snd_ctl_elem_write(card, file, control);

	if (copy_to_user(_control, control, sizeof(*control)))
		result = -EFAULT;
		
	kfree(control);
	return result;
}
最终通过 put 函数写入值。
static int snd_ctl_elem_write(struct snd_card *card, struct snd_ctl_file *file,struct snd_ctl_elem_value *control)
{
	kctl = snd_ctl_find_id(card, &control->id);
	result = kctl->put(kctl, control);
	return result;
}

最终通过 put 函数写入值 ,或者 get 函数来得到值。

put 和 get 定义的地方在:

@ \src\kernel\msm-3.18\sound\soc\msm\qdsp6v2\msm-dai-q6-v2.c

static const struct snd_kcontrol_new tdm_config_controls_data_format[] = {
	SOC_ENUM_EXT("PRI_TDM_RX_0 Data Format", tdm_config_enum[0], 
					msm_dai_q6_tdm_data_format_get, msm_dai_q6_tdm_data_format_put),
	SOC_ENUM_EXT("PRI_TDM_RX_1 Data Format", tdm_config_enum[0],
					msm_dai_q6_tdm_data_format_get, msm_dai_q6_tdm_data_format_put),

又比如:
@ \src\kernel\msm-3.18\sound\soc\intel\mfld_machine.c

static const struct snd_kcontrol_new mfld_snd_controls[] = {
	SOC_ENUM_EXT("Playback Switch", headset_enum, headset_get_switch, headset_set_switch),
	SOC_ENUM_EXT("Lineout Mux", lo_enum, lo_get_switch, lo_set_switch),
};



3.6 tinymix 介绍

tinymix Android 自带的audio 调试工具,代码位于 \external\tinyalsa\ 目录。

如下图:
【Android Audio 入门 三】--- KControl 介绍_第1张图片

包括: tinymix、tinypcminfo、tinyplay、tinycap 等工具

1. tinymix 
	(1)adb shell tinymix  					:列出所有 kcontrol 的状态列出来
	(2)adb shell tinymix  ctrl_index  value  	:配置某个 kcontrol 的值  
	(3)adb shell tinymix  ctrl_index			:获得某个 kcontrol 的值

2. tinypcminfo
	tinypcminfo -D card -d device 		: 获得 声卡card 下第n 个deivce 相关的配置
	比如:   tinypcminfo -D 0 -d 0

3. tinyplay	 直接播放某个文件
	tinyplay  file.wav  -D  card  -d device  -p period_size  -n periods
	注意,使用它播放前,要用 tinymix 把所有的 kcontrol 先配置好才行。
	
	比如:  tinyplay  file.wav  -D 0  -d 0  -p 2048  -n 2

4. tinycap  录音
	tinycap file.wav  -D card  -d device  -c channels  -r rate  -b bits  -p periods_size  -n n_periods

【Android Audio 入门 三】--- KControl 介绍_第2张图片
【Android Audio 入门 三】--- KControl 介绍_第3张图片

你可能感兴趣的:(05--Android,Audio)