一.
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 <alsa/asoundlib.h>
-
#include <stdlib.h>
-
//需要从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(¶ms);
-
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
4.代码打包
4player.rar(下载后改名为4player.tar.gz)
阅读(3481) | 评论(0) | 转发(0) |