wave.c example

This is a sample application that plays back audio data.

/*
 * $QNXLicenseC:
 * Copyright 2007, 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 <sys/slogcodes.h>
#include <time.h>
#include <ctype.h>
#include <limits.h>
#include <signal.h>
#include <pthread.h>
#include <sys/pps.h>

#include <sys/asoundlib.h>

#include <audio/audio_manager_volume.h>
#include <audio/audio_manager_routing.h>
#include <audio/audio_manager_event.h>

const char *kRiffId = "RIFF";
const char *kRifxId = "RIFX";
const char *kWaveId = "WAVE";
bool running = true;
int n;
int N=0;
int verbose = 0;
int print_timing = 0;
int bsize;
snd_mixer_group_t group;
snd_mixer_t *mixer_handle = NULL;
snd_pcm_t *pcm_handle;
char   *mSampleBfr1;
unsigned int mSamples;
bool mBigEndian = false;

typedef struct
{
    char    tag[4];
    long    length;
}
RiffTag;

typedef struct
{
    char    Riff[4];
    long    Size;
    char    Wave[4];
}
RiffHdr;


typedef struct
{
    short   FormatTag;
    short   Channels;
    long    SamplesPerSec;
    long    AvgBytesPerSec;
    short   BlockAlign;
    short   BitsPerSample;
}
WaveHdr;

typedef struct
{
    FILE *file1;
    struct timespec start_time;
}
WriterData;

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


int
FindTag (FILE * fp, const char *tag)
{
    int     retVal;
    RiffTag tagBfr = { "", 0 };

    retVal = 0;

    // Keep reading until we find the tag or hit the EOF.
    while (fread ((unsigned char *) &tagBfr, sizeof (tagBfr), 1, fp))
    {

        if( mBigEndian ) {
            tagBfr.length = ENDIAN_BE32 (tagBfr.length);
        } else {
            tagBfr.length = ENDIAN_LE32 (tagBfr.length);
        }
        // If this is our tag, set the length and break.
        if (strncmp (tag, tagBfr.tag, sizeof tagBfr.tag) == 0)
        {
            retVal = tagBfr.length;
            break;
        }

        // Skip ahead the specified number of bytes in the stream
        fseek (fp, tagBfr.length, SEEK_CUR);
    }

    // Return the result of our operation
    return (retVal);
}


int
CheckHdr (FILE * fp)
{
    RiffHdr riffHdr = { "", 0 };

    // Read the header and, if successful, play the file
    // file or WAVE file.
    if (fread ((unsigned char *) &riffHdr, sizeof (RiffHdr), 1, fp) == 0)
        return 0;

    if (!strncmp (riffHdr.Riff, kRiffId, strlen (kRiffId)))
        mBigEndian = false;
    else if (!strncmp (riffHdr.Riff, kRifxId, strlen (kRifxId)))
        mBigEndian = true;
    else
        return -1;
    if (strncmp (riffHdr.Wave, kWaveId, strlen (kWaveId)))
        return -1;

    return 0;
}


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

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

void
handle_keypress()
{
    int     c;
    int     rtn;

    c = getc (stdin);

    if (c == EOF)
    {
        running = false;
        return;
    }

    /* Handle non-mixer keypresses */
    switch (c)
    {
        case 'p':
            snd_pcm_playback_pause( pcm_handle );
            break;
        case 'r':
            snd_pcm_playback_resume( pcm_handle );
            break;
                // Exit the program
        case 3: // Ctrl-C
        case 27: // Escape
            running = false;
            break;
        default:
            break;
    }

        /* Handle mixer keypresses */
    if (mixer_handle == NULL)
        return;

    if ((rtn = snd_mixer_group_read (mixer_handle, &group)) < 0)
        {
        fprintf (stderr, "snd_mixer_group_read failed: %s\n", snd_strerror (rtn));
                return;
        }

        switch (c)
        {
        case 'q':
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT)
                group.volume.names.front_left += 10;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_LEFT)
                group.volume.names.rear_left += 10;
            if (group.channels & SND_MIXER_CHN_MASK_WOOFER)
                group.volume.names.woofer += 10;
            break;
        case 'a':
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT)
                group.volume.names.front_left -= 10;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_LEFT)
                group.volume.names.rear_left -= 10;
            if (group.channels & SND_MIXER_CHN_MASK_WOOFER)
                group.volume.names.woofer -= 10;
            break;
        case 'w':
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT)
                group.volume.names.front_left += 10;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_LEFT)
                group.volume.names.rear_left += 10;
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER)
                group.volume.names.front_center += 10;
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT)
                group.volume.names.front_right += 10;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT)
                group.volume.names.rear_right += 10;
            if (group.channels & SND_MIXER_CHN_MASK_WOOFER)
                group.volume.names.woofer += 10;
            break;
        case 's':
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT)
                group.volume.names.front_left -= 10;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_LEFT)
                group.volume.names.rear_left -= 10;
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER)
                group.volume.names.front_center -= 10;
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT)
                group.volume.names.front_right -= 10;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT)
                group.volume.names.rear_right -= 10;
            if (group.channels & SND_MIXER_CHN_MASK_WOOFER)
                group.volume.names.woofer -= 10;
            break;
        case 'e':
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT)
                group.volume.names.front_right += 10;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT)
                group.volume.names.rear_right += 10;
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER)
                group.volume.names.front_center += 10;
            break;
        case 'd':
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT)
                group.volume.names.front_right -= 10;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT)
                group.volume.names.rear_right -= 10;
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER)
                group.volume.names.front_center -= 10;
            break;
    }

    if (group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT)
    {
        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.channels & SND_MIXER_CHN_MASK_REAR_LEFT)
    {
        if (group.volume.names.rear_left > group.max)
            group.volume.names.rear_left = group.max;
        if (group.volume.names.rear_left < group.min)
            group.volume.names.rear_left = group.min;
    }
    if (group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER)
    {
        if (group.volume.names.front_center > group.max)
            group.volume.names.front_center = group.max;
        if (group.volume.names.front_center < group.min)
            group.volume.names.front_center = group.min;
    }
    if (group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT)
    {
        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 (group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT)
    {
        if (group.volume.names.rear_right > group.max)
            group.volume.names.rear_right = group.max;
        if (group.volume.names.rear_right < group.min)
            group.volume.names.rear_right = group.min;
    }
    if (group.channels & SND_MIXER_CHN_MASK_WOOFER)
    {
        if (group.volume.names.woofer > group.max)
            group.volume.names.woofer = group.max;
        if (group.volume.names.woofer < group.min)
            group.volume.names.woofer = 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.channels & SND_MIXER_CHN_MASK_FRONT_LEFT)
    {
        printf ("Volume Now at %d:%d \n",
            (group.max - group.min) ? 100 * (group.volume.names.front_left - group.min) /
               (group.max - group.min) : 0,
            (group.max - group.min) ? 100 * (group.volume.names.front_right - group.min) /
               (group.max - group.min): 0);
    }
    else if (group.channels & SND_MIXER_CHN_MASK_REAR_LEFT)
    {
        printf ("Volume Now at %d:%d \n",
            (group.max - group.min) ? 100 * (group.volume.names.rear_left - group.min) /
               (group.max - group.min) : 0,
            (group.max - group.min) ? 100 * (group.volume.names.rear_right - group.min) /
               (group.max - group.min): 0);
    }
    else if (group.channels & SND_MIXER_CHN_MASK_WOOFER)
    {
        printf ("Volume Now at %d:%d \n",
            (group.max - group.min) ? 100 * (group.volume.names.woofer - group.min) /
               (group.max - group.min) : 0,
            (group.max - group.min) ? 100 * (group.volume.names.front_center - group.min) /
               (group.max - group.min): 0);
    }
    else
    {
        printf ("Volume Now at %d:%d \n",
            (group.max - group.min) ? 100 * (group.volume.names.front_left - group.min) /
               (group.max - group.min) : 0,
            (group.max - group.min) ? 100 * (group.volume.names.front_right - group.min) /
               (group.max - group.min): 0);
    }
}

