Linux音频设备驱动_OSS驱动框架(四)————OSS 用户空间编程

一、DSP 编程
对OSS 驱动声卡的编程使用Linux 文件接口函数,DSP 接口的操作一般包括如下几个步骤:
① 打开设备文件/dev/dsp
采用何种模式对声卡进行操作也必须在打开设备时指定,对于不支持全双工的声卡来说,应该使用只读或者只写的方式打开,只有那些支持全双工的声卡,才能以读写的方式打开,这还依赖于驱动程序的具体实现。Linux 允许应用程序多次打开或者关闭与声卡对应的设备文件,从而能够很方便地在放音状态和录音状态之间进行切换。

② 如果有需要,设置缓冲区大小
运行在Linux 内核中的声卡驱动程序专门维护了一个缓冲区,其大小会影响到放音和录音时的效果,使用ioctl()系统调用可以对它的尺寸进行恰当的设置。调节驱动程序中缓冲区大小的操作不是必须的,如果没有特殊的要求,一般采用默认的缓冲区大小也就可以了。如果想设置缓冲区的大小,则通常应紧跟在设备

③ 设置声道(channel)数量
根据硬件设备和驱动程序的具体情况,可以设置为单声道或者立体声。

④ 设置采样格式和采样频率

⑤ 读写/dev/dsp 实现播放或录音
下面代码实现了利用/dev/dsp 接口进行声音录制和播放的过程,它的功能是先录制几秒钟音频数据,将其存放在内存缓冲区中,然后再进行放音。

#include <unistd.h>  
 #include <fcntl.h>  
 #include <sys/types.h>  
 #include <sys/ioctl.h>  
 #include <stdlib.h>  
 #include <stdio.h>  
 #include <linux/soundcard.h>  
 #define LENGTH 3   /* 存储秒数 */  
 #define RATE 8000  /* 采样频率 */  
 #define SIZE 8     /* 量化位数 */  
 #define CHANNELS 1 /* 声道数目 */  
 /* 用于保存数字音频数据的内存缓冲区 */  
 unsigned char buf[LENGTH *RATE * SIZE * CHANNELS / 8];  

 int main()  
 {  
   int fd; /* 声音设备的文件描述符 */  
   int arg; /* 用于ioctl 调用的参数 */  
   int status; /* 系统调用的返回值 */  

   /* 打开声音设备 */  
   fd = open("/dev/dsp", O_RDWR);  
   if (fd < 0)  
   {  
      perror("open of /dev/dsp failed");  
      exit(1);  
   }  
   /* 设置采样时的量化位数 */  
   arg = SIZE;  
   status = ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);  
   if (status == - 1)  
     perror("SOUND_PCM_WRITE_BITS ioctl failed");  

   if (arg != SIZE)  
      perror("unable to set sample size");  

   /* 设置采样时的通道数目 */  
   arg = CHANNELS;  
   status = ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &arg);  
   if (status == - 1)  
      perror("SOUND_PCM_WRITE_CHANNELS ioctl failed");  
   if (arg != CHANNELS)  
      perror("unable to set number of channels");  

   /* 设置采样率 */  
   arg = RATE;  
   status = ioctl(fd, SOUND_PCM_WRITE_RATE, &arg);  
   if (status == - 1)  
      perror("SOUND_PCM_WRITE_WRITE ioctl failed");  

   /* 循环,直到按下Control-C */  
   while (1)  
   {  
      printf("Say something:\n");  
      status = read(fd, buf, sizeof(buf)); /* 录音 */  
      if (status != sizeof(buf))  
         perror("read wrong number of bytes");  

      printf("You said:\n");  
      status = write(fd, buf, sizeof(buf)); /* 放音 */  
      if (status != sizeof(buf))  
         perror("wrote wrong number of bytes");  

      /* 在继续录音前等待放音结束 */  
      status = ioctl(fd, SOUND_PCM_SYNC, 0);  
      if (status == - 1)  
         perror("SOUND_PCM_SYNC ioctl failed");  
   }  
}  

二、mixer 编程
声卡上的混音器由多个混音通道组成,它们可以通过驱动程序提供的设备文件/dev/mixer 进行编程。对混音器的操作一般都通过ioctl()系统调用来完成,所有控制命令都以SOUND_MIXER 或者MIXER 开头,下表列出了常用的混音器控制命令。

