apx_ctl.c example

This is the source for the apx_ctl utility.

Note: The apx_ctl utility is shipped with Acoustic Management Platform 3.0.

For information about using this utility, see apx_ctl in the QNX Neutrino Utilities Reference.

/*
 * Copyright 2018, QNX Software Systems Ltd. All Rights Reserved.
 *
 * This source code may contain confidential information of QNX Software
 * Systems Ltd.  (QSSL) and its licensors. Any use, reproduction,
 * modification, disclosure, distribution or transfer of this software,
 * or any software which includes or is based upon any of this code, is
 * prohibited unless expressly authorized by QSSL by written agreement. For
 * more information (including whether this source code file has been
 * published) please email licensing@qnx.com.
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/asoundlib.h>
#include <string.h>
#include <devctl.h>
#include <errno.h>
#include <ctype.h>
#include <ioctl.h>


//*****************************************************************************
/* *INDENT-OFF* */
#ifdef __USAGE
%C [Options]

Options:
    -a [card#:]<dev#>                     the card & device number to access
                                            OR
    -a[name]                              the card name to access
    -e <chns:volume_chn0:...:volume_chnN> set volume of external amplifiers (outside of io-audio system)
    -u <user volume>                      override default user volume, with system output volume
    -t <apx_id:dataset>                   load runtime acoustic processing dataset
    -x <apx_id:param_id:chn>[:data]       calls set/get data on apx for parameters of size int16_t
    -y <apx_id:param_id:chn>[:data]       calls set/get data on apx for parameters of size int32_t
                                          SPM : apx_id=1
                                          SFO : apx_id=2
#endif
/* *INDENT-ON* */
//*****************************************************************************

const char* optstring = "a:e:u:t:x:y:";

static snd_pcm_t* setup_pcm_handle(char* arg)
{
    int rtn;
    int dev = 0, card = 0;
    const char* name = NULL;
    snd_pcm_t* pcm_handle = NULL;

    if (strchr (arg, ':'))
    {
        card = strtol (arg, &arg, 0);
        if (errno == ERANGE || errno == EINVAL) {
            fprintf(stderr, "Invalid card number\n");
            return NULL;
        }
        if (*arg)  arg++;
        dev = strtol (arg, NULL, 0);
        if (errno == ERANGE || errno == EINVAL) {
            fprintf(stderr, "Invalid device number\n");
            return NULL;
        }
    } else if (isalpha (arg[0]) || arg[0] == '/') {
        name = arg;
    } else {
        dev = strtol (arg, NULL, 0);
        if (errno == ERANGE || errno == EINVAL) {
            fprintf(stderr, "Invalid device number\n");
            return NULL;
        }
    }

    if (name) {
        fprintf (stderr, "Using device %s\n", name);
        rtn = snd_pcm_open_name(&pcm_handle, name, SND_PCM_OPEN_PLAYBACK);
    } else {
        fprintf (stderr, "Using card %d device %d \n", card, dev);
        rtn = snd_pcm_open (&pcm_handle, card, dev, SND_PCM_OPEN_PLAYBACK);
    }
    if (rtn < 0) {
        fprintf(stderr, "Cannot open pcm device\n");
        return NULL;
    }
    return pcm_handle;
}

static int set_ext_vol(snd_pcm_t* pcm_handle, const char* arg)
{
    int i, rtn;
    int16_t ext_vol [32] = {0};
    int ext_vol_channels = strtol (optarg, &optarg, 0);
    if (errno == ERANGE || errno == EINVAL || ext_vol_channels > 32) {
        fprintf(stderr, "Invalid number of external volume channels\n");
        return 1;
    }
    for (i = 0; (i < 32) && optarg && (*optarg == ':') && (errno != ERANGE) && (errno != EINVAL); i++) {
        optarg++;
        ext_vol[i] = strtol (optarg, &optarg, 0);
    }
    rtn = snd_pcm_set_apx_external_volume(pcm_handle, ext_vol_channels, ext_vol);
    printf("APX set external volume ret=%d -- %s\n", rtn, strerror(rtn));
    return rtn;
}

static int set_user_vol(snd_pcm_t* pcm_handle, int16_t vol)
{
    int rtn = snd_pcm_set_apx_user_volume(pcm_handle, vol);
    printf("APX set user volume to %d ret=%d -- %s\n", vol, rtn, strerror(rtn));
    return rtn;
}

static int set_dataset(snd_pcm_t* pcm_handle, char* dataset)
{
    int rtn, ap_status = 0;
    int32_t apx_id = strtol (dataset, &dataset, 0);
    if (errno == ERANGE || errno == EINVAL) {
        fprintf(stderr, "Invalid apx id\n");
        return 1;
    }
    dataset++;
    printf("Loading dataset %s\n", dataset);
    if ((rtn = snd_pcm_load_apx_dataset(pcm_handle, apx_id, dataset, &ap_status)) != EOK)
        printf("Failed to load dataset: (%d) %s\n", rtn, snd_strerror(rtn));
    if (ap_status != 0) {
        printf("Acoustic processing returned status=0x%04X\n", ap_status);
    }
    return rtn;
}

static int set_ap_data(snd_pcm_t* pcm_handle, char* arg, size_t data_size)
{
    int set_data, rtn;
    int32_t apx_id;
    uint32_t data;
    snd_pcm_apx_param_t param = {
        .size = data_size,
    };

    apx_id = strtol (arg, &arg, 0);
    if (errno == ERANGE || errno == EINVAL) {
        fprintf(stderr, "Invalid apx id\n");
        return 1;
    }
    if (*arg) arg++;
    param.dataId = strtol (arg, &arg, 0);
    if (errno == ERANGE || errno == EINVAL) {
        fprintf(stderr, "Invalid apx data id\n");
        return 1;
    }
    if (*arg) arg++;
    param.channel = strtol (arg, &arg, 0);
    if (errno == ERANGE || errno == EINVAL) {
        fprintf(stderr, "Invalid apx channel\n");
        return 1;
    }
    if (*arg) arg++;
    data = strtol (arg, NULL, 0);
    set_data = (errno != ERANGE && errno != EINVAL);
    errno = EOK;
    if (set_data) {
        printf("APX set_ap_data apx=%d id=0x%04x data=%d ", apx_id, param.dataId, data);
        rtn = snd_pcm_set_apx_data(pcm_handle, apx_id, &param, &data);
    } else {
        printf("APX get_ap_data apx=%d id=0x%04x ", apx_id, param.dataId);
        rtn = snd_pcm_get_apx_data(pcm_handle, apx_id, &param, &data);
    }
    printf("ret=%d ap_ret=%d data_ret=%d -- %s\n", rtn, param.status, data, strerror(-rtn));
    return rtn;
}

int main(int argc, char *argv[])
{
    int rtn = EOK;
    snd_pcm_t *pcm_handle = NULL;
    int c;

    /* first setup pcm handle */
    while ((c = getopt(argc, argv, optstring)) != EOF)
    {
        switch (c)
        {
        case 'a':
            if (!(pcm_handle = setup_pcm_handle(optarg))) {
                return EXIT_FAILURE;
            }

            break;
        default:
            break;
        }
    }

    if (!pcm_handle) {
        fprintf (stderr, "Using preferred device \n");
        if (snd_pcm_open_preferred(&pcm_handle, NULL, NULL, SND_PCM_OPEN_PLAYBACK) != EOK)  {
            fprintf(stderr, "Cannot open pcm device\n");
            return EXIT_FAILURE;
        }
    }

    /* reset optind and handle other options */
    optind = 1;
    while (((c = getopt(argc, argv, optstring)) != EOF) && (rtn == EOK))
    {
        switch (c)
        {
        case 'a':
            break;

        case 'e':
            rtn = set_ext_vol(pcm_handle, optarg);
            break;

        case 'u':
            rtn = set_user_vol(pcm_handle, atoi(optarg));
            break;

        case 't':
            rtn = set_dataset(pcm_handle, optarg);
            break;

        case 'x':
            rtn = set_ap_data(pcm_handle, optarg, sizeof(uint16_t));
            break;

        case 'y':
            rtn = set_ap_data(pcm_handle, optarg, sizeof(uint32_t));
            break;

        default:
            printf("Invalid option '%c'\n", c);
            rtn = -1;
            break;
        }
    }
    snd_pcm_close(pcm_handle);
    return (rtn == EOK) ? EXIT_SUCCESS : EXIT_FAILURE;
}

#if defined(__QNXNTO__) && defined(__USESRCVERSION)
#include <sys/srcversion.h>
__SRCVERSION("$URL$ $Rev$")
#endif