⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mfcc_xhg.cpp

📁 语音识别的端点检测程序
💻 CPP
字号:
/*******************************************************************
****	文件:		mfcc_xhg.cpp								****
****	功能:计算MFCC所用到的一些基本函数			****
**** 			 浮点程序										****
****	最后定型01.08.20, 许海国		          				****
//MFCC特征提取实现的主程序,其它另外需要的说明均在
//MFCC_xhg.h中, 程序中只需要包含MFCC_xhg.h即可
*******************************************************************/


#include <math.h>
#include <stdlib.h>
#include <stdio.h>
#include "cst_lib.h"
#include "mfcc_xhg.h"
#include "basop.h"
#include "endPoint_xhg.h"
/*=====================================================
函数名称:	StartEnd
函数功能:	对一句话的语音数据进行端点检测 
函数性质:	
输入参数:	*sData 一句话的首地址
			lDataLength	一句话的长度(样点个数)
			*lStartPnt	返回起始样点
			*lEndPnt	返回结束样点
			lSampleRate	采样率
输出参数:	
备注:		
基本思路:
====================================================*/
void StartEnd(short *sData, long lDataLength, 
			  long *lStartPnt, long *lEndPnt,short *StartFrame,short *EndFrame,
			  long lSampleRate)
{
	long	FrameTotalNumber;//一句话中的帧计数器
	long lDataLength_8000=0;
	short	*sdata_8000=NULL;
	short	nStartFrame, nEndFrame;
	    
 printf("lDataLength==%d\n",lDataLength); 
	lDataLength_8000=lDataLength;
	sdata_8000=sData;
	FrameTotalNumber=(long)((sizeof(short)*lDataLength_8000- (sizeof(short)*FRAME_LEN))/(sizeof(short)*FRAME_STEP)) + 1;

printf("FrameTotalNumber==%d\n",FrameTotalNumber);

	Comp_Feature(sdata_8000, FrameTotalNumber, nStartFrame, nEndFrame);

	
	(*StartFrame)=nStartFrame;
	(*EndFrame)=nEndFrame;
	(*lStartPnt)=(long)((nStartFrame-1)*FRAME_STEP);
	(*lEndPnt)=(long)((nEndFrame+1)*FRAME_STEP);

	if( ((*lStartPnt)>lDataLength_8000) || ((*lStartPnt)<0) )
		(*lStartPnt)=0;

	if( ((*lEndPnt)>lDataLength_8000) || ((*lEndPnt)<0) )
		(*lEndPnt)=lDataLength_8000;



}

