waverec.c example

Updated: October 26, 2022

This is a sample application that captures (i.e., records) audio data.

For information about using this utility, see waverec in the QNX Neutrino Utilities Guide.

/*
 * $QNXLicenseC:
 * Copyright 2016, QNX Software Systems. All Rights Reserved.
 *
 * You must obtain a written license from and pay applicable license fees to QNX
 * Software Systems before you may reproduce, modify or distribute this software,
 * or any work that includes all or part of this software.   Free development
 * licenses are available for evaluation and non-commercial purposes.  For more
 * information visit http://licensing.qnx.com or email licensing@qnx.com.
 *
 * This file may contain contributions from others.  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 <errno.h>
#include <fcntl.h>
#include <gulliver.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/select.h>
#include <sys/stat.h>
#include <sys/termio.h>
#include <sys/types.h>
#include <unistd.h>
#include <limits.h>
#include <ctype.h>

#include <sys/asoundlib.h>

/* *INDENT-OFF* */
struct
{
    char        riff_id[4];
    uint32_t    wave_len;
    struct
    {
        char        fmt_id[8];
        uint32_t    fmt_len;
        struct
        {
            uint16_t    format_tag;
            uint16_t    voices;
            uint32_t    rate;
            uint32_t    char_per_sec;
            uint16_t    block_align;
            uint16_t    bits_per_sample;
        }
        fmt;
        struct
        {
            char        data_id[4];
            uint32_t    data_len;
        }
        data;
    }
    wave;
}
riff_hdr =
{
    {'R', 'I', 'F', 'F' },
    sizeof (riff_hdr.wave),
    {
        {'W', 'A', 'V', 'E', 'f', 'm', 't', ' '    },
        sizeof (riff_hdr.wave.fmt),
        {
            1, 0, 0, 0, 0, 0
        },
        {
            {'d', 'a', 't', 'a' },
            0,
        }
    }
};
/* *INDENT-ON* */

static snd_mixer_t *mixer_handle = NULL;
static snd_pcm_t *pcm_handle = NULL;
static snd_pcm_channel_params_t pp;
static char *mSampleBfr1 = NULL;
static FILE *file1 = NULL;
static int stdin_raw = 0;
static int bytes_to_drain = 0;
static useconds_t period_us = 0;

static int
dev_raw (int fd)
{
    struct termios termios_p;

    if (tcgetattr (fd, &termios_p))
        return (-1);

    termios_p.c_cc[VMIN] = 1;
    termios_p.c_cc[VTIME] = 0;
    termios_p.c_lflag &= ~(ICANON | ECHO | ISIG);
    return (tcsetattr (fd, TCSANOW, &termios_p));
}

static int
dev_unraw (int fd)
{
    struct termios termios_p;

    if (tcgetattr (fd, &termios_p))
        return (-1);

    termios_p.c_lflag |= (ICANON | ECHO | ISIG);
    return (tcsetattr (fd, TCSAFLUSH, &termios_p));
}

static void
cleanup(void)
{
    if (stdin_raw)
        dev_unraw (fileno (stdin));
    if (mSampleBfr1)
        free(mSampleBfr1);
    if (mixer_handle)
        snd_mixer_close (mixer_handle);
    if (file1)
        fclose(file1);
    if (pcm_handle)
        snd_pcm_close (pcm_handle);
}

static void
cleanup_and_exit(int exit_code)
{
    cleanup();
    exit(exit_code);
}

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

