首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >嵌入式Linux下音频开发: alsa-lib实现声音数据捕获保存与播放

嵌入式Linux下音频开发: alsa-lib实现声音数据捕获保存与播放

作者头像
DS小龙哥
发布2022-01-12 10:53:14
发布2022-01-12 10:53:14
6.9K0
举报

一、环境介绍

宿主机: Redhat6.3 (32位Linux系统)

目标开发板: 友善之臂Tiny4412 ---三星EXYNOS4412

交叉编译器: arm-linux-gcc 4.5.1

Linux内核: Linux3.5

二、下载alsa-lib库

项目主页下载地址:https://www.alsa-project.org/wiki/Main_Page

FTP服务器下载地址(可下载历史版本): ftp://ftp.alsa-project.org/pub/lib/

三、编译移植过程

下载源码包之后,在宿主机上解压编译安装。

代码语言:javascript
复制
[wbyq@wbyq work]$ tar xvf /mnt/hgfs/linux-share-dir/alsa-lib-1.2.3.2.tar.bz2 
[wbyq@wbyq work]$ cd alsa-lib-1.2.3.2/
[wbyq@wbyq alsa-lib-1.2.3.2]$ ./configure --host=arm-linux --prefix=$PWD/_install --enable-shared
[wbyq@wbyq alsa-lib-1.2.3.2]$ make && make install
[wbyq@wbyq alsa-lib-1.2.3.2]$ tree _install/
_install/
├── bin
│   └── aserver
├── include
│   ├── alsa
│   │   ├── asoundef.h
│   │   ├── asoundlib.h
│   │   ├── conf.h
│   │   ├── control_external.h
│   │   ├── control.h
│   │   ├── error.h
│   │   ├── global.h
│   │   ├── hwdep.h
│   │   ├── input.h
│   │   ├── mixer_abst.h
│   │   ├── mixer.h
│   │   ├── output.h
│   │   ├── pcm_external.h
│   │   ├── pcm_extplug.h
│   │   ├── pcm.h
│   │   ├── pcm_ioplug.h
│   │   ├── pcm_old.h
│   │   ├── pcm_plugin.h
│   │   ├── pcm_rate.h
│   │   ├── rawmidi.h
│   │   ├── seq_event.h
│   │   ├── seq.h
│   │   ├── seqmid.h
│   │   ├── seq_midi_event.h
│   │   ├── sound
│   │   │   ├── asoc.h
│   │   │   ├── asound_fm.h
│   │   │   ├── emu10k1.h
│   │   │   ├── hdsp.h
│   │   │   ├── hdspm.h
│   │   │   ├── sb16_csp.h
│   │   │   ├── sscape_ioctl.h
│   │   │   ├── tlv.h
│   │   │   ├── type_compat.h
│   │   │   └── uapi
│   │   │       ├── asoc.h
│   │   │       ├── asound_fm.h
│   │   │       ├── emu10k1.h
│   │   │       ├── hdsp.h
│   │   │       ├── hdspm.h
│   │   │       ├── sb16_csp.h
│   │   │       ├── sscape_ioctl.h
│   │   │       └── tlv.h
│   │   ├── timer.h
│   │   ├── topology.h
│   │   ├── use-case.h
│   │   └── version.h
│   ├── asoundlib.h
│   └── sys
│       └── asoundlib.h
├── lib
│   ├── libasound.la
│   ├── libasound.so -> libasound.so.2.0.0
│   ├── libasound.so.2 -> libasound.so.2.0.0
│   ├── libasound.so.2.0.0
│   ├── libatopology.la
│   ├── libatopology.so -> libatopology.so.2.0.0
│   ├── libatopology.so.2 -> libatopology.so.2.0.0
│   ├── libatopology.so.2.0.0
│   └── pkgconfig
│       ├── alsa.pc
│       └── alsa-topology.pc
└── share
    ├── aclocal
    │   └── alsa.m4
    └── alsa
        ├── alsa.conf
        ├── cards
        │   ├── AACI.conf
        │   ├── aliases.conf
        │   ├── ATIIXP.conf
        │   ├── ATIIXP-MODEM.conf
        │   ├── ATIIXP-SPDMA.conf
        │   ├── AU8810.conf
        │   ├── AU8820.conf
        │   ├── AU8830.conf
        │   ├── Audigy2.conf
        │   ├── Audigy.conf
        │   ├── Aureon51.conf
        │   ├── Aureon71.conf
        │   ├── CA0106.conf
        │   ├── CMI8338.conf
        │   ├── CMI8338-SWIEC.conf
        │   ├── CMI8738-MC6.conf
        │   ├── CMI8738-MC8.conf
        │   ├── CMI8788.conf
        │   ├── CS46xx.conf
        │   ├── Echo_Echo3G.conf
        │   ├── EMU10K1.conf
        │   ├── EMU10K1X.conf
        │   ├── ENS1370.conf
        │   ├── ENS1371.conf
        │   ├── ES1968.conf
        │   ├── FireWave.conf
        │   ├── FM801.conf
        │   ├── FWSpeakers.conf
        │   ├── GUS.conf
        │   ├── HDA-Intel.conf
        │   ├── HdmiLpeAudio.conf
        │   ├── ICE1712.conf
        │   ├── ICE1724.conf
        │   ├── ICH4.conf
        │   ├── ICH.conf
        │   ├── ICH-MODEM.conf
        │   ├── Loopback.conf
        │   ├── Maestro3.conf
        │   ├── NFORCE.conf
        │   ├── PC-Speaker.conf
        │   ├── pistachio-card.conf
        │   ├── PMac.conf
        │   ├── PMacToonie.conf
        │   ├── PS3.conf
        │   ├── RME9636.conf
        │   ├── RME9652.conf
        │   ├── SB-XFi.conf
        │   ├── SI7018.conf
        │   ├── TRID4DWAVENX.conf
        │   ├── USB-Audio.conf
        │   ├── vc4-hdmi.conf
        │   ├── VIA686A.conf
        │   ├── VIA8233A.conf
        │   ├── VIA8233.conf
        │   ├── VIA8237.conf
        │   ├── VX222.conf
        │   ├── VXPocket440.conf
        │   ├── VXPocket.conf
        │   └── YMF744.conf
        └── pcm
            ├── center_lfe.conf
            ├── default.conf
            ├── dmix.conf
            ├── dpl.conf
            ├── dsnoop.conf
            ├── front.conf
            ├── hdmi.conf
            ├── iec958.conf
            ├── modem.conf
            ├── rear.conf
            ├── side.conf
            ├── surround21.conf
            ├── surround40.conf
            ├── surround41.conf
            ├── surround50.conf
            ├── surround51.conf
            └── surround71.conf

