QNX系统中播放wav文件

QNX 中播放音频,用的是libasound 库。
官方提供的 wave.c 的代码,用于播放一个wav格式的音频文件。
但在QNX 6.6 虚拟机里一直播放不了,原因是虚拟机没有/dev/snd/ 而且该目录下也没什么设备。继续看官方的帮助文档,是要运行/sbin/io-audio加载声卡驱动才能播放。

The io-audio command can load the following shared objects:

deva-ctrl-4dwave.so
Sound driver for the Trident 4DWave!.
deva-ctrl-audiopci.so
Sound driver for the AudioPCI chip family.
deva-ctrl-cs4281.so
Sound driver for the CS4281.
deva-ctrl-cs46xx.so
Sound driver for the CS46xx family of chips.
deva-ctrl-cyberpro5.so
Sound driver for the CyberPro5XXX.
deva-ctrl-ess1938.so
Sound driver for the ESS1938.
deva-ctrl-geode.so
Sound driver for the National Semiconductor Geode family of chips.
deva-ctrl-i8x0.so
Sound driver for the Intel 8X0.
deva-ctrl-nmg6.so
Sound driver for the Neomagic 6 family of chips.
deva-ctrl-sb.so
Sound driver for Sound Blaster 16 and compatible soundcards.
deva-ctrl-via686.so
Sound driver for the VIA686.
deva-ctrl-vortex.so
Sound driver for the Vortex.
deva-ctrl-ymfds1.so
Sound driver for the Yamaha DS1.
deva-mixer-ac97.so
Mixer DLL for the AC97 codec.
deva-ak4531.so
Mixer DLL for the AK4531 codec.
deva-util-restore.so
Shared object used to restore an audio driver’s state.

在虚拟机内执行:

/sbin/io-audio -d audiopci

加载驱动后,出现了/dev/snd/ 目录,而且目录下有文件

# /sbin/io-audio -d audiopci    
# ls /dev/snd/
controlC0        mixerC0D0        pcmC0D0c         pcmC0D0p         pcmC0D1p         pcmPreferredc    pcmPreferredp

这样就可以用官网那个例子进行播放了,播放命令如下:

# ./playWave -a 0:0 Wave1.wav       
Using card 0 device 0 
SampleRate = 11025, Channels = 2, SampleBits = 16
Format Signed 16-bit Little Endian 
Frag Size 131072 
Total Frags 2 
Rate 11025 
Voices 2 
Mixer Pcm Group [PCM]

将wave.c 中调音量等复杂功能裁剪掉,写成一个只播放的demo:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#define max(a,b) (a>b)?a:b
#define min(a,b) (a

const char *kRiffId = "RIFF";
const char *kWaveId = "WAVE";

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;

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 this is our tag, set the length and break.
        if (strncmp (tag, tagBfr.tag, sizeof tagBfr.tag) == 0)
        {
            retVal = ENDIAN_LE32 (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)) ||
        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 &= ~(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));
}

//*****************************************************************************
// ./playWave Wave1.wav
//*****************************************************************************

int main (int argc, char **argv)
{

    snd_pcm_t *pcm_handle;
    FILE   *file1;
    WaveHdr wavHdr1;
    int     mSamples;
    int     mSampleRate;
    int     mSampleChannels;
    int     mSampleBits;
    char   *mSampleBfr1;
    int     verbose = 0;

    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;
    fd_set  rfds, wfds;
    int     num_frags = -1;

    char    name[_POSIX_PATH_MAX] ="pcmC0D0p";
    int     card = -1;
    int     dev = 0;
    //两种方式打开音频设备; 首选 打开设备 /dev/snd/pcmC0D0p , 设备名根据实际情况变化;
    if (name[0] != '\0')
        printf ("Using device /dev/snd/%s\n", name);
    else
        printf ("Using card %d device %d \n", card, dev);

    setvbuf (stdin, NULL, _IONBF, 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 ((char *)"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 ((char *)"device open");
        }
        else
        {
            if ((rtn = snd_pcm_open (&pcm_handle, card, dev, SND_PCM_OPEN_PLAYBACK)) < 0)
                return err ((char *)"device open");
        }
    }

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

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

    if (CheckHdr (file1) == -1)
        return err ((char *)"CheckHdr #1");

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

    mSampleRate = ENDIAN_LE32 (wavHdr1.SamplesPerSec);
    mSampleChannels = ENDIAN_LE16 (wavHdr1.Channels);
    mSampleBits = ENDIAN_LE16 (wavHdr1.BitsPerSample);

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

    pp.buf.block.frags_max = num_frags;
    pp.buf.block.frags_min = 1;

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

    if (ENDIAN_LE16 (wavHdr1.FormatTag) == 6)
        pp.format.format = SND_PCM_SFMT_A_LAW;
    else if (ENDIAN_LE16 (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 == 24)
        pp.format.format = SND_PCM_SFMT_S24;
    else
        pp.format.format = SND_PCM_SFMT_S16_LE;

    strcpy (pp.sw_mixer_subchn_name, "Wave playback channel");
    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_PLAYBACK)) < 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_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);
        exit (-1);
    }
    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;
    }

    mSamples = FindTag (file1, "data");

    mSampleBfr1 = (char *)malloc (bsize);
    FD_ZERO (&rfds);
    FD_ZERO (&wfds);
    n = 1;
    while (N < mSamples && n > 0)
    {
        if (tcgetpgrp (0) == getpid ())
            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_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)
            return err ((char *)"select");

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

            dev_raw (fileno (stdin));
        }

        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_PLAYBACK), &wfds))
        {
            snd_pcm_channel_status_t status;
            int     written = 0;

            if ((n = fread (mSampleBfr1, 1, min (mSamples - N, bsize), file1)) <= 0)
                continue;
            written = snd_pcm_plugin_write (pcm_handle, mSampleBfr1, n);
            if (verbose)
                printf ("bytes written = %d \n", written);
            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)
                {
                    if (snd_pcm_plugin_prepare (pcm_handle, SND_PCM_CHANNEL_PLAYBACK) < 0)
                    {
                        fprintf (stderr, "underrun: playback channel prepare error\n");
                        exit (1);
                    }
                }
                if (written < 0)
                    written = 0;
                written += snd_pcm_plugin_write (pcm_handle, mSampleBfr1 + written, n - written);
            }
            N += written;
        }
    }

    n = snd_pcm_plugin_flush (pcm_handle, SND_PCM_CHANNEL_PLAYBACK);

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

你可能感兴趣的:(QNX)