Linux实现音频录放
时间:2014-06-22 05:02 来源:linux.it.net.cn 作者:IT网
一、原理简述
在Linux下,录音——从dsp设备读取数据,放音——向dsp设备写入数据。
开发板采用声卡UDA1341实现音频编解码,完成A/D和D/A转换,芯片UDA1341与CPU的连接图如下:
为了实现全双工,数据传输需要使用两个DMA通道。以音频回放为例,数据传输先由内部总线送到内存, 然后传到DMA控制器通道1,再通过IIS控制器写入IIS总线并传输给音频芯片,通道2用来录音。
二、WAV文件
WAVE是录音时用的标准的Windows文件格式,文件的扩展名为“wav”,数据本身的格式为PCM或压缩型,属于无损音乐格式的一种,符合RIFF(Resource Interchange File Format)规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。数据块的记录方式是小端(little-endian)字节顺序,标志符并不是字符串而是单独的符号
以采样率为8kHz,量化位数为16,单通道的record.wav文件为例,文件前三行信息如下:
第一列表示地址,一行表示16个字节。
0x52,0x49,0x46,0x46 //“RIFF”4个字符对应的ASCII码值
0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20 //“WAVEfmt ”各个字符对应的ASCII码值
0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00//sizeof(PCMWAVEFORMAT)4Byte,格式类别2B,通道数1B(声道)
0x40,0x1F,0x00,0x00,0x80,0x3E,0x00,0x00, //采样频率0X0001F40= 8kHZ(8000Hz)4B,0x00003E80B/s=16kB/s 4B
0x02,0x00,0x10,0x00,0x64,0x61,0x74,0x61};//数据调整数0x0002(1*16/8)2B,即一个采样点所占的字节数,样本数据位数0x10(16位)2B,即一个采样点所表示的位数 “data”4B
地址000014H~000017H的值:2400 01 00即十六进制0x00010024,对应十进制65572(65536+36),表示从0x08开始到文件尾的总字节数;
地址000028H~00002BH的值:0080 0C 00即十六进制0x00010000,对应十进制65536,表示采样数据总数。
录音测试命令:cat /dev/sound/dsp > audio.wav
使用cat命令生成的audio.wav是一个PCM纯音频文件:
通过添加wav文件头,可以生成一个标准的wav音频文件:
三、Linux下实现录放音
注意驱动程序中的默认参数,应用程序可以通过ioctl()函数设置新的取值。
Open()函数:采用何种模式对声卡进行操作也必须在打开设备时指定,对于不支持全双工的声卡来说,应该使用只读或者只写的方式打开,只有那些支持全双工的声卡,才能以读写的方式打开,这还依赖于驱动程序的具体实现。open_mode有三种选择:O_RDONLY,O_WRONLY和O_RDWR,分别表示只读、只写和读写。OSS建议尽量使用只读或只写,只有在全双工的情况下(即录音和放音同时)才使用读写模式。Linux 允许应用程序多次打开或者关闭与声卡对应的设备文件,从而能够很方便地在放音状态和录音状态之间进行切换。
注意,用户始终要读/写一个完整的采样。例如一个16-bit的立体声模式下,每个采样有4个字节,所以应用程序每次必须读/写4的倍数个字节。
源码如下:
#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 44100 // 采样频率
#define SIZE 16 // 量化位数
#define CHANNELS 2 // 声道数目
/* 用于保存数字音频数据的内存缓冲区*/
unsigned charbuf[LENGTH*RATE*SIZE*CHANNELS/8];
int main()
{
intfd; // 声音设备的文件描述符
intarg; // 用于ioctl调用的参数
intstatus; // 系统调用的返回值
/*打开声音设备 */
fd= open("/dev/sound/dsp", O_RDONLY);
if(fd< 0)
{
perror("openof /dev/sound/dsp failed");
exit(1);
}
/*设置采样时的量化位数 */
arg= SIZE;
status= ioctl(fd, SOUND_PCM_WRITE_BITS, &arg);
if(status== -1)
perror("SOUND_PCM_WRITE_BITSioctl failed");
if(arg!= SIZE)
perror("unableto set sample size");
/*设置采样时的声道数目 */
arg= CHANNELS;
status= ioctl(fd, SNDCTL_DSP_STEREO, &arg);
if(status== -1)
perror("SNDCTL_DSP_STEREOioctl failed");
if(arg!= CHANNELS)
perror("unableto set number of channels");
/*设置采样时的采样频率 */
arg= RATE;
status= ioctl(fd, SNDCTL_DSP_SPEED, &arg);
if(status== -1)
perror("SNDCTL_DSP_SPEEDioctl failed");
if(arg!= RATE)
perror("unableto set rate");
printf("Saysomething:\n");
status= read(fd, buf, sizeof(buf)); //recording
if(status!= sizeof(buf))
perror("readwrong number of bytes");
printf("Yousaid:\n");
close(fd);
fd= open("/dev/sound/dsp", O_WRONLY);
if(fd< 0)
{
perror("openof /dev/sound/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 toset sample size");
/*设置采样时的声道数目 */
arg = CHANNELS;
status = ioctl(fd, SNDCTL_DSP_STEREO,&arg);
if(status == -1)
perror("SNDCTL_DSP_STEREO ioctl failed");
if(arg != CHANNELS)
perror("unable toset number of channels");
/* 设置采样时的采样频率 */
arg = RATE;
status = ioctl(fd,SNDCTL_DSP_SPEED, &arg);
if(status == -1)
perror("SNDCTL_DSP_SPEED ioctl failed");
if(arg != RATE)
perror("unable toset rate");
status= write(fd, buf, sizeof(buf)); //playing
if(status!= sizeof(buf))
perror("wrotewrong number of bytes");
close(fd);
return0;
}
程序中的注意点是open()函数的参数设置,之前采用参数O_RDWR,结果放音时总是出现错误,具体出错原因可能与驱动程序的设置有关。本设计中,正确的设置是当recording时,选择O_RDONLY,当playing时,选择O_WRONLY。
(责任编辑:IT)
一、原理简述 在Linux下,录音——从dsp设备读取数据,放音——向dsp设备写入数据。 开发板采用声卡UDA1341实现音频编解码,完成A/D和D/A转换,芯片UDA1341与CPU的连接图如下:
为了实现全双工,数据传输需要使用两个DMA通道。以音频回放为例,数据传输先由内部总线送到内存, 然后传到DMA控制器通道1,再通过IIS控制器写入IIS总线并传输给音频芯片,通道2用来录音。 二、WAV文件 WAVE是录音时用的标准的Windows文件格式,文件的扩展名为“wav”,数据本身的格式为PCM或压缩型,属于无损音乐格式的一种,符合RIFF(Resource Interchange File Format)规范。所有的WAV都有一个文件头,这个文件头音频流的编码参数。数据块的记录方式是小端(little-endian)字节顺序,标志符并不是字符串而是单独的符号
以采样率为8kHz,量化位数为16,单通道的record.wav文件为例,文件前三行信息如下:
第一列表示地址,一行表示16个字节。 0x52,0x49,0x46,0x46 //“RIFF”4个字符对应的ASCII码值 0x57,0x41,0x56,0x45,0x66,0x6D,0x74,0x20 //“WAVEfmt ”各个字符对应的ASCII码值 0x10,0x00,0x00,0x00,0x01,0x00,0x01,0x00//sizeof(PCMWAVEFORMAT)4Byte,格式类别2B,通道数1B(声道) 0x40,0x1F,0x00,0x00,0x80,0x3E,0x00,0x00, //采样频率0X0001F40= 8kHZ(8000Hz)4B,0x00003E80B/s=16kB/s 4B 0x02,0x00,0x10,0x00,0x64,0x61,0x74,0x61};//数据调整数0x0002(1*16/8)2B,即一个采样点所占的字节数,样本数据位数0x10(16位)2B,即一个采样点所表示的位数 “data”4B 地址000014H~000017H的值:2400 01 00即十六进制0x00010024,对应十进制65572(65536+36),表示从0x08开始到文件尾的总字节数; 地址000028H~00002BH的值:0080 0C 00即十六进制0x00010000,对应十进制65536,表示采样数据总数。 录音测试命令:cat /dev/sound/dsp > audio.wav 使用cat命令生成的audio.wav是一个PCM纯音频文件:
通过添加wav文件头,可以生成一个标准的wav音频文件:
三、Linux下实现录放音 注意驱动程序中的默认参数,应用程序可以通过ioctl()函数设置新的取值。
Open()函数:采用何种模式对声卡进行操作也必须在打开设备时指定,对于不支持全双工的声卡来说,应该使用只读或者只写的方式打开,只有那些支持全双工的声卡,才能以读写的方式打开,这还依赖于驱动程序的具体实现。open_mode有三种选择:O_RDONLY,O_WRONLY和O_RDWR,分别表示只读、只写和读写。OSS建议尽量使用只读或只写,只有在全双工的情况下(即录音和放音同时)才使用读写模式。Linux 允许应用程序多次打开或者关闭与声卡对应的设备文件,从而能够很方便地在放音状态和录音状态之间进行切换。 注意,用户始终要读/写一个完整的采样。例如一个16-bit的立体声模式下,每个采样有4个字节,所以应用程序每次必须读/写4的倍数个字节。 源码如下:
#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 44100 // 采样频率 #define SIZE 16 // 量化位数 #define CHANNELS 2 // 声道数目 /* 用于保存数字音频数据的内存缓冲区*/ unsigned charbuf[LENGTH*RATE*SIZE*CHANNELS/8]; int main() { intfd; // 声音设备的文件描述符 intarg; // 用于ioctl调用的参数 intstatus; // 系统调用的返回值 /*打开声音设备 */ fd= open("/dev/sound/dsp", O_RDONLY); if(fd< 0) { perror("openof /dev/sound/dsp failed"); exit(1); } /*设置采样时的量化位数 */ arg= SIZE; status= ioctl(fd, SOUND_PCM_WRITE_BITS, &arg); if(status== -1) perror("SOUND_PCM_WRITE_BITSioctl failed"); if(arg!= SIZE) perror("unableto set sample size"); /*设置采样时的声道数目 */ arg= CHANNELS; status= ioctl(fd, SNDCTL_DSP_STEREO, &arg); if(status== -1) perror("SNDCTL_DSP_STEREOioctl failed"); if(arg!= CHANNELS) perror("unableto set number of channels"); /*设置采样时的采样频率 */ arg= RATE; status= ioctl(fd, SNDCTL_DSP_SPEED, &arg); if(status== -1) perror("SNDCTL_DSP_SPEEDioctl failed"); if(arg!= RATE) perror("unableto set rate"); printf("Saysomething:\n"); status= read(fd, buf, sizeof(buf)); //recording if(status!= sizeof(buf)) perror("readwrong number of bytes"); printf("Yousaid:\n"); close(fd); fd= open("/dev/sound/dsp", O_WRONLY); if(fd< 0) { perror("openof /dev/sound/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 toset sample size"); /*设置采样时的声道数目 */ arg = CHANNELS; status = ioctl(fd, SNDCTL_DSP_STEREO,&arg); if(status == -1) perror("SNDCTL_DSP_STEREO ioctl failed"); if(arg != CHANNELS) perror("unable toset number of channels"); /* 设置采样时的采样频率 */ arg = RATE; status = ioctl(fd,SNDCTL_DSP_SPEED, &arg); if(status == -1) perror("SNDCTL_DSP_SPEED ioctl failed"); if(arg != RATE) perror("unable toset rate"); status= write(fd, buf, sizeof(buf)); //playing if(status!= sizeof(buf)) perror("wrotewrong number of bytes"); close(fd); return0; } 程序中的注意点是open()函数的参数设置,之前采用参数O_RDWR,结果放音时总是出现错误,具体出错原因可能与驱动程序的设置有关。本设计中,正确的设置是当recording时,选择O_RDONLY,当playing时,选择O_WRONLY。 (责任编辑:IT) |