Appendix: waverec.c example

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

#include <errno.h>
#include <fcntl.h>
#include <gulliver.h>
#include <stdio.h>
#include <stdlib.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 <sys/asoundlib.h>
/* *INDENT-OFF* */
struct
{
    char    riff_id[4];
    char    wave_len[4];
    struct
    {
        char    fmt_id[8];
        char    fmt_len[4];
        struct
        {
            char    format_tag[2];
            char    voices[2];
            char    rate[4];
            char    char_per_sec[4];
            char    block_align[2];
            char    bits_per_sample[2];
        }
        fmt;
        struct
        {
            char    data_id[4];
            char    data_len[4];
        }
        data;
    }
    wave;
}
riff_hdr =
{
    {'R', 'I', 'F', 'F' },
    {sizeof (riff_hdr.wave), 0, 0, 0 },
    {
        {'W', 'A', 'V', 'E', 'f', 'm', 't', ' ' },
        {sizeof (riff_hdr.wave.fmt), 0, 0, 0 },
        {
            {1, 0 }, 
            {0, 0 },
            {0, 0, 0, 0 },
            {0, 0, 0, 0 },
            {0, 0 },
            {0, 0 }
        },
        {
            {'d', 'a', 't', 'a' },
            {0, 0, 0, 0 }
        }
    }
};
/* *INDENT-ON* */


int
err (char *msg)
{
    perror (msg);
    return -1;
}


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 &= ~(ECHO | ICANON | ISIG | ECHOE | ECHOK | ECHONL);
    termios_p.c_oflag &= ~(OPOST);
    return (tcsetattr (fd, TCSANOW, &termios_p));
}

int
dev_unraw (int fd)
{
    struct termios termios_p;

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

    termios_p.c_lflag |= (ECHO | ICANON | ISIG | ECHOE | ECHOK | ECHONL);
    termios_p.c_oflag |= (OPOST);
    return (tcsetattr (fd, TCSAFLUSH, &termios_p));
}


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

