1. 程式人生 > >嵌入式音訊程式設計——alsa庫使用

嵌入式音訊程式設計——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檔案

[email protected]:~/alsa-lib-1.1.5$ ./configure

[email protected]:~/alsa-lib-1.1.5$ make

[email protected]:~/alsa-lib-1.1.5$ sudo make install       --安裝

編譯程式:(一般需要在PC測試源程式,後在交叉編譯移植到開發板)

gcc -o  record    record.c    -lasound

(2)ARM版

交叉編譯配置

[email protected]:~/alsa-lib-1.1.5$ make clean

[email protected]:~/alsa-lib-1.1.5$ ./configure     --prefix=/opt/alsa         --host=arm-linux

(prefix:指定安裝路徑,這個有個技巧:交叉編譯後,這個庫也要移植到開發板上,為了方便移植,我們一般安裝指定在一個路徑下,

在開發上建立同樣的路徑存放,在新增環境變數即可;host:指定運用平臺,這個必須的)

[email protected]:~/alsa-lib-1.1.5$ make

[email protected]:~/alsa-lib-1.1.5$ sudo make install   --安裝

交叉編譯

[email protected]:/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目錄下

[email protected]:/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 庫產生的音訊資料是很大,必須經過壓縮再儲存、傳輸。