N32905音视频学习笔记-录音和播放

目录

准备工作:

1.N32905 audio简介

2.硬件设计

3.软件设计

3.1内核配置

3.2 audio录音和重放应用程序

4.编译

5.烧录运行

本例程将在Duckbill鸭嘴兽WIFI-Mini905板上学习录音和播放,可以实现录一段自己的声音然后播放,也可以播放本地PCM音频数据。

准备工作:

wifi音视频开发板一块

1.N32905 audio简介

N32905内部集成了音频DAC和SPU(声音处理单元),专用于音频处理。

音频DAC:支持16位立体声,可以直接驱动耳机。

SPU-声音处理单元:支持32个立体声通道;支持的原始音频格式有PCM8/PCM16/4-bit MDPCM/TONE;每一个通道支持7位音量控制;支持10波段均衡器;支持左右声道。

 

2.硬件设计

录音电路主要由mic和滤波电路组成,如下图1.2.1所示。

图2.1 录音电路

播放电路采用了ft690音频功放,可以直接驱动喇叭,如下图1.2.2所示。

图2.2 播放电路

3.软件设计

3.1内核配置

3.2 audio录音和重放应用程序

代码路径:/duckbill/N32905/BSP/applications/audio/audio_demo.c。audio代码如下。

/* 
 *
 * Audio录音和播放程序
 *
 */

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/soundcard.h>
#include <sys/poll.h>
#include <pthread.h>
#include <math.h>
#include <signal.h>
#include <sys/time.h>
#include <time.h>
#include <pthread.h>
#include <errno.h>

//PCM录音文件
char *pcmfiles[] = {
		"./8k.pcm",
		"./11.025k.pcm",
		"./16k.pcm",
		"./22.05k.pcm",
		"./24k.pcm",
		"./32k.pcm",
		"./44.1k.pcm",
		"./48k.pcm"};
	
//采样率	
const int samplerate[] = {
		8000,
		11025,
		16000,
		22050,
		24000,
		32000,
		44100,
		48000};
		
int p_dsp, p_mixer;
int r_dsp, r_mixer;
static int rec_stop = 0;

static volatile int rec_volume, play_volume;

//关闭播放设备
int close_audio_play_device()
{
 
  close(p_dsp);		//关闭设备描述符p_dsp
  close(p_mixer);	//关闭设备描述符p_mixer
	
  return 0;	
}

//打开播放设备
int open_audio_play_device()
{
	printf("*****\n");
	p_dsp = open("/dev/dsp", O_RDWR);		//打开播放设备dsp
	if( p_dsp < 0 ){
		printf("Open dsp error\n");
		return -1;
	}
	printf("====\n");	
	p_mixer = open("/dev/mixer", O_RDWR);	//打开播放设备mixer
	if( p_mixer < 0 ){
		printf("Open mixer error\n");
		return -1;
	}
}

//关闭录音设备
int close_audio_rec_device()
{
 
  close(r_dsp);			//关闭设备描述符r_dsp
  close(r_mixer);		//关闭设备描述符r_mixer
	
  return 0;	
}

//打开录音设备
int open_audio_rec_device()
{
	r_dsp = open("/dev/dsp1", O_RDWR);		//打开录音设备dsp1
	if( r_dsp < 0 ){
		printf("Open dsp error\n");
		return -1;
	}
//	printf("Open dsp OK\n");	
	r_mixer = open("/dev/mixer1", O_RDWR);	//打开录音设备mixer1

	if( r_mixer < 0 ){
		printf("Open mixer error\n");
		return -1;
	}
//	printf("Open mixer OK\n");		
}

