#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <stdlib.h>
#include <stdio.h>
#include <linux/soundcard.h>
#include <termios.h>
#define RATE 16000 //采样频率
#define SIZE 16 //量化位数
#define CHANNELS 1 //声道数目
#define RSIZE 80 //buf的大小,
int main(void)
{
int fd_dev_r;
int fd_dev_w;
int fd_f;
int arg;
int status;
char choice;
int i;
unsigned char buf[RSIZE]; //每次循环取得RSIZE大小的容量,放入buf,然后写入文件;
//打开声卡设备,只读方式;并对声卡进行设置
fd_dev_r= open("/dev/dsp", O_RDONLY,0777);
if (fd_dev_r < 0)
{
perror("Cannot open /dev/dsp device");
return 1;
}
arg = SIZE;
status = ioctl(fd_dev_r, SOUND_PCM_WRITE_BITS, &arg);//设置量化位数
if (status == -1)
{
perror("Cannot set SOUND_PCM_WRITE_BITS ");
return 1;
}
arg = CHANNELS;
status = ioctl(fd_dev_r, SOUND_PCM_WRITE_CHANNELS, &arg);//设置声道数
if (status == -1)
{
perror("Cannot set SOUND_PCM_WRITE_CHANNELS");
return 1;
}
arg = RATE;
status = ioctl(fd_dev_r, SOUND_PCM_WRITE_RATE, &arg);//设置采样率
if (status == -1)
{
perror("Cannot set SOUND_PCM_WRITE_WRITE");
return 1;
}
//打开声卡设备,只写方式;并对声卡进行设置
fd_dev_w = open("/dev/dsp", O_WRONLY,0777);
if (fd_dev_w < 0)
{
perror("Cannot open /dev/dsp device");
return 1;
}
arg = SIZE;
status = ioctl(fd_dev_w, SOUND_PCM_WRITE_BITS, &arg);//设置量化位数
if (status == -1)
{
perror("Cannot set SOUND_PCM_WRITE_BITS ");
return 1;
}
arg = CHANNELS;
status = ioctl(fd_dev_w, SOUND_PCM_WRITE_CHANNELS, &arg);//设置声道数
if (status == -1)
{
perror("Cannot set SOUND_PCM_WRITE_CHANNELS");
return 1;
}
arg = RATE;
status = ioctl(fd_dev_w, SOUND_PCM_WRITE_RATE, &arg);//设置采样率
if (status == -1)
{
perror("Cannot set SOUND_PCM_WRITE_WRITE");
return 1;
}
for(;;)
{
status = read(fd_dev_r, buf, sizeof(buf));
if (status != sizeof(buf))
{
perror("read wrong number of bytes");
}
status = write(fd_dev_w, buf, sizeof(buf));//送声卡播放
if (status != sizeof(buf))
perror("wrote2 wrong number of bytes");
}
close(fd_dev_r);//关闭只读方式的声卡
close(fd_dev_w);
return 0;
}
如果没有强制设置buffer大小,系统默认的缓冲buffer的大小是2*4096=8192字节,其中2表示的是fragement的数量,4096是每个fragementsize的大小。系统读取音频数据之后,放音的时候,先放入缓冲buffer中,当缓冲buffer填满时,系统才会产生中断,播放音频。所以这里会存在一个延迟。像系统默认的buffer大小,如果录制的是16khz 16bit的音频的话,延迟大小是就是2 * 4096 / (16000 * 2) = 256ms。如果程序要求实时性,就必须得修改buffer的大小。
在OSS的ioctl接口中,SNDCTL_DSP_SETFRAGMENT就是用来设置驱动程序内部缓冲区大小。具体的用法如下:(特别注意的是这个设置要放在所有设置之前,不然会出问题)
int param;
param = ( 0x0004 << 16) + 0x000a;
if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) {
...error handling...
}
参数param由两部分组成:低16位为fragment的大小,此处0x000a表示fragment大小为2^0xa,即1024字节;高16位为fragment的数量,此处为0x0004,即4个fragement。设置好fragment参数后,通过ioctl的SNDCTL_DSP_SETFRAGMENT命令调整驱动程序中的缓冲区。
如果你要查看系统的buffer的大小的话,也可以通过以下程序获得
int frag_size;
ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &frag_size)
也可以通过以下程序获得更详细的信息:
audio_buf_info info;
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
{
printf("SNDCTL_DSP_GETOSPACE error\n");
}
printf("bytes:%d fragments:%d fragsize:%d fragstotal:%d\n",info.bytes,info.fragments,info.fragsize,info.fragstotal);
解决时延的完成程序
#include <sys/ioctl.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/soundcard.h>
#include <sys/time.h>
#include <time.h>
#define DEVICE_NAME "/dev/dsp"
#include <stdio.h>
#include <malloc.h>
#define frame_size 80
int audio_fd;
int recoder_fid;
audio_buf_info info;
int main(int argc, char *argv[])
{
short int i;
short int tempVar;
int status;
int sampleRate = 16000; //16khz
int format = 16;
int channels = 1;
int frag_size= 0;
FILE *fp_in;
short int *SigBuf;
int FrameCounter=0;
char *read_mic_file = "read_from_mic.pcm";
printf("时延 (128*2)/(16000*2)= 8ms \n");
fp_in=fopen(read_mic_file,"wb");
recoder_fid = open(DEVICE_NAME, O_RDONLY,0777);
if (recoder_fid < 0)
{
perror("Cannot open /dev/dsp device");
return 1;
}
status = ioctl(recoder_fid, SOUND_PCM_WRITE_BITS, &format);//设置量化位数
if(status == -1)
{
perror("Cannot set SOUND_PCM_WRITE_BITS ");
return 1;
}
status = ioctl(recoder_fid, SOUND_PCM_WRITE_CHANNELS, &channels);//设置声道数
if (status == -1)
{
perror("Cannot set SOUND_PCM_WRITE_CHANNELS");
return 1;
}
status = ioctl(recoder_fid, SOUND_PCM_WRITE_RATE, &sampleRate);//设置采样率
if (status == -1)
{
perror("Cannot set SOUND_PCM_WRITE_WRITE");
return 1;
}
if ((audio_fd = open(DEVICE_NAME, O_WRONLY,0777)) == -1)
{
printf("open error\n");
return -1;
}
if (ioctl(audio_fd, SOUND_PCM_WRITE_BITS, &format) == -1)
{
/* fatal error */
printf("SNDCTL_DSP_SETFMT error\n");
return -1;
}
int param;
param = ( 0x0002 << 16) + 0x0006; //参数param由两部分组成:低16位为fragment的大小,此处0x0007表示fragment大小为2^7,即128字节;
//高16位为fragment的数量,此处为0x0002,即2个fragement。
if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) {
printf("SNDCTL_DSP_SETFRAGMENT error\n");
}
if (ioctl(audio_fd, SOUND_PCM_WRITE_CHANNELS, &channels) == -1)
{
/* Fatal error */
printf("SOUND_PCM_WRITE_CHANNELS error");
return -1;
}
if (ioctl(audio_fd, SOUND_PCM_WRITE_RATE, &sampleRate)==-1)
{
/* Fatal error */
printf("SOUND_PCM_WRITE_RATE error\n");
return -1;
}
int version = 0;
if (ioctl(audio_fd, OSS_GETVERSION, &version) == -1) {
printf("Failed to get OSS version\n");
}
printf("OSS version is:%x \n",version);
ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &frag_size);
printf("fragment size is:%d \n",frag_size);
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
{
printf("SNDCTL_DSP_GETOSPACE error\n");
}
printf("bytes:%d fragments:%d fragsize:%d fragstotal:%d\n",info.bytes,info.fragments,info.fragsize,info.fragstotal);
printf("the wav sampleRate is %d\n",sampleRate);
printf("format: %d \n",format);
SigBuf = calloc(1, sizeof(short int) * frame_size);
while (read(recoder_fid, SigBuf, sizeof(short int)*frag_size))
{
fwrite(SigBuf,sizeof(short int),frag_size,fp_in);
FrameCounter++;
write(audio_fd, SigBuf, sizeof(short int)*frag_size);
}
printf("Frame number:%d \n",FrameCounter);
close(recoder_fid);
close(audio_fd);
fclose(fp_in);
free(SigBuf);
return 0;
}
程序运行图如下:
但是从官方的文档来看,官方并不推荐这样做,http://manuals.opensound.com/developer/audio_timing.html 因为系统默认的buffer是根据你的采样率等设置自动计算的。强制设置buffer未必会达到你想要的结果,就像刚才说的,buffer的设置不能放在后面,之前我是放在后面,程序一开始运行不会出错,但是等一两分之后,程序出现了非常大的延迟,大概有两秒左右。由上面的程序运行图可以知道,OSS版本是3.08。OSS现在已经升级到4.0,所以OSS4.0也对这个时延进行了改进。http://manuals.opensound.com/developer/SNDCTL_DSP_POLICY.html 可以通过以下程序设置:
int policy=The policy value;
ioctl(fd, SNDCTL_DSP_POLICY, &policy);
policy的值来设定时延的大小。Values below 5 will give lower and lower latencies with slightly increased CPU usage. Values above 5 will give lower CPU usage with increased latencies.