命 令                              作 用
SOUND_MIXER_VOLUME               主音量调节
SOUND_MIXER_BASS                 低音控制
SOUND_MIXER_TREBLE               高音控制
SOUND_MIXER_SYNTH FM             合成器
SOUND_MIXER_PCM 主D/A             转换器
SOUND_MIXER_SPEAKER PC           喇叭
SOUND_MIXER_LINE                 音频线输入
SOUND_MIXER_MIC                  麦克风输入
SOUND_MIXER_CD CD                输入
SOUND_MIXER_IMIX                 放音音量
SOUND_MIXER_ALTPCM               从D/A 转换器
SOUND_MIXER_RECLEV               录音音量
SOUND_MIXER_IGAIN                输入增益
SOUND_MIXER_OGAIN                输出增益
SOUND_MIXER_LINE1                声卡的第1 输入
SOUND_MIXER_LINE2                声卡的第2 输入
SOUND_MIXER_LINE3                声卡的第3 输入

对声卡的输入增益和输出增益进行调节是混音器的一个主要作用,目前大部分声卡采用的是8 位或者16 位的增益控制器,声卡驱动程序会将它们变换成百分比的形式,也就是说无论是输入增益还是输出增益,其取值范围都是从0 到100。

• SOUND_MIXER_READ
在进行混音器编程时,可以使用 SOUND_MIXER_READ 宏来读取混音通道的增益大小,例如如下代码可以获得麦克风的输入增益:

ioctl(fd, SOUND_MIXER_READ(SOUND_MIXER_MIC), &vol);

对于只有一个混音通道的单声道设备来说,返回的增益大小保存在低位字节中。而对于支持多个混音通道的双声道设备来说,返回的增益大小实际上包括两个部分,分别代表左、右两个声道的值,其中低位字节保存左声道的音量,而高位字节则保存右声道的音量。下面的代码可以从返回值中依次提取左右声道的增益大小:

int left, right;
left = vol & 0xff;
right = (vol & 0xff00) >> 8;

• SOUND_MIXER_WRITE
如果想设置混音通道的增益大小,则可以通过SOUND_MIXER_WRITE 宏来实现,例如下面的语句可以用来设置麦克风的输入增益:

vol = (right << 8) + left;
ioctl(fd, SOUND_MIXER_WRITE(SOUND_MIXER_MIC), &vol);

• 查询Mixer信息
声卡驱动程序提供了多个ioctl()系统调用来获得混音器的信息,它们通常返回一个整型的位掩码,其中每一位分别代表一个特定的混音通道,如果相应的位为1,则说明与之对应的混音通道是可用的。通过 SOUND_MIXER_READ_DEVMASK 返回的位掩码查询出能够被声卡支持的每一个混音通道,而通过SOUND_MIXER_READ_RECMAS 返回的位掩码则可以查询出能够被当作录音源的每一个通道。例如,如下代码可用来检查CD 输入是否是一个有效的混音通道:

ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask);
if (devmask & SOUND_MIXER_CD)
   printf("The CD input is supported");

如下代码可用来检查CD 输入是否是一个有效的录音源:

ioctl(fd, SOUND_MIXER_READ_RECMASK, &recmask);
if (recmask & SOUND_MIXER_CD)
   printf("The CD input can be a recording source");

大多数声卡提供了多个录音源,通过 SOUND_MIXER_READ_RECSRC 可以查询出当前正在使用的录音源,同一时刻可使用2个或2个以上的录音源,具体由声卡硬件本身决定。相应地,使用 SOUND_MIXER_WRITE_RECSRC可以设置声卡当前使用的录音源,如下代码可以将CD输入作为声卡的录音源使用:

devmask = SOUND_MIXER_CD;
ioctl(fd, SOUND_MIXER_WRITE_RECSRC, &devmask);

此外,所有的混音通道都有单声道和双声道的区别,如果需要知道哪些混音通道提供了对立体声的支持,可以通过SOUND_MIXER_READ_STEREODEVS 来获得。
以下代码实现了利用/dev/mixer 接口对混音器进行编程的过程,该程序可对各种混音通道的增益进行调节。