13 directories, 136 files
[wbyq@wbyq alsa-lib-1.2.3.2]$

编译成功后

(1) 将_install/lib目录下的动态库文件拷贝到目标板的lib目录下。

(2) 在目标开发板的根目录下安装当前宿主机上的安装路径创建一个目录,将_install/share目录拷贝到目标板创建的目录下。

比如: 当前我PC机上的安装路径是: /home/wbyq/work/alsa-lib-1.2.3.2/_install ,那么就在目标开发板上创建一个一模一样的路径。

(3) 将_install/bin目录下的可执行文件拷贝到目标板的bin目录下。

具体拷贝过程如下:

代码语言:javascript
复制
[wbyq@wbyq alsa-lib-1.2.3.2]$ sudo cp _install/share /home/wbyq/work/rootfs/home/wbyq/work/alsa-lib-1.2.3.2/_install/ -rf

[wbyq@wbyq alsa-lib-1.2.3.2]$ cp _install/lib/* /home/wbyq/work/rootfs/lib/ -rf

[wbyq@wbyq alsa-lib-1.2.3.2]$ cp _install/bin/* /home/wbyq/work/rootfs/bin/

四、测试代码

4.1 安装声卡驱动

注意: 开发板上需要先安装声卡驱动。

下面是Tiny4412上安装声卡驱动的脚本。

4.2 编译代码的Makefile文件

代码语言:javascript
复制
app:
	arm-linux-gcc linux_pcm_save.c -o app -lasound -I/home/wbyq/work/alsa-lib-1.2.3.2/_install/include -L/home/wbyq/work/alsa-lib-1.2.3.2/_install/lib
	cp app /home/wbyq/work/rootfs/code -fv
	rm app -f

4.3 采集声音数据保存为PCM文件

(0). 源代码(双声道采集示例)

代码语言:javascript
复制
/*
 进行音频采集,采集pcm数据并直接保存pcm数据
 音频参数: 
	 声道数:		2
	 采样位数:	16bit、LE格式
	 采样频率:	44100Hz
	 gcc linux_pcm_save.c -lasound
*/
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>

