audiomgmt_monitor.c example

Updated: April 19, 2023

This is a sample application shows how to get the current active audio types.

You can use the following options to run the program:

/*
 * $QNXLicenseC:
 * Copyright 2017, QNX Software Systems.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"). You
 * may not reproduce, modify or distribute this software except in
 * compliance with the License. You may obtain a copy of the License
 * at: http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTIES OF ANY KIND, either express or implied.
 *
 * This file may contain contributions from others, either as
 * contributors under the License or as licensors under other terms.
 * Please review this entire file for other proprietary rights or license
 * notices, as well as the QNX Development Suite License Guide at
 * http://licensing.qnx.com/license-guide/ for other information.
 * $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <devctl.h>
#include <errno.h>
#include <sys/asoundlib.h>

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

Options:
   -a<cardno>     Card number
   -n<output_id>  Ducking output ID to monitor
   -l             Use legacy status events

#endif
/* *INDENT-ON* */
//*****************************************************************************

static void audiomgmt_cb ( snd_ctl_t *hdl, void *private_data, int cmd)
{
    int status, i, j;
    const char *ducking_output = private_data;

    switch (cmd)
    {
        case SND_CTL_READ_AUDIOMGMT_CHG:    /* deprecated  - Use SND_CTL_READ_AUDIOMGMT_STATUS_CHG */
        {
            snd_ducking_status_t *info = NULL;
            snd_ducking_type_status_t *active_type = NULL;

            if ((status = snd_ctl_ducking_read(hdl, ducking_output, &info )) != EOK)
            {
                printf("Failed to read ducking info - %s\n", snd_strerror(status));
                return;
            }

            printf("\nActive audio types = %d\n", info->ntypes);
            /* The active_types structure is variable in size based on the number of pids it references, so we must
             * walk the active_type member of the info structure using the SND_DUCKING_STATUS_NEXT_TYPE() marco
             */
            for (i = 0, active_type = info->active_types; i < info->ntypes; i++,
                 active_type = SND_DUCKING_STATUS_NEXT_TYPE(active_type))
            {
                printf("Audio Type %s, Priority %d\n", active_type->name, active_type->prio);
                printf("\tPids (%d): ", active_type->npids);
                for (j = 0; j < active_type->npids; j++)
                {
                    printf("%d ", active_type->pids[j]);
                }
                printf("\n");
            }
            /* snd_ctl_ducking_read() will allocate the info buffer, so we must free it */
            free(info);
            break;
        }
        case SND_CTL_READ_AUDIOMGMT_STATUS_CHG:
        {
            snd_ducking_priority_status_t *info = NULL;

            if ((status = snd_ctl_ducking_status_read(hdl, ducking_output, &info )) != EOK)
            {
                printf("Failed to read ducking info - %s\n", snd_strerror(status));
                return;
            }

            printf("Active Subchns (%d):\n", info->nsubchns);
            /* The priority status structure is variable in size based on the number of subchns it references, so we must
             * walk the entries member of the info structure using the SND_DUCKING_STATUS_NEXT_ENTRY() marco
             */
            for (i = 0; i < info->nsubchns; i++)
            {
                printf("\tPriority: %d\n", info->subchns[i].prio);
                printf("\tPid: %d\n", info->subchns[i].pid);
                printf("\tAudio Type: %s\n", info->subchns[i].name);
                printf("\tState: ");
                if (info->subchns[i].state & SND_PCM_DUCKING_STATE_FORCED_ACTIVE)
                    printf("FORCED_ACTIVE ");
                else
                    printf("ACTIVE ");
                if (info->subchns[i].state & SND_PCM_DUCKING_STATE_DUCKED)
                    printf("| DUCKED ");
                if (info->subchns[i].state & SND_PCM_DUCKING_STATE_HARD_SUSPENDED)
                    printf("| HARD_SUSPENDED ");
                if (info->subchns[i].state & SND_PCM_DUCKING_STATE_SOFT_SUSPENDED)
                    printf("| SOFT_SUSPENDED ");
                if (info->subchns[i].state & SND_PCM_DUCKING_STATE_PAUSED)
                    printf("| PAUSED ");
                if (info->subchns[i].state & (SND_PCM_DUCKING_STATE_MUTE_BY_HIGHER|SND_PCM_DUCKING_STATE_MUTE_BY_SAME))
                    printf("| MUTED ");

                if (info->subchns[i].ducked_by & (SND_PCM_DUCKED_BY_SAME_PRIO|SND_PCM_DUCKED_BY_HIGHER_PRIO))
                {
                    printf("\n\tDucked by: ");
                    if (info->subchns[i].ducked_by & SND_PCM_DUCKED_BY_SAME_PRIO)
                        printf("SAME PRIORITY ");
                    else if (info->subchns[i].ducked_by & SND_PCM_DUCKED_BY_HIGHER_PRIO)
                        printf("HIGHER PRIORITY ");
                    if (info->subchns[i].ducked_by & SND_PCM_DUCKED_BY_NONTRANSIENT)
                        printf("| NON-TRANSIENT");
                    else
                        printf("| TRANSIENT");
                }
                printf("\n\n");
            }
            printf("\n");

            /* snd_ctl_ducking_status_read() will allocate the info buffer, so we must free it */
            free(info);
            break;
        }
        default:
            break;
    }
}

int main (int argc, char *argv[])
{
    int c;
    int legacy = 0;
    fd_set rfds;
    int card = 0, ctl_fd;
    snd_ctl_t *handle;
    snd_ctl_callbacks_t callbacks;
    snd_ctl_filter_t filter;
    char *ducking_output = NULL;

    optind = 1;
    while ((c = getopt(argc, argv, "a:n:l")) != EOF)
    {
        switch (c)
        {
            case 'a':
                card = strtol (optarg, &optarg, 0);
                break;
            case 'n':
                ducking_output = optarg;
                break;
            case 'l':
                legacy = 1;
                break;
            default:
                break;
        }
    }
    if (ducking_output == NULL)
    {
        printf("Ducking output not provided\n");
        return 1;
    }
    if (snd_ctl_open(&handle, card) != EOK)
    {
        printf("Failed to open control device on card %d\n", card);
        return 1;
    }
    ctl_fd = snd_ctl_file_descriptor(handle);
    memset(&callbacks, 0, sizeof (callbacks));
    callbacks.private_data = ducking_output;
    callbacks.audiomgmt = &audiomgmt_cb;

    /* Enable audio management status events */
    if (legacy)
        filter.enable = SND_CTL_EVENT_MASK(SND_CTL_READ_AUDIOMGMT_CHG);
    else
        filter.enable = SND_CTL_EVENT_MASK(SND_CTL_READ_AUDIOMGMT_STATUS_CHG);
    snd_ctl_set_filter(handle, &filter);

    FD_ZERO(&rfds);
    while (1)
    {
        FD_SET(ctl_fd, &rfds);
        if (select(ctl_fd + 1, &rfds, NULL, NULL, NULL) == -1)
        {
            printf("Select failed - %s\n", strerror(errno));
            return 1;
        }

        if (FD_ISSET(ctl_fd, &rfds))
        {
            snd_ctl_read(handle, &callbacks);
        }
    }
    snd_ctl_close (handle);
    return 0;
}

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