//双通道录音生成PCM文件
int record_to_pcm(char *file, int samplerate, int loop)
{
	int i, status = 0, frag;
	int channel = 2, volume = 0x6464;
	FILE *fd;
	char *buffer;
	struct timeval time;
	struct timezone timezone;
	int nowsec;
	
	open_audio_rec_device();		//打开录音设备
	fd = fopen(file, "w+");			//打开录音文件
	if(fd == NULL)
	{	
		printf("open %s error!\n", file);
		return 0;
	}
		
	printf("open %s OK!\n", file);		
	
//	ioctl(r_mixer, MIXER_WRITE(SOUND_MIXER_MIC), &volume);	//set MIC max volume 
	ioctl(r_mixer, MIXER_WRITE(SOUND_MIXER_MIC), &rec_volume);	//set MIC max volume 
	
	status = ioctl(r_dsp, SNDCTL_DSP_SPEED, &samplerate);		//设置采样率
	if(status<0)
	{
		fclose(fd);
		printf("Sample rate not support\n");
		return -1;
	}	
	ioctl(r_dsp, SNDCTL_DSP_CHANNELS, &channel);		//设置通道
	ioctl(r_dsp, SNDCTL_DSP_GETBLKSIZE, &frag);			//获取块的大小frag
	
//	printf("volume=%d\n", volume);	
//	printf("volume=%d\n", rec_volume);	
	printf("samplerate=%d\n", samplerate);	
	printf("channel=%d\n", channel);				
	printf("frag=%d\n", frag);		
	
	buffer = (char *)malloc(frag);			//为buffer分配frag大小的内存
	
	gettimeofday(&time, &timezone);
	nowsec = time.tv_sec;
	
	printf("begin recording!\n");					
	
	for(i=0;i<loop;i++)
	{
		int size=0;
		do
		{		
			size = read(r_dsp, buffer, frag);	// 录音,每次从录音设备r_dsp中读frag大小数据到buffer
		}while(size<=0);		
		fwrite(buffer, 1, frag, fd);			//	将录音数据buffer写到录音文件fd中	
	}		

	printf("----\n");	
	printf("record exit!!\n");
	close_audio_rec_device();					//关闭录音设备	
	fclose(fd);									//关闭录音文件
	free(buffer);								//释放内存
	
	return 0;
}

//单通道录音生成PCM文件
int record_to_pcm_single(char *file, int samplerate, int loop)
{
	int i, status = 0, frag;
	int channel = 1, volume = 0x6464;
	FILE *fd;
	char *buffer;
	struct timeval time;
	struct timezone timezone;
	int nowsec;
	
	open_audio_rec_device();			//打开录音设备
	fd = fopen(file, "w+");				//打开录音文件
	if(fd == NULL)
	{	
		printf("open %s error!\n", file);
		return 0;
	}
		
	printf("open %s OK!\n", file);		
	ioctl(r_mixer, MIXER_WRITE(SOUND_MIXER_MIC), &rec_volume);	/* set MIC max volume. Only support from external  DAC*/	
	status = ioctl(r_dsp, SNDCTL_DSP_SPEED, &samplerate);		//设置采样率
	if(status<0)
	{
		fclose(fd);
		printf("Sample rate not support\n");
		return -1;
	}	
	ioctl(r_dsp, SNDCTL_DSP_CHANNELS, &channel);				//设置通道
	ioctl(r_dsp, SNDCTL_DSP_GETBLKSIZE, &frag);					//获取块的大小frag
	
	fcntl(r_dsp, F_SETFL, O_NONBLOCK);				/* Set to nonblock mode */

	/* fragments, 2^12 = 4096 bytes */
	frag = 0x2000c;	
	ioctl(r_dsp, SNDCTL_DSP_SETFRAGMENT, &frag);
	ioctl(r_dsp, SNDCTL_DSP_GETBLKSIZE, &frag);	

	loop *= 2;		/*  (one channel) * (16-bits per sdample)/frag) */
	loop *= samplerate;
	printf("pow(2, 12) = %d\n", pow(2,12));
	loop /= pow(2, 12);	/* frag =0x200C.  0xC=12 */		

	printf("samplerate=%d\n", samplerate);	
	printf("channel=%d\n", channel);				
	printf("frag=%d\n", frag);		
	
	buffer = (char *)malloc(frag);				//为buffer分配frag大小的内存

	gettimeofday(&time, &timezone);
	nowsec = time.tv_sec;	
	printf("begin recording!\n");					
	for(i=0;i<loop;i++)
	{
		int size=0;
		do
		{
			size = read(r_dsp, buffer, frag);	// 录音,每次从录音设备r_dsp中读frag大小数据到buffer
		}while(size <= 0);					
		fwrite(buffer, 1, frag, fd);			//将录音数据buffer写到录音文件fd中
	}		

	printf("----\n");	
	printf("record exit!!\n");
	close_audio_rec_device();					//关闭录音设备	
	fclose(fd);									//关闭录音文件
	free(buffer);								//释放内存
	
	return 0;
}

