alsa与oss声音播放例程及对比

时间:2021-08-06 15:13:32
一、说明
1. ALSA(Advanced Linux Sound Architecture)比较新的音频驱动程序
它的函数一般都以snd开头的
2. OSS(Open Sound System)比较旧的音频驱动程序
它一般对于设备/dev/dsp、/dev/mixer做write、read、ioctl操作
3. 播放声音文件的步骤(不包含解码部分)
1) 打开音频设备
2) 设置采样格式(format)
A) 采样位数分为8位16位……
B) 采样位数为多字节时(如16位)又分为地址低位存值的低位(LE: Little Endian)和地址低位存值的高位(BE: Big Endian)
c) 存储格式分为有符号(S: Signed)和无符号(U: Unsigned)
D) 例:SND_PCM_FORMAT_S16_LE,指16位,有符号,LE格式
3) 设置声道数(channel)
A) 分单声道(Mono:1)和立体声(Stereo:2)
4) 设置采样率(rate)
A) 48000、44100、22050……
5) 设置的其余项一般都不改动(如SND_PCM_ACCESS_RW_INTERLEAVED)
6) 向设备写入数据
7) 关闭音频设备
二、例程及分析
1. 注意:
1) 测试的声音采用Windows XP 启动.wav
Signed 16 bit Little Endian, Rate 22050 Hz, Stereo
2) 如果选用其它音频文件,需要在程序中改参数,各项参数可用以下命令取得,aplay命令会解析wav文件头,从而显示参数
aplay xxx.wav
3) 为了缩短例程,所有地方都没有做函数返回值的判断,如有问题可用gdb调试
2. OSS
1) 例程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/soundcard.h>
 
int main(int argc, char *argv[])
{
   unsigned char buff[1024];
   int dev_fd, file_fd;
   ssize_t n, nRD, nWR;
   unsigned int format = AFMT_S16_LE;
   unsigned int channel = 2;
   unsigned int rate = 22050;
 
   dev_fd = open("/dev/dsp", O_WRONLY);
   file_fd = open("win.wav", O_RDONLY);
   ioctl(dev_fd, SNDCTL_DSP_SETFMT, &format);
   ioctl(dev_fd, SNDCTL_DSP_CHANNELS, &channel);
   ioctl(dev_fd, SNDCTL_DSP_SPEED, &rate);
 
   while (1)
   {
          nRD = 0L;
          nRD = read(file_fd, buff, 1024);
          if (nRD <= 0)
                 break;
          nWR = nRD;
          while (nWR > 0)
          {
                 if ((n = write(dev_fd, buff + (nRD - nWR), nWR)) < 0)
                        break;
                 nWR -= n;
          }
   }
 
   close(dev_fd);
   close(file_fd);
}
2) 分析
A) OSS通过对设备"/dev/dsp"设备的ioctl来设置参数
B) OSS通过对设备"/dev/dsp"写入数据来播放声音
C) 注意写数据的最后一个参数是数据大小
3. ALSA
1) 例程
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
 
main(int argc, char *argv[])
{
   unsigned char buff[1024];
   int s_fd;
   ssize_t nRD;
   snd_pcm_t *handle;
   snd_pcm_hw_params_t *hw_params;
   snd_pcm_sframes_t frames;
   _snd_pcm_format format = SND_PCM_FORMAT_S16_LE;
   unsigned int channel = 2;
   unsigned int rate = 22050;
 
   s_fd = open("win.wav", O_RDONLY);
   snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0);
   snd_pcm_hw_params_malloc(&hw_params);
   snd_pcm_hw_params_any(handle, hw_params);
   snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED);
   snd_pcm_hw_params_set_format(handle, hw_params, format);
   snd_pcm_hw_params_set_rate_near(handle, hw_params, &rate, 0);
   snd_pcm_hw_params_set_channels(handle, hw_params, channel);
   snd_pcm_hw_params(handle, hw_params);
   snd_pcm_hw_params_free(hw_params);
   snd_pcm_prepare(handle);
 
   while (1)
   {
          nRD = 0L;
          nRD = read(s_fd, buff, 1024);
          if (nRD <= 0)
                 break;
 
          while ((frames = snd_pcm_writei(handle, buff, nRD / 4)) < 0)
                 snd_pcm_prepare(handle);
   }
 
   snd_pcm_close(handle);
   close(s_fd);
}
2) 编译参数
注意加 –lasound 用于连接alsa lib库
3) 分析
A) ALSA通过对函数组snd_pcm_xxx对默认音频设备"default"写数据和设置参数,而不指定具体设备文件
B) 千万注意这个函数snd_pcm_writei的最后一个参数,它不是buffer的大小,而是帧数。如果数据是立体声,且采样位数是16位(2字节),那么它的帧数就应该是strlen(buffer)/2/2
C) snd_pcm_recover和snd_pcm_set_params是ALSA高版本所支持的函数,也很常用,因为有些ALSA版本不支持,下面简单介绍
I.  snd_pcm_recover与snd_pcm_prepare相似,都是用来恢复错误的
II. snd_pcm_set_params是用来设置声卡的各个参数的,功能就是上面的snd_pcm_hw_params的合集
三、参考文档
http://dev.21tx.com/2006/07/18/10815.html
http://www.alsa-project.org/alsa-doc/alsa-lib/_2test_2pcm__min_8c-example.html
http://www.equalarea.com/paul/alsa-audio.html
http://blog.chinaunix.net/u2/70719/showart_724538.html