/*=====================================================
函数名称:	Comp_Feature
函数功能:	对一句话的语音数据进行处理, 求出它们的特征, 包括倒谱(可选择的特征, 差分倒谱, 
			能量, 差分能量, 二阶差分能量, 并判断出端点, 起始帧号 & 结束帧号
函数性质:	
输入参数:	OptionMode		模式参数,MFCC or LPCC, CMS, ...
			sData			语音数据区的头,建议采用连续数据区的数据指针
			Frame_Number	一句话中, 总的语音帧数
输出参数:	FeatureArray	对应于该帧语音的特征矢量,已分配了内存
			nStartFrame		起始帧
			nEndFrame		结束帧
备注:		
基本思路:
====================================================*/
void Comp_Feature(short *sData, int FrameNumber, short &nStartFrame, short &nEndFrame)
{
	short	wSampleCount, wFrameCount;			//样点和帧的循环计数变量
	short	*tmpVoiceHead;						//用来指向每一个语音帧的头, 地址是会改变的, 但是数据是不变的
	float   	fOneFrameData[FRAME_LEN];			//一帧语音数据, 其数据是会在计算过程中改变

	static	int		first =1;					//标志位, hamming窗, cepweightwindow, 三角窗
	static	float	HanmingWindow[FRAME_LEN];	//hanming窗函数

	//用于端点检测
	float	*EnergyArray		= NULL;			//用来保存每一帧的能量
	short	*nZeroPassArray		= NULL;			//用来保存每一帧的过零率	
	float	*fRatioFreq			= NULL;			//用来保存每一帧的低频能量
	float	DcLevelOneSentence	= 0;
	static  float	EnergyTh;					//用于端点检测的能量门限
//printf("2====================\n");
	//动态分配对数能量的内存
	EnergyArray		= new float [FrameNumber];
	//动态分配过零率的内存
	nZeroPassArray	= new short [FrameNumber];
	//动态分配低频能量的内存
	fRatioFreq		= new float [FrameNumber];

	//获取语音数据存储地址
	tmpVoiceHead = sData;
	
	//判断如果是第一次, 这初始化hanming窗, 倒谱加权窗, (MFCC)Mel三角窗
	if(first)
	{
		first = 0;
		Initial(HanmingWindow);
	}

	//---------------->
	//对一句话中的每一帧进行循环, 求出其倒谱特征
	for(wFrameCount=0; wFrameCount<FrameNumber; wFrameCount++)
	{
	
		//从语音数据缓冲区取得一帧的语音数据进行计算
		for(wSampleCount=0; wSampleCount<FRAME_LEN; wSampleCount++)
		{
			fOneFrameData[wSampleCount] = tmpVoiceHead[wSampleCount];
		DcLevelOneSentence += fOneFrameData[wSampleCount];
		}

	//预加重
			PreEmphasis(fOneFrameData);

	//加窗
		for(wSampleCount=0; wSampleCount<FRAME_LEN; wSampleCount++)
			fOneFrameData[wSampleCount] = fOneFrameData[wSampleCount] * HanmingWindow[wSampleCount];


		Comp_Mfcc(fOneFrameData, fRatioFreq[wFrameCount]);

	
	   tmpVoiceHead	+= FRAME_STEP;

	}//<---------------------



	//此处进行一句话的能量计算
	tmpVoiceHead	= sData;

	DcLevelOneSentence /= (FrameNumber * FRAME_LEN);

	//计算基本的能量,  过零率
	EnergyCompute(tmpVoiceHead, EnergyArray, nZeroPassArray, FrameNumber, DcLevelOneSentence);

	//计算用于端点检测的能量门限
	EnergyThreshCompte(EnergyArray, FrameNumber, EnergyTh);


	//对低频能量进行归一化, 把所有的低频能量减去其最大值
	//找出最大值, 由于总的能量的动态范围太大, 故采用这种归一化的方法并不是很合适的.
	double	tmpRatio = -1e50;
	for(wFrameCount=0; wFrameCount<FrameNumber; wFrameCount++)
		if(tmpRatio < fRatioFreq[wFrameCount])
			tmpRatio = fRatioFreq[wFrameCount];

	//归一化
	for(wFrameCount=0; wFrameCount<FrameNumber; wFrameCount++)
		fRatioFreq[wFrameCount] -= (float)tmpRatio;

	//端点检测
	//现在需要输入的参数有: 能量门限, 每一帧的能量 & 过零率 & 低频能量,
	EndPointDetection(EnergyArray, nZeroPassArray, fRatioFreq, FrameNumber, nStartFrame, nEndFrame, EnergyTh);
	//删除内存的分配
	if(EnergyArray!=NULL)
	{
		delete EnergyArray;
	    EnergyArray=NULL;
	}
    if(nZeroPassArray!=NULL)
	{
		delete nZeroPassArray;
	    nZeroPassArray=NULL;
	}
	if(fRatioFreq!= NULL)
	{
		delete fRatioFreq;
		fRatioFreq = NULL;
	}

}


void	Comp_Mfcc(float *fOneFrameData,	float &fAreaEnergy)//!!!
{
	short	wFftCount;					//用来作为循环中的计数变量
	float	fFFTAm[MAX_FREQ_POINT];		//保存FFT频谱的幅度	
	COMPLEX cFFTData[FFT_NUM];			//用来保存复数的语音数据

	//对用于FFT的cFFTData 进行初始化
	for(wFftCount=0; wFftCount<FFT_NUM; wFftCount++)
	{
		cFFTData[wFftCount].real = 0.0;
		cFFTData[wFftCount].image = 0.0;
	}
	for(wFftCount=0; wFftCount<FRAME_LEN; wFftCount++)
		cFFTData[wFftCount].real = fOneFrameData[wFftCount];

	//1.计算FFT, 获得语音信号的频谱
	FFT(cFFTData, FFT_ORDER);

	//2.由FFT的计算结果, 求得信号的幅度谱
	//			FFTAm(x, y) = x^2 + y^2
	FFTAm(cFFTData, fFFTAm, FFT_ORDER);

	//计算第100hz-400hz频域的能量, 因为浊音在该频域的能量比较高,
	AreaFreqComp(fFFTAm, fAreaEnergy);
}