//播放双通道PCM
int play_pcm(char *file, int samplerate)
{
	int data, oss_format, channels, sample_rate;	
	int i;
	char *buffer;
	
	//printf("<=== %s ===>\n", file);
	open_audio_play_device();			//打开播放设备
	
	FILE *fd;
	fd = fopen(file, "r+");				//打开播放文件
    if(fd == NULL)
    {
    	printf("open %s error!\n", file);
    	return 0;
    }
    
	data = 0x5050;
	oss_format=AFMT_S16_LE;/*standard 16bit little endian format, support this format only*/
	sample_rate = samplerate;
	channels = 2;
	ioctl(p_dsp, SNDCTL_DSP_SETFMT, &oss_format);			//设置16位小端格式
	ioctl(p_mixer, MIXER_WRITE(SOUND_MIXER_PCM), &data);	//设置PCM data
	ioctl(p_dsp, SNDCTL_DSP_SPEED, &sample_rate);			//设置采样率
	ioctl(p_dsp, SNDCTL_DSP_CHANNELS, &channels);			//设置通道
			
	int frag;
	ioctl(p_dsp, SNDCTL_DSP_GETBLKSIZE, &frag);				//获取块的大小frag
	buffer = (char *)malloc(frag);							//为buffer分配frag大小的内存
	printf("frag=%d\n", buffer);	
	
	fread(buffer, 1, frag, fd);			//从播放文件fd中读取frag大小数据到buffer中
	while(!feof(fd))	
	{		
		audio_buf_info info;			
		do{			
			ioctl(p_dsp , SNDCTL_DSP_GETOSPACE , &info);	//获取p_dsp剩余空间		
			usleep(100);
		}while(info.bytes < frag);				//若剩余空间不够写入frag大小数据则等待
		
		fd_set writefds;
		struct timeval tv;
		tv.tv_sec       = 0;
		tv.tv_usec      = 0;
		FD_ZERO( &writefds );
		FD_SET( p_dsp , &writefds );
		tv.tv_sec       = 0;
		tv.tv_usec      = 0;
		
		select( p_dsp + 1 , NULL , &writefds , NULL, &tv );		//判断p_dsp是否可写,超时时间tv
		if( FD_ISSET( p_dsp, &writefds ))
		{	
			write(p_dsp, buffer, frag);   			//从buffer中取frag大小数据写到播放设备p_dsp		
			fread(buffer, 1, frag, fd);				//从播放文件fd中读取frag大小数据到buffer中
		}	
		usleep(100);
	}
	
	int bytes;
	ioctl(p_dsp,SNDCTL_DSP_GETODELAY,&bytes);			//获取p_dsp的延时时间
	int delay = bytes / (sample_rate * 2 * channels);	//设置延时时间
	sleep(delay);
	
	printf("Stop Play\n");
	fclose(fd);					//关闭播放文件
	free(buffer);				//释放内存			
	close_audio_play_device();	//关闭播放设备
}

//播放单通道PCM
int play_pcm_single(char *file, int samplerate)
{
	int data, oss_format, channels, sample_rate;	
	int i;
	char *buffer;
	
	//printf("<=== %s ===>\n", file);
	open_audio_play_device();				//打开播放设备
	
	FILE *fd;
	fd = fopen(file, "r+");					//打开播放文件
    if(fd == NULL)
    {
    	printf("open %s error!\n", file);
    	return 0;
    }
    
	data = 0x5050;
	oss_format=AFMT_S16_LE;//standard 16bit little endian format, support this format only
	sample_rate = samplerate;
	channels = 1;
	ioctl(p_dsp, SNDCTL_DSP_SETFMT, &oss_format);				//设置16位小端格式
	ioctl(p_mixer, MIXER_WRITE(SOUND_MIXER_PCM), &data);		//设置PCM data
	ioctl(p_dsp, SNDCTL_DSP_SPEED, &sample_rate);				//设置采样率
	ioctl(p_dsp, SNDCTL_DSP_CHANNELS, &channels);				//设置通道
			
	int frag;
	ioctl(p_dsp, SNDCTL_DSP_GETBLKSIZE, &frag);					//获取块的大小frag
	buffer = (char *)malloc(frag);								//为buffer分配frag大小的内存
	printf("frag=%d\n", buffer);	
	
	fread(buffer, 1, frag, fd);					//从播放文件fd中读取frag大小数据到buffer中
	while(!feof(fd))	
	{		
		audio_buf_info info;			
		do{			
			ioctl(p_dsp , SNDCTL_DSP_GETOSPACE , &info);			//获取p_dsp剩余空间
			usleep(100);
		}while(info.bytes < frag);					//若剩余空间不够写入frag大小数据则等待
		
		fd_set writefds;
		struct timeval tv;
		tv.tv_sec       = 0;
		tv.tv_usec      = 0;
		FD_ZERO( &writefds );
		FD_SET( p_dsp , &writefds );
		tv.tv_sec       = 0;
		tv.tv_usec      = 0;
		
		select( p_dsp + 1 , NULL , &writefds , NULL, &tv );		//判断p_dsp是否可写,超时时间tv
		if( FD_ISSET( p_dsp, &writefds ))
		{	

			write(p_dsp, buffer, frag);   		//从buffer中取frag大小数据写到播放设备p_dsp	
			fread(buffer, 1, frag, fd);			//从播放文件fd中读取frag大小数据到buffer中
		}	
		usleep(100);
	}
	
	int bytes;
	ioctl(p_dsp,SNDCTL_DSP_GETODELAY,&bytes);			//获取p_dsp的延时时间
	int delay = bytes / (sample_rate * 2 * channels);	//设置延时时间
	sleep(delay);
	
	printf("Stop Play\n");
	fclose(fd);								//关闭播放文件
	free(buffer);							//释放内存
	close_audio_play_device();				//关闭播放设备
}

