DSP

LINUX下实时录放音(OSS)—解决时延问题

2019-07-13 21:00发布

#include #include #include #include #include #include #include #include #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 "); } 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; //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 "); 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) { /* fatal error */ printf("SNDCTL_DSP_SETFMT error "); 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 "); } 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 "); 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.