Options:
    -8                use 8 bit mode (16 bit default)
    -a[card#:]<dev#>  the card & device number to record from
    -m                record in mono (stereo default)
    -r <rate>         record at rate (44100 default | 48000 44100 22050 11025)
    -t <sec>          seconds to record (5 seconds default)
#endif
/* *INDENT-ON* */
//*****************************************************************************


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

    snd_pcm_t *pcm_handle;
    FILE   *file1;
    int     mSamples;
    int     mSampleRate;
    int     mSampleChannels;
    int     mSampleBits;
    int     mSampleTime;
    char   *mSampleBfr1;

    int     rtn;

    snd_pcm_channel_info_t pi;
    snd_mixer_t *mixer_handle;
    snd_mixer_group_t group;
    snd_pcm_channel_params_t pp;
    snd_pcm_channel_setup_t setup;
    int     bsize, n, N = 0, c;

    fd_set  rfds;

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

    while ((c = getopt (argc, argv, "8a:mr:t:")) != EOF)
    {
        switch (c)
        {
        case '8':
            mSampleBits = 8;
            break;
        case 'a':
            if (strchr (optarg, ':'))
            {
                card = atoi (optarg);
                dev = atoi (strchr (optarg, ':') + 1);
            }
            else
                dev = atoi (optarg);
            printf ("Using card %d device %d \n", card, dev);
            break;
        case 'm':
            mSampleChannels = 1;
            break;
        case 'r':
            mSampleRate = atoi (optarg);
            break;
        case 't':
            mSampleTime = atoi (optarg);
            break;
        default:
            return 1;
        }
    }

    setvbuf (stdin, NULL, _IONBF, 0);
    if (card == -1)
    {
        if ((rtn = snd_pcm_open_preferred (&pcm_handle, &card, &dev, SND_PCM_OPEN_CAPTURE)) < 0)
            return err ("device open");
    }
    else
    {
        if ((rtn = snd_pcm_open (&pcm_handle, card, dev, SND_PCM_OPEN_CAPTURE)) < 0)
            return err ("device open");
    }

    if (argc < 2)
        return err ("no file specified");

    if ((file1 = fopen (argv[optind], "w")) == 0)
        return err ("file open #1");

    mSamples = mSampleRate * mSampleChannels * mSampleBits / 8 * mSampleTime;

    *(short *) riff_hdr.wave.fmt.voices = ENDIAN_LE16 (mSampleChannels);
    *(long *) riff_hdr.wave.fmt.rate = ENDIAN_LE32 (mSampleRate);
    *(long *) riff_hdr.wave.fmt.char_per_sec =
        ENDIAN_LE32 (mSampleRate * mSampleChannels * mSampleBits / 8);
    *(short *) riff_hdr.wave.fmt.block_align = ENDIAN_LE16 (mSampleChannels * mSampleBits / 8);
    *(short *) riff_hdr.wave.fmt.bits_per_sample = ENDIAN_LE16 (mSampleBits);
    *(long *) riff_hdr.wave.data.data_len = ENDIAN_LE32 (mSamples);
    *(long *) 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\n", mSampleRate, mSampleChannels,
        mSampleBits);

    /* disabling mmap is not actually required in this example but it is included to 
     * demonstrate how it is used when it is required.
     */
    if ((rtn = snd_pcm_plugin_set_disable (pcm_handle, PLUGIN_DISABLE_MMAP)) < 0)
    {
        fprintf (stderr, "snd_pcm_plugin_set_disable failed: %s\n", snd_strerror (rtn));
        return -1;
    }

    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));
        return -1;
    }

    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.buf.block.frag_size = pi.max_fragment_size;
    pp.buf.block.frags_max = -1;
    pp.buf.block.frags_min = 1;

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

    if (mSampleBits == 8)
        pp.format.format = SND_PCM_SFMT_U8;
    else
        pp.format.format = SND_PCM_SFMT_S16_LE;

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

    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));


    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));
        return -1;
    }
    printf ("Format %s \n", snd_pcm_get_format_name (setup.format.format));
    printf ("Frag Size %d \n", setup.buf.block.frag_size);
    printf ("Rate %d \n", setup.format.rate);
    bsize = setup.buf.block.frag_size;

    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, card, setup.mixer_device)) < 0)
    {
        fprintf (stderr, "snd_mixer_open failed: %s\n", snd_strerror (rtn));
        return -1;
    }

    mSampleBfr1 = malloc (bsize);
    FD_ZERO (&rfds);
    n = 1;
    while (N < mSamples && n > 0)
    {
        FD_SET (STDIN_FILENO, &rfds);
        FD_SET (snd_mixer_file_descriptor (mixer_handle), &rfds);
        FD_SET (snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_CAPTURE), &rfds);

        rtn = max (snd_mixer_file_descriptor (mixer_handle),
            snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_CAPTURE));

        if (select (rtn + 1, &rfds, NULL, NULL, NULL) == -1)
            return err ("select");


        if (FD_ISSET (STDIN_FILENO, &rfds))
        {
            dev_raw (fileno (stdin));
            c = getc (stdin);
            dev_unraw (fileno (stdin));
            if (c != EOF)
            {
                if (group.gid.name[0] != 0)
                {
                    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;
                    }
                    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));

                    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));
                }
            }
            else
                exit (0);
        }

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

            snd_mixer_read (mixer_handle, &callbacks);
        }

        if (FD_ISSET (snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_CAPTURE), &rfds))
        {
            snd_pcm_channel_status_t status;
            int     read = 0;

            read = snd_pcm_plugin_read (pcm_handle, mSampleBfr1, bsize);
            if (read < n)
            {
                memset (&status, 0, sizeof (status));
                status.channel = SND_PCM_CHANNEL_CAPTURE;
                if (snd_pcm_plugin_status (pcm_handle, &status) < 0)
                {
                    fprintf (stderr, "overrun: capture channel status error\n");
                    exit (1);
                }

                if (status.status == SND_PCM_STATUS_READY ||
                    status.status == SND_PCM_STATUS_OVERRUN)
                {
                    if (snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_CAPTURE) < 0)
                    {
                        fprintf (stderr, "overrun: capture channel prepare error\n");
                        exit (1);
                    }
                }
            }
            fwrite (mSampleBfr1, 1, read, file1);
            N += read;
        }
    }

    n = snd_pcm_plugin_flush (pcm_handle, SND_PCM_CHANNEL_CAPTURE);

    rtn = snd_mixer_close (mixer_handle);
    rtn = snd_pcm_close (pcm_handle);
    fclose (file1);
    return (0);
}