FILE *pcm_data_file=NULL;
int run_flag=0;
void exit_sighandler(int sig)
{
	run_flag=1;
}

int main(int argc, char *argv[])
{
	int i;
	int err;
	char *buffer;
	int buffer_frames = 128;
	unsigned int rate = 44100;// 常用的采样频率: 44100Hz 、16000HZ、8000HZ、48000HZ、22050HZ
	snd_pcm_t *capture_handle;// 一个指向PCM设备的句柄
	snd_pcm_hw_params_t *hw_params; //此结构包含有关硬件的信息,可用于指定PCM流的配置
	
	/*注册信号捕获退出接口*/
	signal(2,exit_sighandler);

	/*PCM的采样格式在pcm.h文件里有定义*/
	snd_pcm_format_t format=SND_PCM_FORMAT_S16_LE; // 采样位数:16bit、LE格式

	/*打开音频采集卡硬件,并判断硬件是否打开成功,若打开失败则打印出错误提示*/
	if ((err = snd_pcm_open (&capture_handle, argv[1],SND_PCM_STREAM_CAPTURE,0))<0) 
	{
		printf("无法打开音频设备: %s (%s)\n",  argv[1],snd_strerror (err));
		exit(1);
	}
	printf("音频接口打开成功.\n");

	/*创建一个保存PCM数据的文件*/
	if((pcm_data_file = fopen(argv[2], "wb")) == NULL)
	{
		printf("无法创建%s音频文件.\n",argv[2]);
		exit(1);
	} 
	printf("用于录制的音频文件已打开.\n");

	/*分配硬件参数结构对象,并判断是否分配成功*/
	if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) 
	{
		printf("无法分配硬件参数结构 (%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("硬件参数结构已分配成功.\n");
	
	/*按照默认设置对硬件对象进行设置,并判断是否设置成功*/
	if((err=snd_pcm_hw_params_any(capture_handle,hw_params)) < 0) 
	{
		printf("无法初始化硬件参数结构 (%s)\n", snd_strerror(err));
		exit(1);
	}
	printf("硬件参数结构初始化成功.\n");

	/*
		设置数据为交叉模式,并判断是否设置成功
		interleaved/non interleaved:交叉/非交叉模式。
		表示在多声道数据传输的过程中是采样交叉的模式还是非交叉的模式。
		对多声道数据,如果采样交叉模式,使用一块buffer即可,其中各声道的数据交叉传输;
		如果使用非交叉模式,需要为各声道分别分配一个buffer,各声道数据分别传输。
	*/
	if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) 
	{
		printf("无法设置访问类型(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("访问类型设置成功.\n");

	/*设置数据编码格式,并判断是否设置成功*/
	if ((err=snd_pcm_hw_params_set_format(capture_handle, hw_params,format)) < 0) 
	{
		printf("无法设置格式 (%s)\n",snd_strerror(err));
		exit(1);
	}
	fprintf(stdout, "PCM数据格式设置成功.\n");

	/*设置采样频率,并判断是否设置成功*/
	if((err=snd_pcm_hw_params_set_rate_near (capture_handle,hw_params,&rate,0))<0) 
	{
		printf("无法设置采样率(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("采样率设置成功\n");

	/*设置声道,并判断是否设置成功*/
	if((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params,2)) < 0) 
	{
		printf("无法设置声道数(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("声道数设置成功.\n");

	/*将配置写入驱动程序中,并判断是否配置成功*/
	if ((err=snd_pcm_hw_params (capture_handle,hw_params))<0) 
	{
		printf("无法向驱动程序设置参数(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("参数设置成功.\n");

	/*使采集卡处于空闲状态*/
	snd_pcm_hw_params_free(hw_params);

	/*准备音频接口,并判断是否准备好*/
	if((err=snd_pcm_prepare(capture_handle))<0) 
	{
		printf("无法使用音频接口 (%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("音频接口准备好.\n");

	/*配置一个数据缓冲区用来缓冲数据*/
	buffer=malloc(128*snd_pcm_format_width(format)/8*2);
	printf("缓冲区分配成功.\n");

	/*开始采集音频pcm数据*/
	printf("开始采集数据...\n");
	while(1) 
	{
		/*从声卡设备读取一帧音频数据*/
		if((err=snd_pcm_readi(capture_handle,buffer,buffer_frames))!=buffer_frames) 
		{
			  printf("从音频接口读取失败(%s)\n",snd_strerror(err));
			  exit(1);
		}
		/*写数据到文件*/
		fwrite(buffer,(buffer_frames*2),sizeof(short),pcm_data_file);
		
		if(run_flag)
		{
			printf("停止采集.\n");
			break;
		}
	}

	/*释放数据缓冲区*/
	free(buffer);

	/*关闭音频采集卡硬件*/
	snd_pcm_close(capture_handle);

	/*关闭文件流*/
	fclose(pcm_data_file);
	return 0;
}

(1). 源代码(单声道采集示例)

代码语言:javascript
复制
/*
 进行音频采集,采集pcm数据并直接保存pcm数据
 音频参数: 
	 声道数:		1
	 采样位数:	16bit、LE格式
	 采样频率:	44100Hz
运行示例:
$ gcc linux_pcm_save.c -lasound
$ ./a.out hw:0 123.pcm
*/
 
#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>
 
#define AudioFormat SND_PCM_FORMAT_S16_LE  //指定音频的格式,其他常用格式:SND_PCM_FORMAT_U24_LE、SND_PCM_FORMAT_U32_LE
#define AUDIO_CHANNEL_SET   1  			  //1单声道   2立体声
#define AUDIO_RATE_SET 44100   //音频采样率,常用的采样频率: 44100Hz 、16000HZ、8000HZ、48000HZ、22050HZ
 
FILE *pcm_data_file=NULL;
int run_flag=0;
void exit_sighandler(int sig)
{
	run_flag=1;
}

int main(int argc, char *argv[])
{
	if(argc!=3)
	{
		printf("./a.out hw:0 123.pcm \n");
		return 0;
	}
	int i;
	int err;
	char *buffer;
	int buffer_frames = 1024;
	unsigned int rate = AUDIO_RATE_SET;
	snd_pcm_t *capture_handle;// 一个指向PCM设备的句柄
	snd_pcm_hw_params_t *hw_params; //此结构包含有关硬件的信息,可用于指定PCM流的配置
	
	/*注册信号捕获退出接口*/
	signal(2,exit_sighandler);
 
	/*PCM的采样格式在pcm.h文件里有定义*/
	snd_pcm_format_t format=AudioFormat; // 采样位数:16bit、LE格式
 
	/*打开音频采集卡硬件,并判断硬件是否打开成功,若打开失败则打印出错误提示*/
	// SND_PCM_STREAM_PLAYBACK 输出流
	// SND_PCM_STREAM_CAPTURE  输入流
	if ((err = snd_pcm_open (&capture_handle, argv[1],SND_PCM_STREAM_CAPTURE,0))<0) 
	{
		printf("无法打开音频设备: %s (%s)\n",  argv[1],snd_strerror (err));
		exit(1);
	}
	printf("音频接口打开成功.\n");
 
	/*创建一个保存PCM数据的文件*/
	if((pcm_data_file = fopen(argv[2], "wb")) == NULL)
	{
		printf("无法创建%s音频文件.\n",argv[2]);
		exit(1);
	} 
	printf("用于录制的音频文件已打开.\n");
 
	/*分配硬件参数结构对象,并判断是否分配成功*/
	if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) 
	{
		printf("无法分配硬件参数结构 (%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("硬件参数结构已分配成功.\n");
	
	/*按照默认设置对硬件对象进行设置,并判断是否设置成功*/
	if((err=snd_pcm_hw_params_any(capture_handle,hw_params)) < 0) 
	{
		printf("无法初始化硬件参数结构 (%s)\n", snd_strerror(err));
		exit(1);
	}
	printf("硬件参数结构初始化成功.\n");
 
	/*
		设置数据为交叉模式,并判断是否设置成功
		interleaved/non interleaved:交叉/非交叉模式。
		表示在多声道数据传输的过程中是采样交叉的模式还是非交叉的模式。
		对多声道数据,如果采样交叉模式,使用一块buffer即可,其中各声道的数据交叉传输;
		如果使用非交叉模式,需要为各声道分别分配一个buffer,各声道数据分别传输。
	*/
	if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) 
	{
		printf("无法设置访问类型(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("访问类型设置成功.\n");
 
	/*设置数据编码格式,并判断是否设置成功*/
	if ((err=snd_pcm_hw_params_set_format(capture_handle, hw_params,format)) < 0) 
	{
		printf("无法设置格式 (%s)\n",snd_strerror(err));
		exit(1);
	}
	fprintf(stdout, "PCM数据格式设置成功.\n");
 
	/*设置采样频率,并判断是否设置成功*/
	if((err=snd_pcm_hw_params_set_rate_near(capture_handle,hw_params,&rate,0))<0) 
	{
		printf("无法设置采样率(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("采样率设置成功\n");
 
	/*设置声道,并判断是否设置成功*/
	if((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params,AUDIO_CHANNEL_SET)) < 0) 
	{
		printf("无法设置声道数(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("声道数设置成功.\n");
 
	/*将配置写入驱动程序中,并判断是否配置成功*/
	if ((err=snd_pcm_hw_params (capture_handle,hw_params))<0) 
	{
		printf("无法向驱动程序设置参数(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("参数设置成功.\n");
 
	/*使采集卡处于空闲状态*/
	snd_pcm_hw_params_free(hw_params);
 
	/*准备音频接口,并判断是否准备好*/
	if((err=snd_pcm_prepare(capture_handle))<0) 
	{
		printf("无法使用音频接口 (%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("音频接口准备好.\n");
 
	/*配置一个数据缓冲区用来缓冲数据*/
	//snd_pcm_format_width(format) 获取样本格式对应的大小(单位是:bit)
	int frame_byte=snd_pcm_format_width(format)/8;
	buffer=malloc(buffer_frames*frame_byte*AUDIO_CHANNEL_SET); //2048
	printf("缓冲区分配成功.\n");
	
	/*开始采集音频pcm数据*/
	printf("开始采集数据...\n");
	while(1) 
	{
		/*从声卡设备读取一帧音频数据:2048字节*/
		if((err=snd_pcm_readi(capture_handle,buffer,buffer_frames))!=buffer_frames) 
		{
			  printf("从音频接口读取失败(%s)\n",snd_strerror(err));
			  exit(1);
		}
		/*写数据到文件: 音频的每帧数据样本大小是16位=2个字节*/
		fwrite(buffer,(buffer_frames*AUDIO_CHANNEL_SET),frame_byte,pcm_data_file);	
		if(run_flag)
		{
			printf("停止采集.\n");
			break;
		}
	}
 
	/*释放数据缓冲区*/
	free(buffer);
 
	/*关闭音频采集卡硬件*/
	snd_pcm_close(capture_handle);
 
	/*关闭文件流*/
	fclose(pcm_data_file);
	return 0;
}

(2). 运行过程

代码语言:javascript
复制
[root@wbyq code]# ./app hw:0 123.pcm
音频接口打开成功.
用于录制的音频文件已打开.
硬件参数结构已分配成功.
硬件参数结构初始化成功.
访问类型设置成功.
PCM数据格式设置成功.
采样率设置成功
声道数设置成功.
参数设置成功.
音频接口准备好.
缓冲区分配成功.
开始采集数据...

(3). 播放采集的声音: 使用windows下的audacity.exe 软件。

下载地址: https://download.csdn.net/download/xiaolong1126626497/12252685

4.4 播放PCM格式数据

(1)双声道播放(播放使用双声道采集的音频数据)

代码语言:javascript
复制
/*
 进行音频采集,读取存放pcm数据的文件通过声卡进行播放
 音频参数: 
	 声道数:		2
	 采样位数:	16bit、LE格式
	 采样频率:	44100Hz
	 
运行示例:
$ gcc linux_pcm_save.c -lasound
$ ./a.out hw:0 123.pcm
*/

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <signal.h>

#define AudioFormat SND_PCM_FORMAT_S16_LE  //指定音频的格式,其他常用格式:SND_PCM_FORMAT_U24_LE、SND_PCM_FORMAT_U32_LE
#define AUDIO_CHANNEL_SET   2  			  //1单声道   2立体声
#define AUDIO_RATE_SET 44100   //音频采样率,常用的采样频率: 44100Hz 、16000HZ、8000HZ、48000HZ、22050HZ

FILE *pcm_data_file=NULL;
int run_flag=0;
void exit_sighandler(int sig)
{
	run_flag=1;
}


//
int main(int argc, char *argv[])
{
	int i;
	int err;
	char *buffer;
	int buffer_frames = 1024;
	unsigned int rate = AUDIO_RATE_SET;
	snd_pcm_t *capture_handle;// 一个指向PCM设备的句柄
	snd_pcm_hw_params_t *hw_params; //此结构包含有关硬件的信息,可用于指定PCM流的配置
	
	/*注册信号捕获退出接口*/
	signal(2,exit_sighandler);

	/*PCM的采样格式在pcm.h文件里有定义*/
	snd_pcm_format_t format=AudioFormat; // 采样位数:16bit、LE格式

	/*打开音频采集卡硬件,并判断硬件是否打开成功,若打开失败则打印出错误提示*/
	// SND_PCM_STREAM_PLAYBACK 输出流
	// SND_PCM_STREAM_CAPTURE  输入流
	if ((err = snd_pcm_open (&capture_handle, argv[1],SND_PCM_STREAM_PLAYBACK,0))<0) 
	{
		printf("无法打开音频设备: %s (%s)\n",  argv[1],snd_strerror (err));
		exit(1);
	}
	printf("音频接口打开成功.\n");

	/*打开存放PCM数据的文件*/
	if((pcm_data_file = fopen(argv[2], "rb")) == NULL)
	{
		printf("无法打开%s音频文件.\n",argv[2]);
		exit(1);
	} 
	printf("用于播放的音频文件已打开.\n");

	/*分配硬件参数结构对象,并判断是否分配成功*/
	if((err = snd_pcm_hw_params_malloc(&hw_params)) < 0) 
	{
		printf("无法分配硬件参数结构 (%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("硬件参数结构已分配成功.\n");
	
	/*按照默认设置对硬件对象进行设置,并判断是否设置成功*/
	if((err=snd_pcm_hw_params_any(capture_handle,hw_params)) < 0) 
	{
		printf("无法初始化硬件参数结构 (%s)\n", snd_strerror(err));
		exit(1);
	}
	printf("硬件参数结构初始化成功.\n");

	/*
		设置数据为交叉模式,并判断是否设置成功
		interleaved/non interleaved:交叉/非交叉模式。
		表示在多声道数据传输的过程中是采样交叉的模式还是非交叉的模式。
		对多声道数据,如果采样交叉模式,使用一块buffer即可,其中各声道的数据交叉传输;
		如果使用非交叉模式,需要为各声道分别分配一个buffer,各声道数据分别传输。
	*/
	if((err = snd_pcm_hw_params_set_access (capture_handle,hw_params,SND_PCM_ACCESS_RW_INTERLEAVED)) < 0) 
	{
		printf("无法设置访问类型(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("访问类型设置成功.\n");

	/*设置数据编码格式,并判断是否设置成功*/
	if ((err=snd_pcm_hw_params_set_format(capture_handle, hw_params,format)) < 0) 
	{
		printf("无法设置格式 (%s)\n",snd_strerror(err));
		exit(1);
	}
	fprintf(stdout, "PCM数据格式设置成功.\n");

	/*设置采样频率,并判断是否设置成功*/
	if((err=snd_pcm_hw_params_set_rate_near(capture_handle,hw_params,&rate,0))<0) 
	{
		printf("无法设置采样率(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("采样率设置成功\n");

	/*设置声道,并判断是否设置成功*/
	if((err = snd_pcm_hw_params_set_channels(capture_handle, hw_params,AUDIO_CHANNEL_SET)) < 0) 
	{
		printf("无法设置声道数(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("声道数设置成功.\n");

	/*将配置写入驱动程序中,并判断是否配置成功*/
	if ((err=snd_pcm_hw_params (capture_handle,hw_params))<0) 
	{
		printf("无法向驱动程序设置参数(%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("参数设置成功.\n");

	/*使采集卡处于空闲状态*/
	snd_pcm_hw_params_free(hw_params);

	/*准备音频接口,并判断是否准备好*/
	if((err=snd_pcm_prepare(capture_handle))<0) 
	{
		printf("无法使用音频接口 (%s)\n",snd_strerror(err));
		exit(1);
	}
	printf("音频接口准备好.\n");

	/*配置一个数据缓冲区用来缓冲数据*/
	//snd_pcm_format_width(format) 获取样本格式对应的大小(单位是:bit)
	int frame_byte=snd_pcm_format_width(format)/8;
	buffer=malloc(buffer_frames*frame_byte*AUDIO_CHANNEL_SET);
	printf("缓冲区分配成功.\n");
	
	/*开始采集音频pcm数据*/
	printf("开始播放音频数据...\n");
	
	int read_cnt;
	while(1) 
	{
		/*从文件读取数据: 音频的每帧数据样本大小是16位-2个字节*/
		read_cnt=fread(buffer,1,frame_byte*(buffer_frames*AUDIO_CHANNEL_SET),pcm_data_file);
		if(read_cnt<=0)break;
		
		/*向声卡设备写一帧音频数据:2048字节*/
		if((err=snd_pcm_writei(capture_handle,buffer,buffer_frames))!=buffer_frames) 
		{
			  printf("向音频接口写数据失败(%s)\n",snd_strerror(err));
			  exit(1);
		}
			
		if(run_flag)
		{
			printf("停止播放.\n");
			break;
		}
	}
	printf("播放完成.\n");
	/*释放数据缓冲区*/
	free(buffer);

	/*关闭音频采集卡硬件*/
	snd_pcm_close(capture_handle);

	/*关闭文件流*/
	fclose(pcm_data_file);
	return 0;
}

Tiny4412开发板分别引出了耳机输出孔和音频输入口,开发板本身自带了咪头,录音直接就可以使用开发板本身的咪头采集;播放声音时,由于开发板没有带扬声器,可以使用耳机插到开发板的音频输出口播放声音。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/09/22 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、环境介绍
  • 二、下载alsa-lib库
  • 三、编译移植过程
  • 四、测试代码
    • 4.1 安装声卡驱动
    • 4.2 编译代码的Makefile文件
    • 4.3 采集声音数据保存为PCM文件
    • 4.4 播放PCM格式数据
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档