一、声卡缓冲区设置
开发工作中,有时候可能会遇到声卡的XRUN现象,原因就是声卡缓冲区不足或满溢。
在设置OSS声卡驱动内部缓冲区时,为了防止音频抖动,保证播放性能,设置了内部缓冲区,即DMA buffer。
播放时候,首先应用程序通过驱动程序首先将音频数据从应用程序缓冲区,即APP buffer,写入到DMA buffer;接着,由DMA控制器把DMA buffer中的音频数据发送到DAC进行下一步处理
因此,可能会出现矛盾现场:
1. 某些时刻CPU没有时间向DMA buffer放入新的音频数据,DAC由于没有输入新的音频数据,
导致声音播放的间断,这就出现了声音的抖动现象
2. 如果将DMA buffer设置的足够大,使得DAC始终有数据播放,也会使得每次从APP buffer拷贝的时间也变长,
导致了更大的播放延迟
对于这个矛盾,可以从两个不同的方面分别着手解决
1.1 方法一
驱动程序采用多缓冲方式,即将大的DMA buffer分割成多个小的缓冲区,称之为fragment,它们的大小相同。驱动程序开始时只需等待一个fragment满了就开始播放,这样可以通过增加fragment的个数来增加缓冲区的大小,但同时每个fragment被限制在合适的大小,也不影响时延
1.2 方法二
应用程序将驱动程序中的缓冲通过mmap映射到自己地址空间后,以自己的方式来处理这些缓冲区,根据需要设置驱动程序中内部缓冲区的大小
在OSS的ioctl接口中,SNDCTL_DSP_SETFRAGMENT就是用来设置驱动程序内部缓冲区大小。具体的用法如下:
int param;
param = ( 0x0004 << 16) + 0x000a;
if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, ¶m) == -1)
{
printf("错误处理\n");
}
参数param由两部分组成:低16位为fragment的大小,此处0x000a表示fragment大小为2^0xa,即1024字节;高16位为fragment的数量,此处为0x0004,即4个fragement。设置好fragment参数后,通过ioctl的SNDCTL_DSP_SETFRAGMENT命令调整驱动程序中的缓冲区。
一个 fragment 的播放时延 = fragment大小 / (频率 * 2 * 2)
以fragment大小为512字节为例,T = 512 / (48000 * 2 * 2)= 2.90ms
其中,480100表示48KHz的采样频率,第一个2表示双声道,第二个2表示采样深度是16bit,为2个字节
如下,也可以谈过下列方式获取当前已经设置的声卡相关信息
audio_buf_info audio_info;
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &audio_info) == -1)
printf("SNDCTL_DSP_GETOSPACE error!\n");
else
printf("bytes = %d, fragments = %d, fragsize = %d, fragstotal = %d\n",
audio_info.bytes, audio_info.fragments, audio_info.fragsize, audio_info.fragstotal);
二、其它设置
2.1 设置声道数目
int channels = 0; // 0=mono 1=stereo
int result = ioctl(handle, SNDCTL_DSP_STEREO, &channels);
if(result == -1)
{
printf("ioctl channel number\n");
}
if(channels != 0)
{
printf("只支持立体声\n");
}
2.2 设置声卡的采样格式
int format = 16;
int result = ioctl(handle, SNDCTL_DSP_SETFMT, &format);
if ( result == -1 )
{
printf("ioctl sample format\n");
}
2.3 设置声卡的采样频率
int rate = 48000;
int result = ioctl(handle, SNDCTL_DSP_SPEED, &rate);
if ( result == -1 )
{
printf("ioctl sample format\n");
}