#include <unistd.h>  
#include <stdlib.h>  
#include <stdio.h>  
#include <sys/ioctl.h>  
#include <fcntl.h>  
#include <linux/soundcard.h>  

/* 用来存储所有可用混音设备的名称 */  
const char *sound_device_names[] = SOUND_DEVICE_NAMES;  
int fd; /* 混音设备所对应的文件描述符 */  
int devmask, stereodevs; /* 混音器信息对应的bit 掩码 */  
char *name;  

/* 显示命令的使用方法及所有可用的混音设备 */  
void usage()  
{  
   int i;  
   fprintf(stderr, "usage: %s <device> <left-gain%%> <right-gain%%>\n"  
           "%s <device> <gain%%>\n\n""Where <device> is one of:\n", name, name);  

   for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)  
       if ((1 << i) &devmask)  
          /* 只显示有效的混音设备 */  
          fprintf(stderr, "%s ", sound_device_names[i]);  
   fprintf(stderr, "\n");  
   exit(1);  
}  

int main(int argc, char *argv[])  
{  
   int left, right, level; /* 增益设置 */  
   int status; /* 系统调用的返回值 */  
   int device; /* 选用的混音设备 */  
   char *dev; /* 混音设备的名称 */  
   int i;  
   name = argv[0];  

   /* 以只读方式打开混音设备 */  
   fd = open("/dev/mixer", O_RDONLY);  
   if (fd == - 1)  
   {  
      perror("unable to open /dev/mixer");  
      exit(1);  
   }  

   /* 获得所需要的信息 */  
   status = ioctl(fd, SOUND_MIXER_READ_DEVMASK, &devmask);  
   if (status == - 1)  
      perror("SOUND_MIXER_READ_DEVMASK ioctl failed");  

   status = ioctl(fd, SOUND_MIXER_READ_STEREODEVS, &stereodevs);  
   if (status == - 1)  
       perror("SOUND_MIXER_READ_STEREODEVS ioctl failed");  
   /* 检查用户输入 */  
   if (argc != 3 && argc != 4)  
   usage();  

   /* 保存用户输入的混音器名称 */  
   dev = argv[1];  

   /* 确定即将用到的混音设备 */  
   for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)  
   if (((1 << i) &devmask) && !strcmp(dev, sound_device_names[i]))  
      break;  
   if (i == SOUND_MIXER_NRDEVICES)  
   {  
      /* 没有找到匹配项 */  
      fprintf(stderr, "%s is not a valid mixer device\n", dev);  
      usage();  
   }  

   /* 查找到有效的混音设备 */  
  device = i;  
  /* 获取增益值 */  
  if (argc == 4)  
  {  
     /* 左、右声道均给定 */  
     left = atoi(argv[2]);  
     right = atoi(argv[3]);  
  }  
  else  
  {  
     /* 左、右声道设为相等 */  
     left = atoi(argv[2]);  
     right = atoi(argv[2]);  
  }  

  /* 对非立体声设备给出警告信息 */  
  if ((left != right) && !((1 << i) &stereodevs))  
  {  
     fprintf(stderr, "warning: %s is not a stereo device\n", dev);  
  }  

  /* 将两个声道的值合到同一变量中 */  
  level = (right << 8) + left;  

  /* 设置增益 */  
  status = ioctl(fd, MIXER_WRITE(device), &level);  
  if (status == - 1)  
  {  
     perror("MIXER_WRITE ioctl failed");  
     exit(1);  
  }  
  /* 获得从驱动返回的左右声道的增益 */  
  left = level &0xff;  
  right = (level &0xff00) >> 8;  

  /* 显示实际设置的增益 */  
  fprintf(stderr, "%s gain set to %d%% / %d%%\n", dev, left, right);  

  /* 关闭混音设备 */  
  close(fd);  
  return 0;  
}  

编译上述程序为可执行文件mixer,执行

 ./mixer <device> <left-gain%> <right-gain%>   或
 ./mixer<device> <gain%>可设置增益,device 可以是vol、pcm、speaker、line、mic、cd、igain、line1、phin、video。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值