Linux下音频开发: 读取声卡PCM数据保存到文件

一、环境介绍

系统: ubuntu18.04 (64位)

声卡: 电脑自带

二、功能介绍

通过alsa-lib库读取电脑声卡的PCM数据保存到文件。

在ubuntu系统上安装alsa-lib库方法:

 sudo apt-get install libasound2-dev

   如果是在其他发行版linux系统上或者需要在嵌入式linux系统上使用alsa-lib库,可以下载alsa-lib源码包,自行编译。

  开源ALSA架构的官网地址:https://www.alsa-project.org/wiki/Main_Page

三、获取本机可用的声卡设备

获取声卡可以使用arecord -L命令。

ubuntu系统下如果没有这个命令,直接根据提示安装一个即可。

    示例:   一般  hw:0 或者 default 表示默认声卡设备。

wbyq@wbyq:/mnt/hgfs/linux-share-dir/linux_c/linux_pcm_save$ arecord -L

default

    Playback/recording through the PulseAudio sound server

null

    Discard all samples (playback) or generate zero samples (capture)

pulse

    PulseAudio Sound Server

sysdefault:CARD=AudioPCI

    Ensoniq AudioPCI, ES1371 DAC2/ADC

    Default Audio Device

front:CARD=AudioPCI,DEV=0

    Ensoniq AudioPCI, ES1371 DAC2/ADC

    Front speakers

iec958:CARD=AudioPCI,DEV=0

    Ensoniq AudioPCI, ES1371 DAC2/ADC

    IEC958 (S/PDIF) Digital Audio Output

dmix:CARD=AudioPCI,DEV=0

    Ensoniq AudioPCI, ES1371 DAC2/ADC

    Direct sample mixing device

dsnoop:CARD=AudioPCI,DEV=0

    Ensoniq AudioPCI, ES1371 DAC2/ADC

    Direct sample snooping device

hw:CARD=AudioPCI,DEV=0

    Ensoniq AudioPCI, ES1371 DAC2/ADC

    Direct hardware device without any conversions

plughw:CARD=AudioPCI,DEV=0

    Ensoniq AudioPCI, ES1371 DAC2/ADC

    Hardware device with all software conversions

四、示例代码

参考文档:https://users.suse.com/~mana/alsa090_howto.html

/*
 进行音频采集,采集pcm数据并直接保存pcm数据
 音频参数: 
	 声道数:		2
	 采样位数:	16bit、LE格式
	 采样频率:	44100Hz
*/
#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;
}

编译方法:

gcc linux_pcm_save.c -lasound

运行方法:

./a.out hw:0 123.pcm

录制过程中如果想要结束录制,直接按下Ctrl+C即可。

成功之后,会在当前目录下生成123.pcm文件。 

五、播放PCM数据

Windows下可以使用audacity软件进行播放PCM裸流数据。

或者通过ffplay工具(安装ffmpeg会带上这个工具)。

ffplay -ar 44100 -channels 2 -f s16le -i 123.pcm

audacity软件下载地址:

https://download.csdn.net/download/xiaolong1126626497/12252685

下面的公众号有全套的QT、C、C++、单片机学习基础教程:

发布了20 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/xiaolong1126626497/article/details/104916277