linux下lame&alsa进行音频流操作(七)用alsa播放wav文件

1.用alsa播放wav文件的过程

a. 打开snd_pcm_open
b. 为param分配空间,并初始化参数
b.1 这儿为了确认参数需要读取wav文件的头,并根据wav文件来确认参数
需要确认的参数有三个: format(即SND_PCM_FORMAT_S16_LE类似) channels rate
c. snd_pcm_hw_params将参数设到driver中去
d. 循环读取wav文件中的数据,并把数据snd_pcm_writei写到驱动中进行播放

2.代码

#include "utils.h"
//#define ALSA_PCM_NEW_HW_PARAMS_API
#include 
#include 
//需要从wav文件中读取的三个参数
typedef struct {
    snd_pcm_format_t format;
    unsigned int channels;
    unsigned int rate;
}HWParams;
//下面这四个结构体是为了分析wav头的
typedef struct {
    u_int magic;   /* 'RIFF' */
    u_int length;  /* filelen */
    u_int type;    /* 'WAVE' */
} WaveHeader;

typedef struct {
    u_short format;       /* see WAV_FMT_* */
    u_short channels;
    u_int sample_fq;     /* frequence of sample */
    u_int byte_p_sec;
    u_short byte_p_spl;  /* samplesize; 1 or 2 bytes */
    u_short bit_p_spl;   /* 8, 12 or 16 bit */
} WaveFmtBody;

typedef struct {
    WaveFmtBody format;
    u_short ext_size;
    u_short bit_p_spl;
    u_int channel_mask;
    u_short guid_format; /* WAV_FMT_* */
    u_char guid_tag[14]; /* WAV_GUID_TAG */
} WaveFmtExtensibleBody;

typedef struct {
    u_int type; /* 'data' */
    u_int length; /* samplecount */
} WaveChunkHeader;

#define COMPOSE_ID(a,b,c,d) ((a) | ((b)<<8) | ((c)<<16) | ((d)<<24))
#define WAV_RIFF COMPOSE_ID('R','I','F','F')
#define WAV_WAVE COMPOSE_ID('W','A','V','E')
#define WAV_FMT COMPOSE_ID('f','m','t',' ')
#define WAV_DATA COMPOSE_ID('d','a','t','a')
//分析wav的头,并解析出alsa的param需要的三个参数
int check_wavfile(int fd, HWParams* hw_params)
{
    int ret;
    int i, len;
    WaveHeader* header;
    WaveFmtBody* fmt;
    WaveChunkHeader* chunk_header;
    unsigned char* pbuf = (unsigned char*)malloc(128);
    if(NULL == pbuf)
    {
        dbmsg("pbuf malloc error");
        return -1;
    }
    //1. check Wave Header
    len = sizeof(WaveHeader);
    if( (ret=read(fd, pbuf, len)) != len)
    {
        dbmsg("read error");
        return -1;
    }
    header = (WaveHeader*)pbuf;
    if( (header->magic!=WAV_RIFF) || (header->type!=WAV_WAVE))
    {
        dbmsg("not a wav file");
        return -1;
    }
    //2. check Wave Fmt
    len = sizeof(WaveChunkHeader)+sizeof(WaveFmtBody);
    if( (ret=read(fd, pbuf, len)) != len)
    {
        dbmsg("read error");
        return -1;
    }
    chunk_header = (WaveChunkHeader*)pbuf;
    if( chunk_header->type!=WAV_FMT)
    {
        dbmsg("fmt body error");
        return -1;
    }
    fmt = (WaveFmtBody*)(pbuf+sizeof(WaveChunkHeader));
    if(fmt->format != 0x0001) //WAV_FMT_PCM
    {
        dbmsg("format is not pcm");
        return -1;
    }
    dbmsg("format=0x%x, channels=0x%x,sample_fq=%d,byte_p_sec=%d,byte_p_sample=%d,bit_p_sample=%d",
            fmt->format, fmt->channels,fmt->sample_fq, fmt->byte_p_sec,
            fmt->byte_p_spl, fmt->bit_p_spl);
    //copy params
    hw_params->channels = fmt->channels;
    hw_params->rate = fmt->sample_fq;
    switch(fmt->bit_p_spl)
    {
        case 8:
            hw_params->format = SND_PCM_FORMAT_U8;
            break;
        case 16:
            hw_params->format = SND_PCM_FORMAT_S16_LE;
            break;
        default:
            dbmsg("FIXME: add more format");
            break;
    }
    //3. check data chunk
    len = sizeof(WaveChunkHeader);
    if( (ret=read(fd, pbuf, len)) != len)
    {
        dbmsg("read error");
        return -1;
    }
    chunk_header = (WaveChunkHeader*)pbuf;
    if(chunk_header->type != WAV_DATA)
    {
        dbmsg("not data chunk");
        return -1;
    }
    dbmsg("pcm_data_size=0x%x",chunk_header->length);

    free(pbuf);
    pbuf = NULL;
    return -1;
}

