EGI例子: EGI_FFFT快速傅立叶变换应用

8 篇文章 0 订阅
4 篇文章 0 订阅

EGI mat_egiFFFT()函数采用定点方式进行快速傅立叶变换,特别适用于那些没有FPU单元的处理器,可以加快其计算速度.当然,定点计算对FFT输入数据的数量和取值范围有一定的限制.
应用mat_egiFFFT()函数可以制作一些有趣的小应用,比如从麦克风拾取声音数据,并实时展示其频谱,让我们可以看到音频的律动效果.

下面是具体的代码和解释.

/*------------------------------------------------------------------
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as
published by the Free Software Foundation.

EGI定点快速傅立叶变换(EGI Fixed_point FFT)展示拾音频谱
An example to analyze MIC captured audio and display its spectrum.

Midas Zhou
midaszhou@yahoo.com
------------------------------------------------------------------*/
#include <stdio.h>
#include <alsa/asoundlib.h>
#include "egi_fbgeom.h"
#include "egi_FTsymbol.h"
#include "egi_math.h"

int main(void)
{
	int i,j;
	int ret;

	/* FFT参数设置 nexp+aexp Max. 21 */
	unsigned int 	nexp=7;
	unsigned int 	np=1<<nexp;  	/* 采样点数 input element number for FFT */
	unsigned int 	aexp=14;     	/* 最大幅度限制 MAX Amp=2^(14+1)-1 */
	unsigned int    fs=8000;	/* 采样频率fs, PCM录音采样率,则基频为 fs/np=62.5Hz */
	int		*nx;		/* 用于存放输入值 FFT samples */
	EGI_FCOMPLEX 	*ffx;  		/* 用于存放输出值 FFT output */
        EGI_FCOMPLEX 	*wang; 		/* 用于存放FFT系列相位角 phase angle factor */
	unsigned int 	ns=1<<6;   	/* 用于显示的点数,不是所有的np. Points for spectrum diagram */
	int		ng=np/ns;	/* 每隔ng个点显示,ng=2,则刻度为 2*62.5=125HZ, each ns covers ng numbers of np */
	int 		pps;		/* 每个显示点间隔像素 pps=spwidth/(ns-1);  pixel per ns */
	int 		sdx[ns];	/* 显示点的屏幕坐标 displaying result points  */
	int 		sdy[ns];
	int		sx0;
	int		spwidth=320;    /* 频谱在屏幕上的宽度 displaying width(in pixels) for the spectrum diagram */
	char 		strtmp[32];

	/* 计算各显示点的横坐标 CoordX for displaying results, however sdx[ns] NOT applicable! */
	pps=spwidth/ns;
	sx0=(320-pps*ns)/2;
	for(i=0; i<ns; i++) {
		sdx[i]=sx0+pps*i;
	}
	/* Freq span per ns: fgap=fs/ns=8k/64=125Hz, 32*125=4000KHz, =8kHz/2 */

	/* FFT数据准备 prepare FFT */
	nx=calloc(np, sizeof(int));
	if(nx==NULL) {
		printf("Fail to calloc nx[].\n");
		return -1;
	}
	ffx=calloc(np, sizeof(EGI_FCOMPLEX));
	if(ffx==NULL) {
		printf("Fail to calloc ffx[]. \n");
		return -1;
	}
        wang=mat_CompFFTAng(np); /*  产生FFT系列相位角 phase angle */

        /* 设置ALSA拾音参数 For PCM Capture */
	snd_pcm_t *rec_handle;
        int 	nchanl=1;   			/* 单声道 number of channles */
        int 	format=SND_PCM_FORMAT_S16_LE;   /* PCM数据格式, 16位带符号 */
        int 	frame_size=2;               	/* bytes per frame, for 1 channel, format S16_LE */
        int 	rec_srate=fs;			/* 采样率 HZ, output sample rate from capture */
        bool 	enable_resample=true;      	/* whether to enable soft resample */
	/* Adjust latency to a suitable value,according to CAPTURE/PLAYBACK period_size */
        int 	rec_latency=5000;     		/* required overall latency in us */
        /* For CAPTURE, period_size=1536 frames, while for PLAYBACK: 278 frames */
        snd_pcm_uframes_t chunk_frames=np;	/* in frame, expected frames for readi/writei each time */
	const int buffsize=1536;		/* Try PCM Capture period size,实际每次读取np个 */
	int16_t buff[buffsize];			/* 存放每次拾音得到的PCM数据 */


        /* <<<<<   初始化EGI系统 EGI general init >>>>>> */
        if(FTsymbol_load_sysfonts() !=0 ) {  	/* 加载EGI系统字体 load FT fonts */
                printf("Fail to load FT appfonts, quit.\n");
                return -2;
        }
        if( init_fbdev(&gv_fb_dev) )		/* 初始化FB显示设备 init sys FB */
                return -1;

        /* 设置FB显示方式 Set sys FB mode */
        fb_set_directFB(&gv_fb_dev,true);  /* 直接写屏 */
        fb_position_rotate(&gv_fb_dev,0);  /* 屏幕方向 */

        /* 打开ALSA默认录音设备 Open pcm captrue device */
        if( snd_pcm_open(&rec_handle, "default", SND_PCM_STREAM_CAPTURE, 0) <0 ) {
                printf("Fail to open pcm captrue device!\n");
                return -1;
        }

        /* 设置录音参数 Set params for pcm capture handle */
        if( snd_pcm_set_params( rec_handle, format, SND_PCM_ACCESS_RW_INTERLEAVED,
                                nchanl, rec_srate, enable_resample, rec_latency )  <0 ) {
                printf("Fail to set params for pcm capture handle.!\n");
                snd_pcm_close(rec_handle);
                return -2;
        }

        /* 调节录音音量大小,或者后期用alsamixer命令条件.Adjust record volume, or to call ALSA API */
        system("amixer -D hw:0 set Capture 85%");
        system("amixer -D hw:0 set 'ADC PCM' 85%");

	/* 清屏 Clear screen */
	clear_screen(&gv_fb_dev,WEGI_COLOR_DARKPURPLE);

	/* 绘制频谱刻度 Draw spectrum scale:
	 * 125Hz*ns=125Hz*64=8kHz.  4*125Hz=500Hz for each scale.
	 * Each 125Hz takes spwidth/(ns-1)=300/63=4pixels.
	 */
	fbset_color(WEGI_COLOR_CYAN);
	draw_line(&gv_fb_dev, sx0, 215, sx0+pps*ns-1, 215);
	for(i=0; i<ns+1; i++) {  /* Total results points. ns */
		if(i%8==0) {	/* each 8 * 1/ns,  as 1000Hz */
			draw_line(&gv_fb_dev, sx0+i*pps, 215, sx0+i*pps, 215-9);  /* each 1/ns (4 pixel) as 125Hz */
			sprintf(strtmp,"%dk", (i/8 -(i>32?2*(i-32)/8:0)) );
		    //if(i<ns/2+1)
		        FTsymbol_uft8strings_writeFB(&gv_fb_dev, egi_sysfonts.regular,  /* FBdev, fontface */
                		                     12, 12, (UFT8_PCHAR)strtmp,        /* fw,fh, pstr */
                                		     100, 1,  0,           	  /* pixpl, lines, gap */
		                                     sx0+i*pps-(i==0?0:6), 215,                /* x0,y0, */
                		                     WEGI_COLOR_CYAN, -1, 255,   /* fontcolor, stranscolor,opaque */
						     NULL, NULL, NULL, NULL);
		}
		else if(i%4==0) { /* each 500Hz */
			draw_line(&gv_fb_dev, sx0+i*pps, 215, sx0+i*pps, 215-6);  /* each 1/ns (4 pixel) as 125Hz */
		}
		else	/* each 125Hz */
			draw_line(&gv_fb_dev, sx0+i*pps, 215, sx0+i*pps, 215-3);  /* each 1/ns (4 pixel) as 125Hz */


	}

	/* 写上标题 Put a tab */
	draw_blend_filled_rect( &gv_fb_dev, 0, 40-10, 320-1, 40+30+10, WEGI_COLOR_GRAY, 150);
        FTsymbol_uft8strings_writeFB(&gv_fb_dev, egi_sysfonts.regular,  /* FBdev, fontface */
                                     25, 25, (UFT8_PCHAR)"EGI FFT DEMO",             /* fw,fh, pstr */
                                     240, 1,  0,           	  /* pixpl, lines, gap */
                                     80,  40,                     /* x0,y0, */
                                     WEGI_COLOR_WHITE, -1, 255,   /* fontcolor, stranscolor,opaque */
				     NULL, NULL, NULL, NULL);

	/* 循环: 拾音/FFT/显示频谱 */
        while(1) /* Let user to interrupt, or if(count<record_size) */
        {
		/* 从声卡读取拾音数据,每次np个. */
                /* snd_pcm_sframes_t snd_pcm_readi (snd_pcm_t pcm, void buffer, snd_pcm_uframes_t size) */
		/* return number of frames, for 1 chan, 1 frame=size of a sample (S16_Le)  */
                ret=snd_pcm_readi(rec_handle, buff, np);
                if(ret == -EPIPE ) {
                        /* EPIPE for overrun */
                        printf("Overrun occurs during capture, try to recover...\n");
                        /* try to recover, to put the stream in PREPARED state so it can start again next time */
                        snd_pcm_prepare(rec_handle);
                        continue;
                }
                else if(ret<0) {
                        printf("snd_pcm_readi error: %s\n",snd_strerror(ret));
                        continue; /* carry on anyway */
                }
                /* CAUTION: short read may cause trouble! Let it carry on though. */
                //else if(ret != chunk_frames) {
                else if(ret < chunk_frames) {
                      printf("snd_pcm_readi: read end or short read ocuurs! get only %d of %ld expected frame",
                                                                                ret, chunk_frames);
                        /* pad 0 to chunk_frames */
                        if( ret<chunk_frames ) {  /* >chunk_frames NOT possible? */
                                memset( buff+ret*frame_size, 0, (chunk_frames-ret)*frame_size);
                        }
                }

		/* <<<<<   EGI FFT 开始  >>>>> */
		/* 将拾音数据转化成FFT输入数据 Convert PCM buff[] to FFT nx[] */
		for(i=0; i<np; i++)
			nx[i]=buff[i];  /* 如有必要的话对数据进行修剪,使其不大于2^(aexp+1)-1. trim amplitude to Max 2^(aexp+1)-1 */

		/* 对nx[]进行np点FFT运算,结果存入ffx[].  Run FFT with INT nx[] */
        	mat_egiFFFT(np, wang, NULL, nx, ffx);

		/* 计算频谱对应各点的纵向坐标. Update sdy[] */
		for(i=0; i<ns; i++) {
			sdy[i]=200-( mat_uintCompAmp(ffx[i*ng])>>(nexp-1 ) ); /* 如有必要对幅度进行缩小,以方便显示 */

			/* trim sdy[] */
			if(sdy[i]<0)
				sdy[i]=0;
		}

		/* 如有必要可对频谱进行光滑等处理 */
                /* Apply 4 points average filter for sdy[], to smooth spectrum diagram, so it looks better! */
                for(i=0; i<ns-(2-1); i++) {
                        for(j=0; j<(2-1); j++)
                                sdy[i] += sdy[i+j];
                        sdy[i]>>=1;
                }

		/* 清除上一次的显示图谱 */
	        fb_filo_off(&gv_fb_dev);   /* turn off filo */
	        fb_filo_flush(&gv_fb_dev); /* flush and restore old FB pixel data */
        	fb_filo_on(&gv_fb_dev);    /* start collecting old FB pixel data */

		/* 绘制频谱,用线段连接 */
		fbset_color(WEGI_COLOR_PINK);
		for(i=1; i<ns-1; i++) {  /* i=0/ns-1 is DC */
			draw_line(&gv_fb_dev,sdx[i], sdy[i], sdx[i+1],sdy[i+1]);
		}
		/* <<<<<  EGI FFT 结束  >>>>> */
	}

	/* 关闭录音设备,释放资源 */
        snd_pcm_close(rec_handle);
	free(ffx);
	free(wang);

        /* <<<<<  释放EGI系统资源 EGI general release >>>>> */
        printf("FTsymbol_release_allfonts()...\n");
        FTsymbol_release_allfonts();
	printf("release_fbdev()...\n");
        fb_filo_flush(&gv_fb_dev);
        release_fbdev(&gv_fb_dev);
        printf("<-------  END  ------>\n");

return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Midas-Zhou

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值