void handle_mixer() 
{
    fd_set  rfds;
    FD_ZERO(&rfds);
    FD_SET (snd_mixer_file_descriptor (mixer_handle), &rfds);

    if (select (snd_mixer_file_descriptor (mixer_handle) + 1, &rfds, NULL, NULL, NULL) == -1)
        err ("select");

    snd_mixer_callbacks_t callbacks = { 0, 0, 0, 0 };

    snd_mixer_read (mixer_handle, &callbacks);
}

void write_audio_data(WriterData *wd)
{
    struct  timespec current_time;
    snd_pcm_channel_status_t status;
    int     written = 0;

    if ((n = fread (mSampleBfr1, 1, min (mSamples - N, bsize), wd->file1)) <= 0)
        return;
    written = snd_pcm_plugin_write (pcm_handle, mSampleBfr1, n);

    if (verbose)
        printf ("bytes written = %d \n", written);
    if( print_timing ) {
        clock_gettime( CLOCK_REALTIME, &current_time );
        printf ("Sent frag at %llu\n", (current_time.tv_sec - wd->start_time.tv_sec) *
                1000000000LL + (current_time.tv_nsec - wd->start_time.tv_nsec));
    }
    if (written < n)
    {
        memset (&status, 0, sizeof (status));
        status.channel = SND_PCM_CHANNEL_PLAYBACK;
        if (snd_pcm_plugin_status (pcm_handle, &status) < 0)
        {
            fprintf (stderr, "underrun: playback channel status error\n");
            exit (1);
        }

        if (status.status == SND_PCM_STATUS_READY ||
            status.status == SND_PCM_STATUS_UNDERRUN ||
            status.status == SND_PCM_STATUS_CHANGE)
        {
            if( status.status == SND_PCM_STATUS_UNDERRUN ) {
                printf ("Audio underrun occurred\n");
            } else if( status.status == SND_PCM_STATUS_CHANGE ) {
                printf ("Audio device change occurred from %s to %s\n",
                        audio_manager_get_device_name(status.status_data.change_data.old_device),
                        audio_manager_get_device_name(status.status_data.change_data.new_device));
            }
            if (snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
            {
                fprintf (stderr, "underrun: playback channel prepare error\n");
                exit (1);
            }
        }
        else if (status.status == SND_PCM_STATUS_UNSECURE)
        {
            fprintf (stderr, "Channel unsecure\n");
            if (snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
            {
                fprintf (stderr, "unsecure: playback channel prepare error\n");
                exit (1);
            }
        }
        else if (status.status == SND_PCM_STATUS_ERROR)
        {
            fprintf(stderr, "error: playback channel failure\n");
            exit(1);
        }
        else if (status.status == SND_PCM_STATUS_PREEMPTED)
        {
            fprintf(stderr, "error: playback channel preempted\n");
            exit(1);
        }

        if (written < 0)
            written = 0;
        written += snd_pcm_plugin_write (pcm_handle, mSampleBfr1 + written, n - written);
    }
    N += written;
}

void *writer_thread_handler(void *data)
{
    WriterData *wd = (WriterData *)data;
    sigset_t signals;
    
    sigfillset (&signals);
    pthread_sigmask (SIG_BLOCK, &signals, NULL);

    while (running && N < mSamples && n > 0)
    {
        write_audio_data(wd);
    }

    return NULL;
}

void *generic_thread_handler(void *data)
{
    sigset_t signals;

    sigfillset (&signals);
    pthread_sigmask (SIG_BLOCK, &signals, NULL);

    while(1) {
        ((void (*)(void))data)();
    }

    return NULL;
}


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

Options:
    -a[card#:]<dev#>   the card & device number to play out on
    -f<frag_size>      requested fragment size
    -v                 verbose
    -s                 content is protected
    -e                 content would like to be played on a secure channel
    -r                 content can only be played on a secure channel
    -t                 print timing information of when data is sent in ns
    -w                 use separate threads to control and write audio data
    -c<args>[,args ..] voice matrix configuration
    -n<num_frags>      requested number of fragments
    -b<num_frags>      requested number of fragments while buffering
    -p<volume in %>    volume in percent
    -m<mixer name>     string name for mixer input
    -o<audio type>     name of the audio type registers with audioman
    -x                 use mmap interface
    -R<value>          SRC rate method
                       (1 = 7-pt kaiser windowed, 2 = 20-pt remez, 3 = linear interpolation)

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* */
//*****************************************************************************

void sig_handler( int sig_no )
{
        running = false;
        return;
}

int
main (int argc, char **argv)
{
    int     card = -1;
    int     dev = 0;
    WriterData wd;
    WaveHdr wavHdr1;
    int     mSampleRate;
    int     mSampleChannels;
    int     mSampleBits;
    int     fragsize = -1;

    int     rtn;
    snd_pcm_channel_info_t pi;
    snd_pcm_channel_params_t pp;
    snd_pcm_channel_setup_t setup;
    int c;
    fd_set  rfds, wfds;
    uint32_t voice_mask[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
    snd_pcm_voice_conversion_t voice_conversion;
    int     voice_override = 0;
    int     num_frags = -1;
    int     num_buffered_frags = 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 };
    float   vol_percent = -1;
    float   volume;
    char    mixer_name[32];
    int     mix_name_enable = -1;
    int     protected_content = 0;
    int     enable_protection = 0;
    int     require_protection = 0;
    int     use_writer_thread = 0;
    int     uses_audioman_handle = 1;
    unsigned int audioman_handle;
    void   *retval;
    pthread_t writer_thread;
    pthread_t mixer_thread;
    pthread_t keypress_thread;
    char   *type = "multimedia";
    int    rate_method = 0;
    int    use_mmap = 0;

    while ((c = getopt (argc, argv, "a:ef:vc:n:b:p:m:qrstwo:xR:")) != EOF)
    {
        switch (c)
        {
        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 %s\n", name);
            else
                printf ("Using card %d device %d \n", card, dev);
            break;
        case 'f':
            fragsize = atoi (optarg);
            break;
        case 'v':
            verbose = 1;
            break;
        case 'c':
            sub_opts = sub_opts_copy = strdup (optarg);
            if (sub_opts == NULL) {
                printf("Cannot allocate sub_opts\n");
                exit(1);
            }
            while (*sub_opts != '\0')
            {
                int channel = getsubopt (&sub_opts, dev_opts, &value);
                if( channel >= 0 && channel < sizeof(voice_mask)/sizeof(voice_mask[0]) && value ) {
                    voice_mask[channel] = strtoul (value, NULL, 0);
                } else {
                    fprintf (stderr, "Invalid channel map specified\n");
                    exit(1);
                }
            }
            free(sub_opts_copy);
            voice_override = 1;
            break;
        case 'n':
            num_frags = atoi (optarg) - 1;
            break;
        case 'b':
            num_buffered_frags = atoi (optarg);
            break;
        case 'p':
            vol_percent = atof (optarg);
            break;
        case 'm':
            strncpy (mixer_name, optarg, 32);
            mix_name_enable = 1;
            break;
        case 's':
            protected_content = 1;
            break;
        case 'e':
            enable_protection = 1;
            break;
        case 'r':
            require_protection = 1;
            break;
        case 't':
            print_timing = 1;
            break;
        case 'w':
            use_writer_thread = 1;
            break;
        case 'o':
            type = optarg;
            break;
        case 'x':
            use_mmap = 1;
            break;
        case 'R':
            rate_method = atoi(optarg);
            if (rate_method < 0 || rate_method > 3)
            {
                rate_method = 0;
                printf("Invalid rate method, using method 0\n");
            }
            break;
        default:
            return 1;
        }
    }

    setvbuf (stdin, NULL, _IONBF, 0);

    if ( audio_manager_get_handle (audio_manager_get_type_from_name(type), 0, true, &audioman_handle) ) {
        uses_audioman_handle = 0;
    }
    if (name[0] != '\0')
    {
        snd_pcm_info_t info;

        if ((rtn = snd_pcm_open_name (&pcm_handle, name, SND_PCM_OPEN_PLAYBACK)) < 0)
        {
            return err ("open_name");
        }
        rtn = snd_pcm_info (pcm_handle, &info);
        card = info.card;
    }
    else
    {
        if (card == -1)
        {
            if ((rtn =
                    snd_pcm_open_preferred (&pcm_handle, &card, &dev, SND_PCM_OPEN_PLAYBACK)) < 0)
                return err ("device open");
        }
        else
        {
            if ((rtn = snd_pcm_open (&pcm_handle, card, dev, SND_PCM_OPEN_PLAYBACK)) < 0)
                return err ("device open");
        }
    }

    if( uses_audioman_handle ) {
        if ((rtn = snd_pcm_set_audioman_handle (pcm_handle, audioman_handle)) < 0)
            return err ("set audioman handle");
    }

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

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

    if (CheckHdr (wd.file1) == -1)
        return err ("CheckHdr #1");

    mSamples = FindTag (wd.file1, "fmt ");
    fread (&wavHdr1, sizeof (wavHdr1), 1, wd.file1);
    fseek (wd.file1, (mSamples - sizeof (WaveHdr)), SEEK_CUR);

    if( mBigEndian ) {
        mSampleRate = ENDIAN_BE32 (wavHdr1.SamplesPerSec);
        mSampleChannels = ENDIAN_BE16 (wavHdr1.Channels);
        mSampleBits = ENDIAN_BE16 (wavHdr1.BitsPerSample);
        wavHdr1.FormatTag = ENDIAN_BE16 (wavHdr1.FormatTag);
    } else {
        mSampleRate = ENDIAN_LE32 (wavHdr1.SamplesPerSec);
        mSampleChannels = ENDIAN_LE16 (wavHdr1.Channels);
        mSampleBits = ENDIAN_LE16 (wavHdr1.BitsPerSample);
        wavHdr1.FormatTag = ENDIAN_LE16 (wavHdr1.FormatTag);
    }

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

    if (!use_mmap)
    {
        /* disabling mmap is not actually required in this example but it is included to
         * demonstrate how it is used when it is required.
         */
        snd_pcm_plugin_set_disable (pcm_handle, PLUGIN_DISABLE_MMAP);
    }

    snd_pcm_plugin_set_enable (pcm_handle, PLUGIN_ROUTING);

    memset (&pi, 0, sizeof (pi));
    pi.channel = SND_PCM_CHANNEL_PLAYBACK;
    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
            | (protected_content ? SND_PCM_MODE_FLAG_PROTECTED_CONTENT : 0)
            | (enable_protection ? SND_PCM_MODE_FLAG_ENABLE_PROTECTION : 0)
            | (require_protection ? SND_PCM_MODE_FLAG_REQUIRE_PROTECTION : 0);

    pp.channel = SND_PCM_CHANNEL_PLAYBACK;
    pp.start_mode = SND_PCM_START_FULL;
    pp.stop_mode = SND_PCM_STOP_STOP;

    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_buffered_max = num_buffered_frags;
    pp.buf.block.frags_min = 1;

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

    if (wavHdr1.FormatTag == 6) {
        pp.format.format = SND_PCM_SFMT_A_LAW;
    } else if (wavHdr1.FormatTag == 7) {
        pp.format.format = SND_PCM_SFMT_MU_LAW;
    } else if (mSampleBits == 8) {
        pp.format.format = SND_PCM_SFMT_U8;
    } else if (mSampleBits == 16) {
        if (mBigEndian) {
            pp.format.format = SND_PCM_SFMT_S16_BE;
        } else {
            pp.format.format = SND_PCM_SFMT_S16_LE;
        }
    } else if (mSampleBits == 24) {
        if (mBigEndian) {
            pp.format.format = SND_PCM_SFMT_S24_BE;
        } else {
            pp.format.format = SND_PCM_SFMT_S24_LE;
        }
    } else if (mSampleBits == 32) {
        if (mBigEndian) {
            pp.format.format = SND_PCM_SFMT_S32_BE;
        } else {
            pp.format.format = SND_PCM_SFMT_S32_LE;
        }
    } else {
        fprintf(stderr, "Unsupported number of bits per sample %d", mSampleBits);
        return -1;
    }

    if (mix_name_enable == 1)
    {
        strncpy (pp.sw_mixer_subchn_name, mixer_name, 32);
    }
    else
    {
        strcpy (pp.sw_mixer_subchn_name, "Wave playback channel");
    }
        
    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, why_failed = %d\n", snd_strerror (rtn),
                 pp.why_failed);
        return -1;
    }

    if ((rtn = snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_PLAYBACK)) < 0)
        fprintf (stderr, "snd_pcm_plugin_prepare failed: %s\n", snd_strerror (rtn));

    if (voice_override)
    {
        snd_pcm_plugin_get_voice_conversion (pcm_handle, SND_PCM_CHANNEL_PLAYBACK,
            &voice_conversion);
        voice_conversion.matrix[0] = voice_mask[0];
        voice_conversion.matrix[1] = voice_mask[1];
        voice_conversion.matrix[2] = voice_mask[2];
        voice_conversion.matrix[3] = voice_mask[3];
        voice_conversion.matrix[4] = voice_mask[4];
        voice_conversion.matrix[5] = voice_mask[5];
        voice_conversion.matrix[6] = voice_mask[6];
        voice_conversion.matrix[7] = voice_mask[7];
        snd_pcm_plugin_set_voice_conversion (pcm_handle, SND_PCM_CHANNEL_PLAYBACK,
            &voice_conversion);
    }

    memset (&setup, 0, sizeof (setup));
    memset (&group, 0, sizeof (group));
    setup.channel = SND_PCM_CHANNEL_PLAYBACK;
    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 ("Total Frags %d \n", setup.buf.block.frags);
    printf ("Rate %d \n", setup.format.rate);
    printf ("Voices %d \n", setup.format.voices);
    bsize = setup.buf.block.frag_size;

    if (group.gid.name[0] == 0)
    {
        printf ("Mixer Pcm Group [%s] Not Set \n", group.gid.name);
    }
    else
    {
        printf ("Mixer Pcm Group [%s]\n", group.gid.name);
        if ((rtn = snd_mixer_open_pcm (&mixer_handle, pcm_handle)) < 0)
        {
            fprintf (stderr, "snd_mixer_open failed: %s\n", snd_strerror (rtn));
            return -1;
        }
    }
    if (tcgetpgrp (0) == getpid ())
        dev_raw (fileno (stdin));
    mSamples = FindTag (wd.file1, "data");

    if( print_timing ) {
        clock_gettime( CLOCK_REALTIME, &wd.start_time );
    }

    mSampleBfr1 = malloc (bsize);
    FD_ZERO (&rfds);
    FD_ZERO (&wfds);
    n = 1;

    if (mixer_handle)
    {
        if (vol_percent >=0)
        {
            if ((rtn = snd_mixer_group_read (mixer_handle, &group)) < 0)
                fprintf (stderr, "snd_mixer_group_read failed: %s\n", snd_strerror (rtn));

            volume = (float)(group.max - group.min) * ( vol_percent / 100);

            if (group.channels & SND_MIXER_CHN_MASK_FRONT_LEFT)
                group.volume.names.front_left = (int)volume;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_LEFT)
                group.volume.names.rear_left = (int)volume;
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_CENTER)
                group.volume.names.front_center = (int)volume;
            if (group.channels & SND_MIXER_CHN_MASK_FRONT_RIGHT)
                group.volume.names.front_right = (int)volume;
            if (group.channels & SND_MIXER_CHN_MASK_REAR_RIGHT)
                group.volume.names.rear_right = (int)volume;
            if (group.channels & SND_MIXER_CHN_MASK_WOOFER)
                group.volume.names.woofer = (int)volume;

            if ((rtn = snd_mixer_group_write (mixer_handle, &group)) < 0)
                fprintf (stderr, "snd_mixer_group_write failed: %s\n", snd_strerror (rtn));

            vol_percent = -1;
        }
    }

    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);

    if( use_writer_thread ) {
        pthread_create( &writer_thread, NULL, writer_thread_handler, &wd );
        pthread_create( &keypress_thread, NULL, generic_thread_handler, handle_keypress );
        if (mixer_handle)
            pthread_create( &mixer_thread, NULL, generic_thread_handler, handle_mixer );
        // First wait for feeder to complete. Any other thread will cause it to stop.
        // Then just kill the other threads
        pthread_join(writer_thread, &retval);
        pthread_cancel(keypress_thread);
        if (mixer_handle)
            pthread_cancel(mixer_thread);
    } else {
        while (running && N < mSamples && n > 0)
        {
            FD_ZERO(&rfds);
            FD_ZERO(&wfds);
            if (tcgetpgrp (0) == getpid ())
                FD_SET (STDIN_FILENO, &rfds);
            if (mixer_handle) {
                FD_SET (snd_mixer_file_descriptor (mixer_handle), &rfds);
            }
            FD_SET (snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_PLAYBACK), &wfds);

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

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

            if (FD_ISSET (STDIN_FILENO, &rfds))
            {
                handle_keypress();
            }

            if (FD_ISSET (snd_mixer_file_descriptor (mixer_handle), &rfds))
            {
                handle_mixer();
            }

            if (FD_ISSET (snd_pcm_file_descriptor (pcm_handle, SND_PCM_CHANNEL_PLAYBACK), &wfds))
            {
                write_audio_data(&wd);
            }
        }
    }

    if (tcgetpgrp (0) == getpid ())
        dev_unraw (fileno (stdin));
    printf("Exiting...\n");
    if (running) {
        snd_pcm_plugin_flush (pcm_handle, SND_PCM_CHANNEL_PLAYBACK);
    }
    if (mixer_handle)
        snd_mixer_close (mixer_handle);
    snd_pcm_close (pcm_handle);

    if (uses_audioman_handle) {
        audio_manager_free_handle (audioman_handle);
    }
    fclose(wd.file1);

    return (0);
}