int main ( int argc, char *argv[] )
{
    int i, fd;
    int ret, dir, size;
    unsigned int val, val2;
    char* buffer;
    snd_pcm_t* handle;
    snd_pcm_hw_params_t* params;
    snd_pcm_uframes_t periodsize;
    snd_pcm_uframes_t frames;
    HWParams hw_params;
    if(argc<2)
    {
        dbmsg("usage ./play ");
        return -1;
    }
    fd = open(argv[1], O_RDWR);
    if(fd<0)
    {
        dbmsg("file open error");
        return -1;
    }
    check_wavfile(fd, &hw_params);   //从wav头中分析出的参数,保存在hw_param中
    //1. 打开alsa
    if( (ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0)) < 0)
    {
        dbmsg("open pcm device error:%s", snd_strerror(ret));
        return -1;
    }
    //2. 给参数分配空间,并用hw_param(从wav头中分析出的参数)初始化
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_hw_params_any(handle, params);
    snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED);
    //snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); //last param get from wav file
    snd_pcm_hw_params_set_format(handle, params, hw_params.format);

    //snd_pcm_hw_params_set_channels(handle, params, 2);
    snd_pcm_hw_params_set_channels(handle, params, hw_params.channels); //last param get from wav file
    dbmsg("hw_params: format=%d, channels=%d", hw_params.format, hw_params.channels);

    val = 44100;
    snd_pcm_hw_params_set_rate_near(handle,params, &val, &dir);

    frames = 32*4;
    snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir);
    //3. set param to driver
    if((ret=snd_pcm_hw_params(handle, params)) < 0)
    {
        dbmsg("set hw params error:%s", snd_strerror(ret));
        return -1;
    }

    snd_pcm_hw_params_get_period_size(params, &frames, &dir);
    size = frames*4; //2byte/smaple, 2 channels
    buffer = (char*)malloc(size);

    snd_pcm_hw_params_get_period_time(params, &val, &dir);
    while(1)
    {
        ret = read(fd, buffer, size);                //3.从wav文件中读取数据
        if(ret==0)
        {
            dbmsg("end of file");
            return 0;
        }else if (ret!=size)
        {
            dbmsg("short read");
        }

        ret = snd_pcm_writei(handle, buffer, frames);   //4.将读取数据写到driver中进行播放
        if(ret == -EPIPE)
        {
            //dbmsg("-EPIPE");
            snd_pcm_prepare(handle);
        }
    }

    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
    return EXIT_SUCCESS;
}

3. 运行结果

cong@msi:/work/ffmpeg/test/alsa/testalsa/4player$ make run
export LD_LIBRARY_PATH=/work/ffmpeg/out/lib/ \
    && ./play /work/ffmpeg/test/resource//test.wav
play.c:check_wavfile[94]: format=0x1, channels=0x2,sample_fq=44100,byte_p_sec=176400,byte_p_sample=4,bit_p_sample=16
play.c:check_wavfile[123]: pcm_data_size=0x25c9b20
play.c:main[168]: hw_params: format=2, channels=2

你可能感兴趣的:(jetson,nane,or,xavier,编解码,c++,linux,音视频,运维,alsa)