嵌入式音频编程——alsa库使用

2019-07-13 01:47发布

0 资料库:alsa编程基本思路及编程框架 1 嵌入式上层音频编程原理:上层开发------>> 调用中间层(alsa库)<<------驱动开发关联中间层 <<------硬件。 所以只需遵循alsa库规则开发上层应用程序,在任何一个遵循alsa库开发的驱动关联的硬件,该应用程序都可以移植到该平台,我们的重新如何使用alsa库开发自己想要的东西。 框架 2 alsa 库安装: (1)PC 版: 下载alsa库:alsa库安装包 tar -xf alsa-lib-1.1.5.tar.bz2     (切记,不要使用sudo 权限解压,否则后面交叉编译时会出现问题) 配置生成Makefile文件 gec@gec-ubuntu:~/alsa-lib-1.1.5$ ./configure gec@gec-ubuntu:~/alsa-lib-1.1.5$ make gec@gec-ubuntu:~/alsa-lib-1.1.5$ sudo make install       --安装 编译程序:(一般需要在PC测试源程序,后在交叉编译移植到开发板) gcc -o  record    record.c    -lasound (2)ARM版 交叉编译配置 gec@gec-ubuntu:~/alsa-lib-1.1.5$ make clean gec@gec-ubuntu:~/alsa-lib-1.1.5$ ./configure     --prefix=/opt/alsa         --host=arm-linux (prefix:指定安装路径,这个有个技巧:交叉编译后,这个库也要移植到开发板上,为了方便移植,我们一般安装指定在一个路径下, 在开发上建立同样的路径存放,在添加环境变量即可;host:指定运用平台,这个必须的) gec@gec-ubuntu:~/alsa-lib-1.1.5$ make gec@gec-ubuntu:~/alsa-lib-1.1.5$ sudo make install   --安装 交叉编译 gec@gec-ubuntu:/mnt/hgfs/aud_video/code$ arm-linux-gcc -o armrecord alsa_record.c  -I/opt/alsa/include   -L/opt/alsa/lib   -lasound (asound:表示库) 移植库 /opt/ 下面的alsa拷贝开发板对应/opt目录下 gec@gec-ubuntu:/opt$ sudo  tar  -jcf  alsa.tar.bz2       alsa/ 生成:alsa.tar.bz2,把alsa.tar.bz2下载到开发板/opt目录下并且解压 设置环境变量 /etc/profile 文件中添加:export  LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/opt/alsa/lib source /etc/profile  ---生效 3 程序案列:录音通过UDP发送给播放器播放 (1)播放器: #include #include #include #include #include #include #include int main(int argc, char **argv) { snd_pcm_t *handle;//pcm句柄 snd_pcm_hw_params_t *params;//pcm属性 //打开设备 int r = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK,0); if(r < 0) { perror("snd pcm open fail"); return -1; } //设置参数 //初始化pcm属性 snd_pcm_hw_params_alloca(¶ms); snd_pcm_hw_params_any(handle, params); //交错模式--- snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); //设置双声道,小端格式,16位 snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_channels(handle, params, 2); //设置采样率(44100标准MP3采样频率) int val = 44100; snd_pcm_hw_params_set_rate_near(handle,params,&val,0); //设在采样周期,(最好是让系统自动设置,这一步可以省略) int frames; //snd_pcm_hw_params_set_period_size_near(handle,params,(snd_pcm_uframes_t*)&frames,0); //设置好的参数回写设备 r = snd_pcm_hw_params(handle, params); if(r < 0) { perror("snd pcm params fail"); return -1; } //16--2--(一帧数据4个字节) //获取一个周期有多少帧数据,一个周期一个周期方式处理音频数据。 snd_pcm_hw_params_get_period_size(params,(snd_pcm_uframes_t*)&frames,0); unsigned char *buffer = malloc(4*frames);//由于双通道,16bit,每个通道2个字节,一个周期所需要的空间为4个字节*帧数 //初始化网络 int sockfd = socket (AF_INET, SOCK_DGRAM, 0); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(atoi(argv[1]));//端口号,比如9000 addr.sin_addr.s_addr = htonl(INADDR_ANY);//本地IP //绑定 int bret = bind(sockfd, (struct sockaddr*)&addr, sizeof(addr)); if(bret < 0) { perror("bind fail"); exit(1); } while(1) { //接收数据 bret = recvfrom(sockfd, buffer, frames*4, 0, NULL, NULL); if(bret <= 0){break;} printf("recv:%d ", bret); snd_pcm_writei(handle,buffer,frames); } close(sockfd); //关闭 snd_pcm_drain(handle); snd_pcm_close(handle); free(buffer); return 0; } (2)录音器 #include #include #include #include #include #include int main(int argc, char **argv) { snd_pcm_t *handle;//pcm句柄 snd_pcm_hw_params_t *params;//pcm属性 //打开设备 int r = snd_pcm_open(&handle, "default", SND_PCM_STREAM_CAPTURE,0); if(r < 0) { perror("open fail"); return -1; } //设置参数 //初始化pcm属性 snd_pcm_hw_params_alloca(¶ms); snd_pcm_hw_params_any(handle, params); //交错模式--- snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); //设置双声道,小端格式,16位 snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); snd_pcm_hw_params_set_channels(handle, params, 2); //设置采样率 int val = 44100; snd_pcm_hw_params_set_rate_near(handle,params,&val,0); //设在采样周期 int frames; //snd_pcm_hw_params_set_period_size_near(handle,params,(snd_pcm_uframes_t*)&frames,0); //设置好的参数回写设备 r = snd_pcm_hw_params(handle, params); if(r < 0) { perror("set params fail"); return -1; } //16--2--(一帧数据4个字节) //获取一个周期有多少帧数据 snd_pcm_hw_params_get_period_size(params,(snd_pcm_uframes_t*)&frames,0); snd_pcm_hw_params_get_rate(params,&val,0); printf("frames=%d, rate=%d ", frames, val); unsigned char *buffer = malloc(4*frames); //初始化网络 int sockfd = socket(AF_INET, SOCK_DGRAM, 0); struct sockaddr_in addr; memset(&addr, 0, sizeof(addr)); addr.sin_family = AF_INET; addr.sin_port = htons(atoi(argv[2]));//服务器的端口号 addr.sin_addr.s_addr = inet_addr(argv[1]);//服务器IP int ret = 0; while(1) { //录音---返回帧数 ret = snd_pcm_readi(handle,buffer,frames); if(ret != frames) { snd_pcm_prepare(handle); continue; } //udp发送-- ret = sendto(sockfd, buffer, frames*4, 0, (struct sockaddr*)&addr, sizeof(addr)); if(ret != frames*4) { break; } } close(sockfd); //关闭 snd_pcm_drain(handle); snd_pcm_close(handle); free(buffer); return 0; } 4 知识拓展 1. 可以调用Fmpeg库对音频进行编程。 2.alsa 库产生的音频数据是很大,必须经过压缩再存储、传输。