/*=====================================================
函数名称:FFT
函数功能:FFT变换,基2DIT
输入参数: x 复数点序列,
		  m FFT的级数
输出参数:无,原址操作
====================================================*/
void FFT(COMPLEX *input, int x)
{
	int n , i , nv2 , j , k , le , l , le1 , ip , nm1 ;
	COMPLEX t , u , w ;

	n = 1;
	for(i=0; i<x; i++)
		n = n*2 ;

	nv2 = n / 2 ;
	nm1 = n - 1 ;
  	j = 1 ;

	for (i = 1 ; i <= nm1 ; i ++)
	{
		if (i < j)
		{
			t.real = input[i - 1].real ;
			t.image = input[i - 1].image ;
			input[i - 1].real = input[j - 1].real ;
			input[i - 1].image = input[j - 1].image ;
			input[j - 1].real = t.real ;
			input[j - 1].image = t.image ;
		}

		k = nv2 ;

		while (k < j)
		{
			j -= k ;
			k /= 2 ;
		}
		j += k ;
	}

	le = 1 ;
	for (l= 1 ; l <= x ; l ++)
	{
		le *= 2 ;
   		le1 = le / 2 ;
		u.real = 1.0f ;
		u.image = 0.0f ;
		w.real = (float) cos(PI / le1) ;
		w.image =(float) -sin(PI / le1) ;

		for (j = 1 ; j <= le1 ; j ++)
		{
			for (i = j ; i <= n ; i += le)
			{
				ip = i + le1 ;
				t.real = input[ip - 1].real * u.real - input[ip - 1].image * u.image ;
				t.image = input[ip - 1].real * u.image + input[ip - 1].image * u.real ;
				input[ip - 1].real = input[i - 1].real - t.real ;
				input[ip - 1].image = input[i - 1].image - t.image ;
				input[i - 1].real = t.real + input[i - 1].real ;
				input[i - 1].image = t.image + input[i - 1].image ;
			}

			t.real = u.real * w.real - u.image * w.image ;
			t.image = u.image * w.real + u.real * w.image ;
			u.real = t.real ;
      		u.image = t.image ;
		}
	}
}

/*=====================================================
函数名称:	FFTAm
函数功能:	求取FFT变换后的幅度 s(i) = x(i)^ 2 + y(i)^ 2
输入参数:	x 原始FFT复数点 ,
			y 输出的存储FFT幅度的序列,由于对称所以只需算一半,
			m FFT的级数 
返回参数:	无 
====================================================*/
void FFTAm(COMPLEX *x , float *y, long m)
{
	long n,i;
	n = 1 ;
	for (i = 0 ; i < m ; i ++)
		n *= 2 ;
	for (i = 0 ; i < n/2 ; i ++)
      y[i] =(float) (x[i].real * x[i].real + x[i].image * x[i].image) ;
}

/*=====================================================
函数名称:	AreaFreqComp
函数功能:	计算区段频域的能量								
输入参数:	fFFTAm[MAX_FREQ_POINT]		FFT能量序列 	
输出参数:	fAreaEnergy					区段频率的能量										
====================================================*/
void	AreaFreqComp(float fFFTAm[MAX_FREQ_POINT], float &fAreaEnergy)
{
	short wFFTCount;
	
	//计算出与频率相对应的频点
	static short	lowFreq = (short) (LOW_FREQ_AREA / SAMPLE_FREQUENCY * FFT_NUM);
	static short	highFreq = (short) (HIGH_FREQ_AREA / SAMPLE_FREQUENCY * FFT_NUM);

	fAreaEnergy = 0;
	for(wFFTCount=lowFreq; wFFTCount<highFreq; wFFTCount++)
		fAreaEnergy += fFFTAm[wFFTCount];
	fAreaEnergy = (float)log(fAreaEnergy);
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -