Audio:Android-TinyAlsa架构 Mixer API

本文深入探讨了Android音频框架中TinyAlsa库的mixer配置,重点分析了tinymix工具的源码,包括tinymix_open、tinymix_list_controls、tinymix_detail_control和tinymix_set_value等关键方法。tinymix用于查看和设置音频通道参数,tinymix_list_controls列出所有混音器控制,tinymix_detail_control展示控制详情,tinymix_set_value则用于修改控制值。这些接口在Android音频设备初始化和配置中起到关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

        Android中对音频多声道数据的配置binary tinymix是tinyplay执行前必要的步骤,对tinymix的源码进行check在整合tinyalsa相关通道配置以及流程上是非常有必要的。tinymix梳理之后还是比较简单的,主要有mixer_open, mixer_close, tinymix_list_controls, tinymix_detail_control, tinymix_set_value这个几个方法。

1.tinymix.c

file path: external/tinyalsa/tinymix.c

int main(int argc, char **argv)
{
    struct mixer *mixer;
    int card = 0;
    int ret = 0;

    while (1) {
        int option_index = 0;
        int option_char = 0;

        option_char = getopt_long(argc, argv, tinymix_short_options,
                                  tinymix_long_options, &option_index);
        if (option_char == -1)
            break;

        switch (option_char) {
        case 'D':
            ...
    }


    //mixer_open打开对应card的mixer配置,默认为0
    mixer = mixer_open(card);
    if (!mixer) {
        fprintf(stderr, "Failed to open mixer\n");
        return ENODEV;
    }

    if (argc == optind) {
        printf("Mixer name: '%s'\n", mixer_get_name(mixer));
        //查看所有的mixer配置参数
        tinymix_list_controls(mixer);
    } else if (argc == optind + 1) {
        //查看某一项mixer配置参数
        ret = tinymix_detail_control(mixer, argv[optind], !g_value_only, !g_value_only);
    } else if (argc >= optind + 2) {
        //通过mixer设置通道参数
        ret = tinymix_set_value(mixer, argv[optind], &argv[optind + 1], argc - optind - 1);
    }

    //最后通过mixer_close关闭mixer配置
    mixer_close(mixer);

    return ret;
}

2.tinytest.c

        参考tinymix.c写入相关的音频配置参数S_NORMAL_AP01_C_CODEC SWITCH 和 S_VOICE_C_CODEC SWITCH都写为1,on的状态。

int main(int argc, char **argv)
{
    struct mixer *mixer;
    int card = 0;
    int i;
    char *control1 = "S_NORMAL_AP01_C_CODEC SWITCH";
    char *control2 = "S_VOICE_C_CODEC SWITCH";
    char *values = "1";

    if ((argc > 2) && (strcmp(argv[1], "-D") == 0)) {
        argv++;
        if (argv[1]) {
            card = atoi(argv[1]);
            argv++;
            argc -= 2;
        } else {
            argc -= 1;
        }
    }

    mixer = mixer_open(card);
    if (!mixer) {
        DEBUG_Log_Err("Failed to open mixer\n");
        return EXIT_FAILURE;
    }

    DEBUG_Log_Info("shengjie-setmixer");
    tinymix_set_value(mixer, control1, &values, 1);
    tinymix_set_value(mixer, control2, &values, 1);

    mixer_close(mixer);

    return 0;
}

3.tinymix-tinymix_list_controls

        这边可以显示出所有的mixer配置的音频参数通道以及通过tinymix_detail_control查看通道现在的值,具体使用方法如下图所示。

static void tinymix_list_controls(struct mixer *mixer)
{
    struct mixer_ctl *ctl;
    const char *name, *type;
    unsigned int num_ctls, num_values;
    unsigned int i;

    num_ctls = mixer_get_num_ctls(mixer);

    printf("Number of controls: %u\n", num_ctls);

    if (g_tabs_only)
        printf("ctl\ttype\tnum\tname\tvalue");
    else
        printf("ctl\ttype\tnum\t%-40s value\n", "name");
    if (g_all_values)
        printf("\trange/values\n");
    else
        printf("\n");
    for (i = 0; i < num_ctls; i++) {
        ctl = mixer_get_ctl(mixer, i);

        name = mixer_ctl_get_name(ctl);
        type = mixer_ctl_get_type_string(ctl);
        num_values = mixer_ctl_get_num_values(ctl);
        if (g_tabs_only)
            printf("%d\t%s\t%d\t%s\t", i, type, num_values, name);
        else
            printf("%d\t%s\t%d\t%-40s ", i, type, num_values, name);
        tinymix_detail_control(mixer, name, 0, g_all_values);
    }
}

4.tinymix-tinymix_detail_control

        tinymix_detail_control接口目的就是查看具体音频通道的配置信息,具体使用方法如下图所示。

static int tinymix_detail_control(struct mixer *mixer, const char *control,
                                  int prefix, int print_all)
{
    struct mixer_ctl *ctl;
    enum mixer_ctl_type type;
    unsigned int num_values;
    unsigned int i;
    int min, max;
    int ret;
    char *buf = NULL;
    size_t len;
    unsigned int tlv_header_size = 0;
    const char *space = g_tabs_only ? "\t" : " ";

    if (isdigit(control[0]))
        ctl = mixer_get_ctl(mixer, atoi(control));
    else
        ctl = mixer_get_ctl_by_name(mixer, control);

    if (!ctl) {
        fprintf(stderr, "Invalid mixer control: %s\n", control);
        return ENOENT;
    }

    type = mixer_ctl_get_type(ctl);
    num_values = mixer_ctl_get_num_values(ctl);

    if (type == MIXER_CTL_TYPE_BYTE) {
        if (mixer_ctl_is_access_tlv_rw(ctl)) {
            tlv_header_size = TLV_HEADER_SIZE;
        }
        buf = calloc(1, num_values + tlv_header_size);
        if (buf == NULL) {
            fprintf(stderr, "Failed to alloc mem for bytes %d\n", num_values);
            return ENOENT;
        }

        len = num_values;
        ret = mixer_ctl_get_array(ctl, buf, len + tlv_header_size);
        if (ret < 0) {
            fprintf(stderr, "Failed to mixer_ctl_get_array\n");
            free(buf);
            return ENOENT;
        }
    }


    printf("shengjie-%s, num:%d", mixer_ctl_get_name(ctl), num_values);
    if (prefix)
        printf("%s:%s", mixer_ctl_get_name(ctl), space);

    for (i = 0; i < num_values; i++) {
        switch (type)
        {
        case MIXER_CTL_TYPE_INT:
            printf("%d", mixer_ctl_get_value(ctl, i));
            break;
        case MIXER_CTL_TYPE_BOOL:
            printf("%s", mixer_ctl_get_value(ctl, i) ? "On" : "Off");
            break;
        case MIXER_CTL_TYPE_ENUM:
            tinymix_print_enum(ctl, space, print_all);
            break;
        case MIXER_CTL_TYPE_BYTE:
            /* skip printing TLV header if exists */
            printf(" %02x", buf[i + tlv_header_size]);
            break;
        default:
            printf("unknown");
            break;
        }

        if (i < num_values - 1)
            printf("%s", space);
    }

    if (print_all) {
        if (type == MIXER_CTL_TYPE_INT) {
            min = mixer_ctl_get_range_min(ctl);
            max = mixer_ctl_get_range_max(ctl);
            printf("%s(dsrange %d->%d)", space, min, max);
        }
    }

    free(buf);

    printf("\n");
    return 0;
}

 

5.tinymix-tinymix_set_value

        tinymix_set_value接口在tinymix使用中应该是最重要的,所有的配置都需要这个接口进行控制,应用层或Audio HAL层调用的set audio mixer参数都要通过mixer_open、tinymix_set_value、mixer_close接口。下图所示是包含配置增益的参数配置check。

static int tinymix_set_value(struct mixer *mixer, const char *control,
                             char **values, unsigned int num_values)
{
    struct mixer_ctl *ctl;
    enum mixer_ctl_type type;
    unsigned int num_ctl_values;
    unsigned int i;

    if (isdigit(control[0]))
        ctl = mixer_get_ctl(mixer, atoi(control));
    else
        ctl = mixer_get_ctl_by_name(mixer, control);

    if (!ctl) {
        fprintf(stderr, "Invalid mixer control: %s\n", control);
        return ENOENT;
    }

    type = mixer_ctl_get_type(ctl);
    num_ctl_values = mixer_ctl_get_num_values(ctl);

    if (type == MIXER_CTL_TYPE_BYTE) {
        tinymix_set_byte_ctl(ctl, values, num_values);
        return ENOENT;
    }

    if (isdigit(values[0][0])) {
        if (num_values == 1) {
            /* Set all values the same */
            int value = atoi(values[0]);

            for (i = 0; i < num_ctl_values; i++) {
                if (mixer_ctl_set_value(ctl, i, value)) {
                    fprintf(stderr, "Error: invalid value\n");
                    return EINVAL;
                }
            }
        } else {
            /* Set multiple values */
            if (num_values > num_ctl_values) {
                fprintf(stderr,
                        "Error: %u values given, but control only takes %u\n",
                        num_values, num_ctl_values);
                return EINVAL;
            }
            for (i = 0; i < num_values; i++) {
                if (mixer_ctl_set_value(ctl, i, atoi(values[i]))) {
                    fprintf(stderr, "Error: invalid value for index %d\n", i);
                    return EINVAL;
                }
            }
        }
    } else {
        if (type == MIXER_CTL_TYPE_ENUM) {
            if (num_values != 1) {
                fprintf(stderr, "Enclose strings in quotes and try again\n");
                return EINVAL;
            }
            if (mixer_ctl_set_enum_by_string(ctl, values[0])) {
                fprintf(stderr, "Error: invalid enum value\n");
                return EINVAL;
            }
        } else {
            fprintf(stderr, "Error: only enum types can be set with strings\n");
            return EINVAL;
        }
    }

    return 0;
}

小结

        mixer和pcm一起构成了android 音频控制的底层架构TinyAlsa,对Android所有的音频控件而言,都需要通过JNI或者Audio HAL调到tinyalsa底层的mixer、pcm去进行音频device的初始以及配置问题。

        mixer里面有提供很多灵活的接口供linux底层,比如mixer_get_ctl、mixer_ctl_get_name、mixer_get_ctl_by_name、mixer_ctl_get_num_values等,相关的开发任务关键在于check API的熟悉度。

在MTK平台上使用tinyalsa进行音频设备的初始化和测试,关键在于理解其在Android系统中的位置和作用。首先,你需要对Android系统中音频架构有所了解,包括内核中的ALSA驱动、HAL层以及用户空间的tinyalsa库和AudioFlinger服务。以下是详细的操作步骤: 参考资源链接:[MTK平台Android tinyalsa音频测试:简化与内核集成](https://wenku.youkuaiyun.com/doc/53gr29go8p?spm=1055.2569.3001.10343) 1. **环境准备**:确保你的开发环境已经搭建好Android源码,并且对MTK平台的构建系统有一定的了解。下载并编译MTK平台的Android源码,确保tinyalsa库和相关的驱动模块被正确编译进内核。 2. **内核驱动加载**:在系统启动时,需要加载相应的音频驱动模块。通常这一步是通过修改`/system/etc/init.rc`文件来实现的,确保在开机时自动加载音频驱动。 3. **tinyalsa初始化**:在应用层,通过tinyalsa提供的API初始化音频设备。首先,需要打开PCM设备节点,例如`/dev/snd/pcmC0D0p`和`/dev/snd/pcmC0D0c`,分别用于播放和录音。使用`pcm_open`函数进行打开,并配置相应的参数,如采样率、声道数、位深度等。 4. **音频测试**:初始化PCM设备后,即可进行音频测试。对于播放测试,使用`pcm_writei`函数将音频数据写入设备。对于录音测试,使用`pcm_readi`函数从设备读取音频数据。 5. **验证测试结果**:播放和录音测试完成后,验证音频数据是否正确。播放时可以使用外部设备监听声音输出,录音时可以检查录音文件的内容是否符合预期。 6. **资源释放**:测试完成后,需要关闭PCM设备并释放相关资源,使用`pcm_close`函数。 为了更好地掌握这一过程,推荐阅读《MTK平台Android tinyalsa音频测试:简化与内核集成》这本书。它详细介绍了MTK平台音频测试的流程以及如何使用tinyalsa进行操作,特别适合希望深入了解MTK平台音频测试的开发者。 通过上述步骤,你可以在MTK平台上使用tinyalsa进行音频设备的初始化和测试。掌握这些技能后,你将能够有效地进行音频相关的性能优化和系统移植工作。此外,如果你对Linux ALSA内核驱动、用户层接口、PCM设备和Mixer设备有更深入的兴趣,建议继续阅读《MTK平台Android tinyalsa音频测试:简化与内核集成》中的高级章节,那里有更多的细节和高级特性等待你去发掘。 参考资源链接:[MTK平台Android tinyalsa音频测试:简化与内核集成](https://wenku.youkuaiyun.com/doc/53gr29go8p?spm=1055.2569.3001.10343)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值