Options:
    -a[card#:]<dev#>   the card & device number to record from
                       OR
    -a[name]           the symbolic name of the device to record from
    -b <size>          Sample size (8, 16, 24, 32)
    -m                 record in mono (stereo default)
    -n <voices>        the number of voices to record (2 voices default, stereo)
    -r <rate>          record at rate (44100 default | 48000 44100 22050 11025)
    -t <sec>           seconds to record (5 seconds default)
    -f <frag_size>     requested fragment size
    -v                 verbosity
    -c <args>[,args ...] voice matrix configuration
    -x                 use mmap interface
    -i <0|1>           Interleave samples (default: 1)
    -R <value>         SRC rate method
                       (0 = linear interpolation, 1 = 7-pt kaiser windowed, 2 = 20-pt remez)
    -z<num_frags>      requested number of fragments
    -y                 Nonblocking mode

Note:
    If both 'm' and 'n' are specified in commandline, the one specified later will be used

Args:
    1=<hw_channel_bitmask> hardware channel bitmask for application voice 1
    2=<hw_channel_bitmask> hardware channel bitmask for application voice 2
    3=<hw_channel_bitmask> hardware channel bitmask for application voice 3
    4=<hw_channel_bitmask> hardware channel bitmask for application voice 4
    5=<hw_channel_bitmask> hardware channel bitmask for application voice 5
    6=<hw_channel_bitmask> hardware channel bitmask for application voice 6
    7=<hw_channel_bitmask> hardware channel bitmask for application voice 7
    8=<hw_channel_bitmask> hardware channel bitmask for application voice 8

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

volatile int end = 0;

static void sig_handler( int sig_no )
{
    end = 1;
    return;
}

static const char *
why_failed ( int why_failed )
{
    switch (why_failed)
    {
        case SND_PCM_PARAMS_BAD_MODE:
            return ("Bad Mode Parameter");
        case SND_PCM_PARAMS_BAD_START:
            return ("Bad Start Parameter");
        case SND_PCM_PARAMS_BAD_STOP:
            return ("Bad Stop Parameter");
        case SND_PCM_PARAMS_BAD_FORMAT:
            return ("Bad Format Parameter");
        case SND_PCM_PARAMS_BAD_RATE:
            return ("Bad Rate Parameter");
        case SND_PCM_PARAMS_BAD_VOICES:
            return ("Bad Vocies Parameter");
        case SND_PCM_PARAMS_NO_CHANNEL:
            return ("No Channel Available");
        default:
            return ("Unknown Error");
    }

    return ("No Error");
}

int
main (int argc, char **argv)
{
    int     i, j;
    int     card = -1;
    int     dev = 0;
    int     ret;

    unsigned int mSamples;
    int     mSampleRate;
    int     mSampleChannels;
    int     mSampleBits;
    int     mSampleBytes;
    int     mSampleTime;
    int     fragsize = -1;
    int     num_frags = -1;
    int     verbose = 0;
    int     mode = SND_PCM_OPEN_CAPTURE;

    int     rtn;
    int     mixer_fd = 0;
    int     pcm_fd;

    snd_pcm_channel_info_t pi;
    snd_mixer_group_t group;
    snd_pcm_channel_setup_t setup;
    int     bsize, N = 0, c;
#define MAX_VOICES 8
    uint32_t voice_mask[MAX_VOICES] = { 0 };
    struct {
        snd_pcm_chmap_t map;
        unsigned int pos[32];
    } map;
    snd_pcm_voice_conversion_t voice_conversion;
    int     voice_override = 0;
    char   *sub_opts, *sub_opts_copy, *value;
    char   *dev_opts[] = {
#define CHN1 0
        "1",
#define CHN2 1
        "2",
#define CHN3 2
        "3",
#define CHN4 3
        "4",
#define CHN5 4
        "5",
#define CHN6 5
        "6",
#define CHN7 6
        "7",
#define CHN8 7
        "8",
        NULL
    };
    char    name[_POSIX_PATH_MAX] = { 0 };
    int interleave = 1;
    fd_set  rfds, ofds;
    int use_mmap = 0;
    int rate_method = 0;
    snd_pcm_filter_t pevent;

    mSampleRate = 44100;
    mSampleChannels = 2;
    mSampleBits = 16;
    mSampleBytes = 2;
    mSampleTime = 5;

    while ((c = getopt (argc, argv, "b:a:f:mn:r:t:vc:xi:R:z:y")) != EOF)
    {
        switch (c)
        {
        case 'b':
            mSampleBits = atoi (optarg);
            if (mSampleBits != 8 && mSampleBits != 16 && mSampleBits != 24 && mSampleBits != 32)
            {
                fprintf(stderr, "Invalid sample size, must be one of 8, 16, 24, 32\n");
                return (EXIT_FAILURE);
            }
            mSampleBytes = mSampleBits/8;
            break;
        case 'a':
            if (strchr (optarg, ':'))
            {
                card = atoi (optarg);
                dev = atoi (strchr (optarg, ':') + 1);
            }
            else if (isalpha (optarg[0]) || optarg[0] == '/')
                strcpy (name, optarg);
            else
                dev = atoi (optarg);

            if (name[0] != '\0')
                printf ("Using device /dev/snd/%s\n", name);
            else
                printf ("Using card %d device %d \n", card, dev);
            break;
        case 'f':
            fragsize = atoi (optarg);
            break;
        case 'i':
            interleave = atoi(optarg);
            if (interleave <= 0)
                interleave = 0;
            else
                interleave = 1;
            break;
        case 'm':
            mSampleChannels = 1;
            break;
        case 'n':
            mSampleChannels = atoi (optarg);
            break;
        case 'r':
            mSampleRate = atoi (optarg);
            break;
        case 't':
            mSampleTime = atoi (optarg);
            break;
        case 'v':
            verbose = 1;
            break;
        case 'c':
            sub_opts = sub_opts_copy = strdup (optarg);
            if (sub_opts == NULL) {
                perror("Cannot allocate sub_opts");
                return (EXIT_FAILURE);
            }
            while (*sub_opts != '\0')
            {
                int channel = getsubopt (&sub_opts, dev_opts, &value);
                if( (channel >= 0) && (channel < MAX_VOICES) && value ) {
                    voice_mask[channel] = strtoul (value, NULL, 0);
                } else {
                    fprintf (stderr, "Invalid channel map specified\n");
                    free(sub_opts_copy);
                    return (EXIT_FAILURE);
                }
            }
            free(sub_opts_copy);
            voice_override = 1;
            break;
        case 'x':
            use_mmap = 1;
            break;
        case 'R':
            rate_method = atoi(optarg);
            if (rate_method < 0 || rate_method > 2)
            {
                rate_method = 0;
                printf("Invalid rate method, using method 0\n");
            }
            break;
        case 'z':
            num_frags = atoi (optarg) - 1;
            break;
        case 'y':
            mode |= SND_PCM_OPEN_NONBLOCK;
            break;
        default:
            fprintf(stderr, "Invalid option -%c\n", c);
            return (EXIT_FAILURE);
        }
    }

    if (optind >= argc)
    {
        fprintf(stderr, "no file specified\n");
        return (EXIT_FAILURE);
    }

    if (name[0] != '\0')
    {
        snd_pcm_info_t info;

        if ((rtn = snd_pcm_open_name (&pcm_handle, name, mode)) < 0)
        {
            fprintf(stderr, "snd_pcm_open_name failed - %s\n", snd_strerror(rtn));
            return (EXIT_FAILURE);
        }
        rtn = snd_pcm_info (pcm_handle, &info);
        card = info.card;
    }
    else
    {
        if (card == -1)
        {
            if ((rtn = snd_pcm_open_preferred (&pcm_handle, &card, &dev, mode)) < 0)
            {
                fprintf(stderr, "snd_pcm_open_preferred failed - %s\n", snd_strerror(rtn));
                return (EXIT_FAILURE);
            }
        }
        else
        {
            if ((rtn = snd_pcm_open (&pcm_handle, card, dev, mode)) < 0)
            {
                fprintf(stderr, "snd_pcm_open failed - %s\n", snd_strerror(rtn));
                return (EXIT_FAILURE);
            }
        }
    }

    if ((file1 = fopen (argv[optind], "w")) == 0)
    {
        perror("file open failed");
        cleanup_and_exit(EXIT_FAILURE);
    }

    if( mSampleTime == 0 ) {
        mSamples = 0xFFFFFFFF - sizeof(riff_hdr) + 8;
    } else {
        mSamples = mSampleRate * mSampleChannels * mSampleBytes * mSampleTime;
    }

    riff_hdr.wave.fmt.voices = ENDIAN_LE16 (mSampleChannels);
    riff_hdr.wave.fmt.rate = ENDIAN_LE32 (mSampleRate);
    riff_hdr.wave.fmt.char_per_sec =
        ENDIAN_LE32 (mSampleRate * mSampleChannels * mSampleBytes);
    riff_hdr.wave.fmt.block_align = ENDIAN_LE16 (mSampleChannels * mSampleBytes);
    riff_hdr.wave.fmt.bits_per_sample = ENDIAN_LE16 (mSampleBits);
    riff_hdr.wave.data.data_len = ENDIAN_LE32 (mSamples);
    riff_hdr.wave_len = ENDIAN_LE32 (mSamples + sizeof (riff_hdr) - 8);
    fwrite (&riff_hdr, 1, sizeof (riff_hdr), file1);

    printf ("SampleRate = %d, Channels = %d, SampleBits = %d, SampleBytes = %d\n",
        mSampleRate, mSampleChannels, mSampleBits, mSampleBytes);

    /* Enable PCM events */
    pevent.enable = (1<<SND_PCM_EVENT_OVERRUN);
    snd_pcm_set_filter(pcm_handle, SND_PCM_CHANNEL_CAPTURE, &pevent);

    if (use_mmap)
    {
        snd_pcm_plugin_set_enable (pcm_handle, PLUGIN_MMAP);
    }

    memset (&pi, 0, sizeof (pi));
    pi.channel = SND_PCM_CHANNEL_CAPTURE;
    if ((rtn = snd_pcm_plugin_info (pcm_handle, &pi)) < 0)
    {
        fprintf (stderr, "snd_pcm_plugin_info failed: %s\n", snd_strerror (rtn));
        cleanup_and_exit(EXIT_FAILURE);
    }

    memset (&pp, 0, sizeof (pp));

    pp.mode = SND_PCM_MODE_BLOCK;
    pp.channel = SND_PCM_CHANNEL_CAPTURE;
    pp.start_mode = SND_PCM_START_DATA;
    pp.stop_mode = SND_PCM_STOP_STOP;
    pp.time = 1;

    pp.buf.block.frag_size = pi.max_fragment_size;
    if (fragsize != -1)
        pp.buf.block.frag_size = fragsize;
    pp.buf.block.frags_max = num_frags;
    pp.buf.block.frags_min = 1;

    pp.format.interleave = interleave;
    pp.format.rate = mSampleRate;
    pp.format.voices = mSampleChannels;

    switch (mSampleBits)
    {
        case 8:
            pp.format.format = SND_PCM_SFMT_U8;
            break;
        case 16:
        default:
            pp.format.format = SND_PCM_SFMT_S16_LE;
            break;
        case 24:
            pp.format.format = SND_PCM_SFMT_S24_LE;
            break;
        case 32:
            pp.format.format = SND_PCM_SFMT_S32_LE;
            break;
    }

    if ((rtn = snd_pcm_plugin_set_src_method(pcm_handle, rate_method)) != rate_method)
    {
        fprintf(stderr, "Failed to apply rate_method %d, using %d\n", rate_method, rtn);
    }

    if ((rtn = snd_pcm_plugin_params (pcm_handle, &pp)) < 0)
    {
        fprintf (stderr, "snd_pcm_plugin_params failed: %s - %s\n", snd_strerror (rtn), why_failed(pp.why_failed));
        cleanup_and_exit(EXIT_FAILURE);
    }

    if (voice_override)
    {
        snd_pcm_plugin_get_voice_conversion (pcm_handle, SND_PCM_CHANNEL_CAPTURE,
            &voice_conversion);
        for(i = 0; i < MAX_VOICES; i++) {
            voice_conversion.matrix[i] = voice_mask[i];
        }
        snd_pcm_plugin_set_voice_conversion (pcm_handle, SND_PCM_CHANNEL_CAPTURE,
            &voice_conversion);
    }

    memset (&setup, 0, sizeof (setup));
    memset (&group, 0, sizeof (group));
    setup.channel = SND_PCM_CHANNEL_CAPTURE;
    setup.mixer_gid = &group.gid;
    if ((rtn = snd_pcm_plugin_setup (pcm_handle, &setup)) < 0)
    {
        fprintf (stderr, "snd_pcm_plugin_setup failed: %s\n", snd_strerror (rtn));
        cleanup_and_exit(EXIT_FAILURE);
    }
    printf ("Format %s \n", snd_pcm_get_format_name (setup.format.format));
    printf ("Frag Size %d \n", setup.buf.block.frag_size);
    printf ("Total Frags %d \n", setup.buf.block.frags);
    printf ("Rate %d \n", setup.format.rate);
    bsize = setup.buf.block.frag_size;
    period_us = (( (int64_t)bsize * 1000000 ) / (mSampleBytes * mSampleChannels * mSampleRate));
    printf("Frag Period is %.3f ms\n", (double)period_us/1000);
    period_us *= (setup.buf.block.frags - 1);   /* For adding delay for drain handling */

    map.map.channels = 32;
    if((rtn = snd_pcm_query_channel_map(pcm_handle, &map.map)) == EOK) {
        printf("Channel map:");
        if((rtn = snd_pcm_plugin_get_voice_conversion (pcm_handle, SND_PCM_CHANNEL_CAPTURE, &voice_conversion)) != EOK) {
            // The hardware map is the same as the voice map
            for( i = 0; i < map.map.channels; i ++ ) {
                printf("(%s) ", snd_pcm_get_chmap_channel_name(map.map.pos[i]));
            }
            printf("\n");
        } else {
            // Map hardware channels according to the voice map
            for( i = 0; i < voice_conversion.app_voices; i ++ ) {
                bool printed = false;
                printf(" (");
                for( j = 0; j < voice_conversion.hw_voices; j ++ ) {
                    if( voice_conversion.matrix[i] & (1<<j) ) {
                        if ( printed ) {
                            printf("+");
                        } else {
                            printed = true;
                        }
                        printf("%s", snd_pcm_get_chmap_channel_name(map.map.pos[j]));
                    }
                }
                if (printed)
                    printf(")");
                else
                    printf("Silence)");
            }
            printf("\n");
        }
    }

    if (group.gid.name[0] == 0)
    {
        printf ("Mixer Pcm Group [%s] Not Set \n", group.gid.name);
        printf ("***>>>> Input Gain Controls Disabled <<<<*** \n");
    }
    else
    {
        printf ("Mixer Pcm Group [%s]\n", group.gid.name);
        if ((rtn = snd_mixer_open (&mixer_handle, setup.mixer_card, setup.mixer_device)) < 0)
        {
            fprintf (stderr, "snd_mixer_open failed: %s\n", snd_strerror (rtn));
            cleanup_and_exit(EXIT_FAILURE);
        }
    }

    if (tcgetpgrp (0) == getpid ()) {
        stdin_raw = 1;
        dev_raw (fileno (stdin));
    }

    mSampleBfr1 = malloc (bsize);
    if ( mSampleBfr1 == NULL ) {
        perror("Failed to allocate pcm buffer");
        cleanup_and_exit(EXIT_FAILURE);
    }

    if ((rtn = snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_CAPTURE)) < 0)
    {
        fprintf (stderr, "snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rtn));
        cleanup_and_exit(EXIT_FAILURE);
    }

    FD_ZERO (&rfds);
    FD_ZERO(&ofds);
    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);
    while (!end && N < mSamples)
    {
        /* If we are the foreground process group associated with STDIN then include
         * STDIN in the fdset to handle key presses.
         */
        if (stdin_raw)
            FD_SET (STDIN_FILENO, &rfds);
        if (mixer_handle)
        {
            /* Include the mixer_handle descriptor in the fdset to handle
             * mixer events.
             */
            mixer_fd = snd_mixer_file_descriptor (mixer_handle);
            FD_SET (mixer_fd, &rfds);
        }
        rtn = pcm_fd = snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_CAPTURE);
        FD_SET (pcm_fd, &rfds);
        FD_SET (pcm_fd, &ofds);

        if (mixer_handle)
            rtn = max (mixer_fd, pcm_fd);

        if (select (rtn + 1, &rfds, NULL, &ofds, NULL) == -1)
        {
            perror("select");
            break; /* break loop to exit cleanly */
        }

        if (FD_ISSET (STDIN_FILENO, &rfds))
        {
            c = getc (stdin);
            if (c != EOF)
            {
                /* Only handle volume key presses if there is a mixer group */
                if (group.gid.name[0] != 0)
                {
                    int update_volume = 1;
                    if ((rtn = snd_mixer_group_read (mixer_handle, &group)) < 0)
                        fprintf (stderr, "snd_mixer_group_read failed: %s\n", snd_strerror (rtn));
                    switch (c)
                    {
                    case 'q':
                        group.volume.names.front_left += 1;
                        break;
                    case 'a':
                        group.volume.names.front_left -= 1;
                        break;
                    case 'w':
                        group.volume.names.front_left += 1;
                        group.volume.names.front_right += 1;
                        break;
                    case 's':
                        group.volume.names.front_left -= 1;
                        group.volume.names.front_right -= 1;
                        break;
                    case 'e':
                        group.volume.names.front_right += 1;
                        break;
                    case 'd':
                        group.volume.names.front_right -= 1;
                        break;
                    default:
                        update_volume = 0;
                        break;
                    }

                    if (update_volume)
                    {
                        if (group.volume.names.front_left > group.max)
                            group.volume.names.front_left = group.max;
                        if (group.volume.names.front_left < group.min)
                            group.volume.names.front_left = group.min;
                        if (group.volume.names.front_right > group.max)
                            group.volume.names.front_right = group.max;
                        if (group.volume.names.front_right < group.min)
                            group.volume.names.front_right = group.min;
                        if ((rtn = snd_mixer_group_write (mixer_handle, &group)) < 0)
                            fprintf (stderr, "snd_mixer_group_write failed: %s\n", snd_strerror (rtn));

                        if (group.max==group.min)
                            printf ("Volume Now at %d:%d\n", group.max, group.max);
                        else
                            printf ("Volume Now at %d:%d \n",
                                    100 * (group.volume.names.front_left - group.min) / (group.max - group.min),
                                    100 * (group.volume.names.front_right - group.min) / (group.max - group.min));
                    }
                }

                switch (c)
                {
                case 'o':
                    printf("Forcing overrun...\n");
                    sleep(5);
                    break;
                case 'x':
                    /* waverec will read all data as soon as it is available, so to test drain functionality
                     * add a delay to allow io-audio to accumulate some "extra" data in it's buffers to
                     * verify that we are able to read until the buffer is empty after the drain call
                     */
                    printf("Delaying %.3f ms for drain...\n", (double)period_us/1000);
                    usleep(period_us);
                    /* Fallthrough */
                case 'f':
                    if( (ret = snd_pcm_plugin_drain( pcm_handle, SND_PCM_CHANNEL_CAPTURE )) == 0 ) {
                        snd_pcm_channel_status_t status;

                        printf("Draining...\n");
                        memset (&status, 0, sizeof (status));
                        status.channel = SND_PCM_CHANNEL_CAPTURE;
                        if (snd_pcm_plugin_status (pcm_handle, &status) < 0)
                        {
                            fprintf (stderr, "Capture channel status error\n");
                            cleanup_and_exit(EXIT_FAILURE);
                        }
                        bytes_to_drain = status.count;
                        printf("%d frags (%d bytes) to drain\n", status.count/bsize, bytes_to_drain);
                    } else {
                        fprintf(stderr, "Drain failed: %d\n", ret);
                    }
                    break;
                case 'g':
                    if( (ret = snd_pcm_plugin_prepare( pcm_handle, SND_PCM_CHANNEL_CAPTURE )) == 0 ) {
                        printf("Preparing\n");
                    } else {
                        fprintf(stderr, "Preparing failed: %d\n", ret);
                    }
                    break;
                case 'p':
                    if( (ret = snd_pcm_capture_pause( pcm_handle )) == 0 ) {
                        printf("Pausing\n");
                    } else {
                        fprintf(stderr, "Pause failed: %d\n", ret);
                    }
                    break;
                case 'r':
                    if( (ret = snd_pcm_capture_resume( pcm_handle )) == 0 ) {
                        printf("Resuming\n");
                    } else {
                        fprintf(stderr, "Resume failed: %d\n", ret);
                    }
                    break;
                case 3: //Ctrl-C
                case 27: // Escape
                    end = 1;
                    break;
                case 'z':
                    printf("delaying 500ms\n");
                    delay(500);
                    break;
                default:
                    break;
                }
            }
            else {
                cleanup_and_exit(EXIT_SUCCESS);
            }
        }

        if (mixer_handle && FD_ISSET (mixer_fd, &rfds))
        {
            snd_mixer_callbacks_t callbacks = {
                0, 0, 0, 0
            };

            snd_mixer_read (mixer_handle, &callbacks);
        }

        if (FD_ISSET (pcm_fd, &rfds))
        {
            snd_pcm_channel_status_t status;
            int     read = 0;

            read = snd_pcm_plugin_read (pcm_handle, mSampleBfr1, bsize);
            if (verbose)
                printf ("bytes read = %d, bsize = %d \n", read, bsize);
            if (read < bsize)
            {
                memset (&status, 0, sizeof (status));
                status.channel = SND_PCM_CHANNEL_CAPTURE;
                if (snd_pcm_plugin_status (pcm_handle, &status) < 0)
                {
                    fprintf (stderr, "Capture channel status error\n");
                    cleanup_and_exit(EXIT_FAILURE);
                }
                if (status.status == SND_PCM_STATUS_CHANGE ||
                    status.status == SND_PCM_STATUS_OVERRUN) {
                    if (status.status == SND_PCM_STATUS_CHANGE) {
                        fprintf(stderr, "change: capture channel capability change\n");
                        if (snd_pcm_plugin_params (pcm_handle, &pp) < 0)
                        {
                            fprintf (stderr, "Capture channel snd_pcm_plugin_params error\n");
                            cleanup_and_exit(EXIT_FAILURE);
                        }
                    }
                    if (status.status == SND_PCM_STATUS_OVERRUN) {
                        fprintf(stderr, "overrun: capture channel\n");
                    }
                    if (snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_CAPTURE) < 0)
                    {
                        fprintf (stderr, "Capture channel prepare error\n");
                        cleanup_and_exit(EXIT_FAILURE);
                    }
                }
                else if (status.status == SND_PCM_STATUS_ERROR)
                {
                    fprintf(stderr, "error: capture channel failure\n");
                    cleanup_and_exit(EXIT_FAILURE);
                }
                else if (status.status == SND_PCM_STATUS_PREEMPTED)
                {
                    fprintf(stderr, "error: capture channel preempted\n");
                    cleanup_and_exit(EXIT_FAILURE);
                }
                else if (status.status == SND_PCM_STATUS_READY)
                {
                    /* Capture has ended due to drain request */
                    end = 1;
                    break;
                }
            } else {
                fwrite (mSampleBfr1, 1, read, file1);
                N += read;
                if (bytes_to_drain)
                {
                    bytes_to_drain -= read;
                    printf("Bytes remaining %d\n", bytes_to_drain);
                }
            }
        }
        if (FD_ISSET (pcm_fd, &ofds))
        {
            snd_pcm_event_t event;
            if ((rtn = snd_pcm_channel_read_event (pcm_handle, SND_PCM_CHANNEL_CAPTURE, &event)) == EOK)
            {
                switch (event.type)
                {
                    case SND_PCM_EVENT_OVERRUN:
                        printf("Overrun event received\n");
                        break;
                    default:
                        printf("Unknown PCM event type for capture - %d\n", event.type);
                        break;
                }
            }
            else
                printf("snd_pcm_channel_read_event() failed with %d\n", rtn);
        }
    }

    printf("Exiting...\n");

    /* Update wave header with actual length of audio captured */
    riff_hdr.wave.data.data_len = ENDIAN_LE32 (N);
    riff_hdr.wave_len = ENDIAN_LE32 (N + sizeof (riff_hdr) - 8);
    fseek(file1, 0, SEEK_SET);
    fwrite (&riff_hdr, 1, sizeof (riff_hdr), file1);

    snd_pcm_plugin_drop (pcm_handle, SND_PCM_CHANNEL_CAPTURE);
    cleanup();
    return(EXIT_SUCCESS);
}

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