set_pcm_play函数用于播放长度为datalen的buffer中的字符串,其中buffer中字符串为除去WAV文件头得到的二进制歌曲文件。
int demo_sound::set_pcm_play(char *buffer,int datalen)
{
int rc;
int ret=0;
int size;
snd_pcm_t* handle; //PCI设备句柄
snd_pcm_hw_params_t* params;//硬件信息和PCM流配置
unsigned int val;
int dir=0;
snd_pcm_uframes_t frames;
int channels=1;
int frequency=16000;
int bit=16;
int datablock=2;
unsigned char ch[100]; //用来存储wav文件的头信息
rc=snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
if(rc<0)
{
perror("\nopen PCM device failed:");
exit(1);
}
snd_pcm_hw_params_alloca(¶ms); //分配params结构体
if(rc<0)
{
perror("\nsnd_pcm_hw_params_alloca:");
exit(1);
}
rc=snd_pcm_hw_params_any(handle, params);//初始化params
if(rc<0)
{
perror("\nsnd_pcm_hw_params_any:");
exit(1);
}
rc=snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); //初始化访问权限
if(rc<0)
{
perror("\nsed_pcm_hw_set_access:");
exit(1);
}
/* Signed 16-bit little-endian format */
snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE);
rc=snd_pcm_hw_params_set_channels(handle, params, 1); //设置声道,1表示单声>道,2表示立体声
if(rc<0)
{
perror("\nsnd_pcm_hw_params_set_channels:");
exit(1);
}
val = frequency;
rc=snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); //设置>频率
if(rc<0)
{
perror("\nsnd_pcm_hw_params_set_rate_near:");
exit(1);
}
rc = snd_pcm_hw_params(handle, params);
if(rc<0)
{
perror("\nsnd_pcm_hw_params: ");
exit(1);
}
if ((rc = snd_pcm_set_params(handle,
SND_PCM_FORMAT_S16_LE,
SND_PCM_ACCESS_RW_INTERLEAVED,
1,
16000,
1,
500000)) < 0) { /* 0.5sec */
printf("Playback open error: %s\n", snd_strerror(rc));
exit(EXIT_FAILURE);
}
rc=snd_pcm_hw_params_get_period_size(params, &frames, &dir); /*获取周期长度*/
if(rc<0)
{
perror("\nsnd_pcm_hw_params_get_period_size:");
exit(1);
}
size = frames * datablock; /*4 代表数据快长度*/
int readlen=0;
while (1)
{
// 写音频数据到PCM设备
int try_count=0; //错误重试计数
while((ret = snd_pcm_writei(handle, buffer+readlen, frames))<0)
{
// usleep(2000);
if (ret == -EPIPE) /*设备被抢占,重新配置设备*/
{
SYS_LOG(INFO,"underrun occurred\n");
int err = snd_pcm_prepare(handle); //完成硬件参数设置,使设备准备好
if (err < 0){
printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(err));
goto ERROR_MASK;
}
err = snd_pcm_recover(handle, ret, 0);
if (err < 0){
printf("snd_pcm_recover prepare failed: %s\n", snd_strerror(err));
goto ERROR_MASK;
}
printf("err %d",err);
}
else if (ret < 0)
{
SYS_LOG(INFO,"error from writei: %s\n",snd_strerror(ret));
}
printf("try_count %d ",try_count);
/*写设备出错,重试10次,避免死循环*/
{
try_count++;
if(try_count>10)
{
SYS_LOG(INFO,"the count of retry out of time !\n");
try_count=0;
goto ERROR_MASK;
break;
}
}
}
readlen+=size;
if(readlen+size>datalen)
break;
}
snd_pcm_drain(handle);
snd_pcm_close(handle);
return 0;
ERROR_MASK:
snd_pcm_drain(handle);
snd_pcm_close(handle);
return -1;
}
错误:出现underrun的原因是:初始化配置有问题。