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

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 <stdio.h>
#include <alsa/asoundlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <arpa/inet.h>

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(&params);
	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\n", bret);
		snd_pcm_writei(handle,buffer,frames);
	}

	close(sockfd);
	//关闭
	snd_pcm_drain(handle);
	snd_pcm_close(handle);
	free(buffer);
	return 0;
}

(2)录音器

#include <stdio.h>
#include <alsa/asoundlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include <arpa/inet.h>


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(&params);
	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\n", 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 库产生的音频数据是很大,必须经过压缩再存储、传输。

 

 

 

猜你喜欢

转载自blog.csdn.net/wghkemo123/article/details/82152473
今日推荐