alsa mixer编程

来源:互联网 发布:金十数据直播间喊单 编辑:程序博客网 时间:2024/06/07 05:49
dev/mixer
在声卡的硬件电路中,混音器(mixer)是一个很重要的组成部分,它的作用是将多个信号组合或者叠加在一起,对于不同的声卡来说,其混音器的作用可能各不相同。运行在Linux内核中的声卡驱动程序一般都会提供/dev/mixer这一设备文件,它是应用程序对混音器进行操作的软件接口。混音器电路通常由两个部分组成:输入混音器(input mixer)和输出混音器(output mixer)。
输入混音器负责从多个不同的信号源接收模拟信号,这些信号源有时也被称为混音通道或者混音设备。模拟信号通过增益控制器和由软件控制的音量调节器后,在不同的混音通道中进行级别(level)调制,然后被送到输入混音器中进行声音的合成。混音器上的电子开关可以控制哪些通道中有信号与混音器相连,有些声卡只允许连接一个混音通道作为录音的音源,而有些声卡则允许对混音通道做任意的连接。经过输入混音器处理后的信号仍然为模拟信号,它们将被送到A/D转换器进行数字化处理。
输出混音器的工作原理与输入混音器类似,同样也有多个信号源与混音器相连,并且事先都经过了增益调节。当输出混音器对所有的模拟信号进行了混合之后,通常还会有一个总控增益调节器来控制输出声音的大小,此外还有一些音调控制器来调节输出声音的音调。经过输出混音器处理后的信号也是模拟信号,它们最终会被送给喇叭或者其它的模拟输出设备。对混音器的编程包括如何设置增益控制器的级别,以及怎样在不同的音源间进行切换,这些操作通常来讲是不连续的,而且不会像录音或者放音那样需要占用大量的计算机资源。由于混音器的操作不符合典型的读/写操作模式,因此除了open和close两个系统调用之外,大部分的操作都是通过ioctl系统调用来完成的。与/dev/dsp不同,/dev/mixer允许多个应用程序同时访问,并且混音器的设置值会一直保持到对应的设备文件被关闭为止。

为了简化应用程序的设计,Linux上的声卡驱动程序大多都支持将混音器的ioctl操作直接应用到声音设备上,也就是说如果已经打开了/dev/dsp,那么就不用再打开/dev/mixer来对混音器进行操作,而是可以直接用打开/dev/dsp时得到的文件标识符来设置混音器。



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

名 称作 用SOUND_MIXER_VOLUME主音量调节SOUND_MIXER_BASS低音控制SOUND_MIXER_TREBLE高音控制SOUND_MIXER_SYNTHFM合成器SOUND_MIXER_PCM主D/A转换器SOUND_MIXER_SPEAKERPC喇叭SOUND_MIXER_LINE音频线输入SOUND_MIXER_MIC麦克风输入SOUND_MIXER_CDCD输入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输入

表1 混音器命令

对声卡的输入增益和输出增益进行调节是混音器的一个主要作用,目前大部分声卡采用的是8位或者16位的增益控制器,但作为程序员来讲并不需要关心这些,因为声卡驱动程序会负责将它们变换成百分比的形式,也就是说无论是输入增益还是输出增益,其取值范围都是从0到100。在进行混音器编程时,可以使用SOUND_MIXER_READ宏来读取混音通道的增益大小,例如在获取麦克风的输入增益时,可以使用如下的代码:

int vol; ioctl(fd, SOUND_MIXER_READ(SOUND_MIXER_MIC), &vol); printf("Mic gain is at %d %%\n", vol);

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

int left, right; left = vol & 0xff; right = (vol & 0xff00) >> 8; printf("Left gain is %d %%, Right gain is %d %%\n", left, right);

类似地,如果想设置混音通道的增益大小,则可以通过SOUND_MIXER_WRITE宏来实现,此时遵循的原则与获取增益值时的原则基本相同,例如下面的语句可以用来设置麦克风的输入增益:

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

在编写实用的音频程序时,混音器是在涉及到兼容性时需要重点考虑的一个对象,这是因为不同的声卡所提供的混音器资源是有所区别的。声卡驱动程序提供了多个ioctl系统调用来获得混音器的信息,它们通常返回一个整型的位掩码(bitmask),其中每一位分别代表一个特定的混音通道,如果相应的位为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");

如果进一步还想知道其是否是一个有效的录音源,则可以使用如下语句:

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可以查询出当前正在使用的录音源,同一时刻能够使用几个录音源是由声卡硬件决定的。类似地,使用SOUND_MIXER_WRITE_RECSRC可以设置声卡当前使用的录音源,例如下面的代码可以将CD输入作为声卡的录音源使用:

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

此外,所有的混音通道都有单声道和双声道的区别,如果需要知道哪些混音通道提供了对立体声的支持,可以通过SOUND_MIXER_READ_STEREODEVS来获得。

4.3 音频录放框架

下面给出一个利用声卡上的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; 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"); 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"); } }

4.4 混音器框架

下面再给出一个对混音器进行编程的基本框架,利用它可以对各种混音通道的增益进行调节,其所有的功能都是通过读写/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; 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; }

编译好上面的程序之后,先不带任何参数执行一遍,此时会列出声卡上所有可用的混音通道:

[xiaowp@linuxgam sound]$ ./mixer usage: ./mixer <device> <left-gain%> <right-gain%> ./mixer <device> <gain%> Where <device> is one of: vol pcm speaker line mic cd igain line1 phin video

之后就可以很方便地设置各个混音通道的增益大小了,例如下面的命令就能够将CD输入的左、右声道的增益分别设置为80%和90%:

[xiaowp@linuxgam sound]$ ./mixer cd 80 90 cd gain set to 80% / 90%