文章摘要:
本文主要描述了采用alsa接口播放wav格式声音方法,稍后抄一些关于alsa的介绍补充。

运行环境:debian8


安装alsa应用程序接口库:

编译选项: -lasound


打开并初始化设备:

#include <alsa/asoundlib.h>

snd_pcm_t *snd_pcm;      
snd_pcm_hw_params_t *params;    
unsigned int sample     = 44100;    // 采样率(重要)
unsigned int channels   = 1;        // 通道数量
snd_pcm_format_t format = SND_PCM_FORMAT_S16_LE; 

/*
 * 初始化ALSA设备
 */
int alsa_init(void)
{
  int ret;     
  snd_pcm_uframes_t frames = 1024;
   
  // 打开音频设备
  ret = snd_pcm_open(&snd_pcm, "default",SND_PCM_STREAM_PLAYBACK, 0);
  if (ret)
  {
    printf("can not open pcm device: %s\r\n", snd_strerror(ret));
    return -1;
  }
  // 分配参数内存(将指针地址传入)
  snd_pcm_hw_params_alloca(&params);
  // 填充默认参数
  snd_pcm_hw_params_any(snd_pcm, params);
  // 初始化访问权限
  ret = snd_pcm_hw_params_set_access(snd_pcm, params, SND_PCM_ACCESS_RW_INTERLEAVED);
  if (ret < 0) 
  {
    printf("set_access error: %s\r\n", snd_strerror(ret));
    return -2;
  }
  // 设置数据格式: SND_PCM_FORMAT_S16_LE/S24_LE/U8
  snd_pcm_hw_params_set_format(snd_pcm, params, format);
  // 设置通道数量(重要)
  snd_pcm_hw_params_set_channels(snd_pcm, params, channels);
  // 设置采样率(重要)
  snd_pcm_hw_params_set_rate_near(snd_pcm, params, &sample, 0);
  
#if 0
  // 设置每个周期有多少帧(可采用默认值)  
  ret = snd_pcm_hw_params_set_period_size_near(snd_pcm, params,&frames, 0);
  if(ret < 0)
  {
    printf("set_period_size error: %s\r\n", snd_strerror(ret));    
  }
  printf("frames = %d\r\n", frames);
#endif
  
  // 将参数写入设备
  ret = snd_pcm_hw_params(snd_pcm, params);
  if(ret < 0)
  {
    printf("write params failed: %s\r\n", snd_strerror(ret));    
    return -3;
  }   
  
  // 读取每周期帧数
  snd_pcm_hw_params_get_period_size(params, &frames, 0);
  
  return 0;
}

播放WAV格式:

int main(int argc, char** argv)
{
  int fd;
  int ret;
  int count;
  char *pdata;                  // 数据指针
  char buff[2048];
  snd_pcm_uframes_t frames;
  
  // 打开声音文件
  fd = open(argv[1], O_RDONLY);
  if (fd < 0)
  {
    perror("can not open snd file ");
  }
     
  // 读取文件头并解析相关参数,此处先跳过文件头,再WAV章节再做解析
  ret = read(fd, buff, 44); 
    
  // 初始化声卡
  alsa_init(); 
  // 获取每周期的帧数
  snd_pcm_hw_params_get_period_size(params, &frames, 0);
  // 计算单次数据长度 = 每周期帧数*通道号*样本数据位数/8
  count = frames * channels * 2;
  
  printf("size = %d\r\n", count);
  
  pdata = malloc(count);
  if(pdata == NULL)
  {
    printf("malloc failed\r\n");    
  }

  while (1)
  {
    // 从文件读入声音数据
    ret = read(fd, pdata, count);
    if (ret <= 0)
    {
      break;
    }
     
    // 将数据写入alsa播放(写入的帧数是前边读出来的值)
    ret = snd_pcm_writei(snd_pcm, pdata, frames);
    if (ret == -EPIPE)
    {
      snd_pcm_prepare(snd_pcm);
    }
  }

  // 等待缓冲区数据播放结束
  while(snd_pcm_avail_update(snd_pcm) > 0)
  {
    usleep(20 * 1000);
  }

  snd_pcm_drop(snd_pcm);
  snd_pcm_drain(snd_pcm);
  snd_pcm_close(snd_pcm); // 关闭ALSA
  
  close(fd); // 关闭文件
}

移植alsa库至ARM平台:
下载地址:https://www.alsa-project.org/files/pub/lib/
软件版本:alsa-lib-1.2.1.tar.bz2

# ./configure --host=arm-linux --prefix=/opt/rootfs CC=arm-linux-gnueabihf-gcc
# make
# make install

注意事项:
V1.2.2版本有问题,交叉编译报错。