//录音
void * p_rec(void * arg)
{
	printf("recording ...\n");
	record_to_pcm("./rec.pcm", 16000, -1);	
}

//播放
void * p_play(void * arg)
{
	printf("playing ...\n");
	play_pcm("./16k.pcm", 16000);	
	rec_stop = 1;
}

int main()
{
	int i, loop, sr;
	char path[256];
	int buf;
	
	printf("\n**** Audio Test Program ****\n");
	printf("1. Play \n");						//1播放本地PCM歌曲
	printf("2. Record and then Play \n");		//2录音后播放
	printf("Select:");
		
	scanf("%d", &i);		//选择值保存在变量i
	
	if(i<1 || i >2)			//若i不在1和2范围里则返回
	  return 0;
	 
	if(i == 1)				//播放本地PCM歌曲
	{ 
		printf("Playing ...\n");	
play_pcm("./16k.pcm", 16000);	
		//for(i=0;i<8;i++)
		//	play_pcm(pcmfiles[i], samplerate[i]);       //依次播放8种不同采样率的PCM歌曲
	}
	else if(i==2)			//录音后播放
	{
		printf("\nRec Seconds:");
		scanf("%d", &loop);		//选择录音时间,单位秒,保存在变量loop
		
		printf("Sample Rate(8000, 11025, 12000 or 16000):");	//选择采样率,保存在变量sr
		scanf("%d", &sr);
	
		printf(" *** Recording ***\n");
		printf("SampleRate=%d, Time=%d sec\n", sr, loop);	

		record_to_pcm_single("./rec.pcm", sr, loop);			//录音loop时间,采样率sr,录音文件保存在rec.pcm

		printf("\nDone, Now play it ...\n");

		getchar();
		play_pcm_single("./rec.pcm", sr);			//播放录音文件rec.pcm
	}
	else
		printf("wrong input\n");

	
	return 0;
}

程序流程:main函数一进来,等待用户输入选择,输入1代表播放本地PCM原始音频文件,调用play_pcm()执行;输入2代表录音和播放,调用record_to_pcm_single()和play_pcm_single()执行。

在play_pcm()、play_pcm_single()和record_to_pcm_single()中,每个函数都需要经过这几个步骤完成录音和播放:打开设备,初始化设备设置参数,然后从设备中读写数据,最后关闭设备。

注意:play_pcm()和play_pcm_single()区别在于前者是双通道,后者是单通道,record_to_pcm()和record_to_pcm_single()的区别也一样。在播放本地歌曲时,由于本地歌曲是双通道的格式,因此只能调用play_pcm(),若调用play_pcm_single()单通道则声音异常。

4.编译

在ubuntu下切换路径至/duckbill/N32905/applications/audio。

执行make,编译生成可执行文件audio_demo,audio_demo将会自动拷贝至例程文件系统目录 /duckbill/N32905/usr/TEST_mini905/mkFilesys下。

执行例程文件系统TEST_mini905/test_mini905/目录下的脚本mkjffs2.sh,生成我们所需的jffs2文件系统。

5.烧录运行

WIFI-Mini905开发板与电脑之间连接好usb电源线(也充当下载线)、usb转串口线,将拨码开关S1拨向Rec位,按下自锁开关K1,开发板通电,N32905进入烧录模式。

使用TurboWriter依次烧录 开发板光盘资料\Mini905光盘资料\BIN\基础例程下的loader(SpiLoader_905.bin)、内核(Kernel.bin)以及刚刚生成的文件系统(TEST_mini905.jffs2.summary),烧录步骤与例程1一致。

烧录完成后将拨码开关S1拨向Nor位,开发板重新通电,电源指示灯亮,N32905进入正常启动模式,等待系统运行起来。

在串口超级终端输入./aucio_demo,执行音频应用程序,出现菜单,提示用户选择选项,在这里输入1,喇叭就可以播放悦耳的歌曲了。

等歌曲播放完成,重新执行./aucio_demo,选择2,接着根据提示信息输入相应的参数-录音时间和采样率,采样率越高,声音越清晰细腻。你就可以对着底板上的mic头说话,时间一到喇叭就把你刚才说的播放一次。

猜你喜欢

转载自blog.csdn.net/chenzhe805/article/details/81665265