#include
#include
#include
#include
#include
#include
#include
#include
#define RATE 16000
#define SIZE 16
#define CHANNELS 1
#define RSIZE 80
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];
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
");
}
printf("bytes:%d fragments:%d fragsize:%d fragstotal:%d
",info.bytes,info.fragments,info.fragsize,info.fragstotal);
解决时延的完成程序
#include
#include
#include
#include
#include
#include
#define DEVICE_NAME "/dev/dsp"
#include
#include
#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;
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
");
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
");
return -1;
}
if (ioctl(audio_fd, SOUND_PCM_WRITE_BITS, &format) == -1)
{
printf("SNDCTL_DSP_SETFMT error
");
return -1;
}
int param;
param = ( 0x0002 << 16) + 0x0006;
if (ioctl(audio_fd, SNDCTL_DSP_SETFRAGMENT, ¶m) == -1) {
printf("SNDCTL_DSP_SETFRAGMENT error
");
}
if (ioctl(audio_fd, SOUND_PCM_WRITE_CHANNELS, &channels) == -1)
{
printf("SOUND_PCM_WRITE_CHANNELS error");
return -1;
}
if (ioctl(audio_fd, SOUND_PCM_WRITE_RATE, &sampleRate)==-1)
{
printf("SOUND_PCM_WRITE_RATE error
");
return -1;
}
int version = 0;
if (ioctl(audio_fd, OSS_GETVERSION, &version) == -1) {
printf("Failed to get OSS version
");
}
printf("OSS version is:%x
",version);
ioctl(audio_fd, SNDCTL_DSP_GETBLKSIZE, &frag_size);
printf("fragment size is:%d
",frag_size);
if (ioctl(audio_fd, SNDCTL_DSP_GETOSPACE, &info) == -1)
{
printf("SNDCTL_DSP_GETOSPACE error
");
}
printf("bytes:%d fragments:%d fragsize:%d fragstotal:%d
",info.bytes,info.fragments,info.fragsize,info.fragstotal);
printf("the wav sampleRate is %d
",sampleRate);
printf("format: %d
",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
",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.