www.pudn.com > hmmPlatform.rar > Speech.cpp


#include "StdAfx.h" 
#include ".\speech.h" 
 
CSpeech::CSpeech(void) 
{ 
} 
 
CSpeech::~CSpeech(void) 
{ 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 对输入数据加窗 
void CSpeech::AddWindow(  
						unsigned int nWinSize,		// 处理窗宽度 
						double* pData,				// 输入数据 
						unsigned int nInLen			// 输入数据长度 
						) 
{ 
	// 计算处理次数 
	unsigned int nCount = nInLen / nWinSize; 
	unsigned int nOffSet = 0; 
 
	for (unsigned int i = 0; i < nCount; i++) 
	{ 
		nOffSet = i * nWinSize; 
 
		for (unsigned int j = 0; j < nWinSize; j++) 
		{ 
			pData[nOffSet + j] =  
				pData[nOffSet + j] * CSpeech::HammingWinFunc(j, nWinSize); 
		} 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 对输入数据加窗 
void CSpeech::AddWindow( 
						unsigned int nWinSize,		// 处理窗宽度 
						const double* pDataIn,		// 输入数据 
						unsigned int nInLen,		// 输入数据长度 
						double* pDataOut			// 输出数据 
						) 
{ 
	// 拷贝输入数据到输出数据 
	memcpy(pDataOut, pDataIn, sizeof(double) * nInLen); 
 
	// 对输入数据加窗 
	CSpeech::AddWindow(nWinSize, pDataOut, nInLen); 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 输入信号序列的短时平均过零率,输入数据为加窗后的数据 
void CSpeech::GetZero(  
					  unsigned int nWinSize,		/*处理窗口大小*/ 
					  const double* pDataIn,		/*输入信号序列*/ 
					  unsigned int nInLen,			/*输入信号序列长度*/ 
					  double* pDataOut				/*输出过零率序列*/ 
					  ) 
{ 
	// 保存窗口函数值 
	// double nWinValue = 0; 
	unsigned int nOutLen = nInLen / nWinSize; 
	unsigned int nOffSet = 0; 
 
	// 清空输出序列 
	memset(pDataOut, 0, sizeof(double)* nOutLen); 
 
	// 利用海明窗作为窗口函数计算短时平均过零率 
	for (unsigned int i = 0; i < nOutLen; i ++) 
	{ 
		nOffSet = i * nWinSize; 
 
		// 计算当前窗口的过零率 
		for (unsigned int j = 0; j < (nWinSize - 1); j ++) 
		{ 
			// 计算窗口函数值 
			//nWinValue = HammingWinFunc(j, nWinSize); 
 
			pDataOut[i] +=  
				abs(Sgn(pDataIn[nOffSet + j]) - Sgn(pDataIn[nOffSet + j + 1])); 
				// abs(Sgn(pDataIn[nOffSet + j]) - Sgn(pDataIn[nOffSet + j + 1])) * nWinValue; 
		} 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 计算输入信号序列的短时能量,输入数据为加窗后的数据 
void CSpeech::GetEnergy(  
						unsigned int nWinSize,			/*处理窗口大小*/ 
						const double* pDataIn,				/*输入信号序列*/ 
						unsigned int nInLen,			/*输入信号序列长度*/ 
						double* pDataOut				/*输出短时能量序列*/ 
						) 
{ 
	// 保存窗口函数值 
	// double nWinValue = 0; 
	unsigned int nOutLen = nInLen / nWinSize; 
	unsigned int nOffSet = 0; 
 
	// 清空输出序列 
	memset(pDataOut, 0, sizeof(double)* nOutLen); 
 
	// 若输出序列和输出序列可用 
	if (pDataIn != NULL && pDataOut != NULL) 
	{ 
		// 利用海明窗作为窗口函数计算短时能量 
		for (unsigned int i = 0; i < nOutLen; i ++) 
		{ 
			nOffSet = i * nWinSize; 
 
			// 计算当前窗口的过零率 
			for (unsigned int j = 0; j < nWinSize; j ++) 
			{ 
				// 计算窗口函数值 
				// nWinValue = HammingWinFunc(j, nWinSize); 
 
				// pDataOut[i] += pow(pDataIn[nOffSet + j], 2) * nWinValue; 
				pDataOut[i] += pow(pDataIn[nOffSet + j], 2); 
			} 
		} 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 计算输入信号序列的平均振幅,输入数据为加窗后的数据 
void CSpeech::GetAvgVibration( 
							  unsigned int nWinSize,		/*处理窗口大小*/ 
							  const double* pDataIn,				/*输入信号序列*/ 
							  unsigned int nInLen,			/*输入信号序列长度*/ 
							  double* pDataOut				/*输出平均振幅序列*/ 
							  ) 
{ 
	// 保存窗口函数值 
	// double nWinValue = 0; 
	unsigned int nOutLen = nInLen / nWinSize; 
	unsigned int nOffSet = 0; 
 
	// 清空输出序列 
	memset(pDataOut, 0, sizeof(double)* nOutLen); 
 
	// 若输出序列和输出序列可用 
	if (pDataIn != NULL && pDataOut != NULL) 
	{ 
		// 利用海明窗作为窗口函数计算平均振幅 
		for (unsigned int i = 0; i < nOutLen; i ++) 
		{ 
			nOffSet = i * nWinSize; 
 
			// 计算当前窗口的过零率 
			for (unsigned int j = 0; j < nWinSize; j ++) 
			{ 
				// 计算窗口函数值 
				// nWinValue = HammingWinFunc(j, nWinSize); 
 
				// pDataOut[i] += abs(pDataIn[nOffSet + j]) * nWinValue; 
				pDataOut[i] += abs(pDataIn[nOffSet + j]); 
			} 
		} 
	} 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 对输入采样序列进行端点检测,端点检测时的K1,k2和具体环境有关 
// 不具备普遍意义。PEnergy指的是平均振幅 
unsigned int CSpeech::SubSection( 
					const double* pDataIn,	// 输入采样序列 
					unsigned int nInLen,	// 输入采样数 
					const double* pEnergy,	// 输入样本序列分帧的短时能量 
					const double* pZero,	// 输入样本序列分帧的短时过零率 
					unsigned int nFrameSize,// 每帧包含采样数 
					double* pDataOut,		// 去掉寂静帧后的采样序列 
					unsigned int nWinSize	// 滑动窗包含帧数(统计窗长度) 
					) 
{ 
	// 起始能量门限值 
	double fBeginEnergyDoor = 0; 
	// 结束能量门限值 
	double fEndEnergyDoor = 0; 
	// 过零率门限值 
	double fZeroDoor = 0; 
	// 滑动窗内符合能量门限值的帧数 
	unsigned int nEnergyCount = 0; 
	// 滑动窗内符合过零率门限值的帧数 
	unsigned int nZeroCount = 0; 
	// 连续滑动帧法中的IF 
	double fIF = 20; 
	// 计算前10帧过零率均值 
	double fAvgZero = 0; 
	// 计算前10帧的样本方差 
	double fVar = 0; 
	// 最小能量 
	double fMinEnergy = 0; 
	// 最大能量 
	double fMaxEnergy = 0; 
	// 连续滑动帧算法中的I1 
	double fEnergyI1 = 0; 
	// 连续滑动帧算法中的I2 
	double fEnergyI2 = 0; 
	// 循环变量 
	unsigned int i = 0;  
	unsigned int j = 0; 
	// 输入样本序列分帧数 
	unsigned int nFrameNums = nInLen / nFrameSize; 
	// 进行帧统计时的,若条件判断失败,进行偏移时的偏移窗大小(窗间叠接) 
	unsigned int nStep = 10; 
	// 语音起始点,按帧 
	unsigned int nBegin = 0; 
	unsigned int nEnd = nFrameNums - 1;	 
	// 端点前测后的字节数 
	unsigned int nRetLen = 0; 
 
	if (pDataIn != NULL) 
	{ 
		// 计算前10帧的过零率均值 
		for (i = 0; i < 10; i++) 
		{ 
			fAvgZero += pZero[i]; 
		} 
 
		fAvgZero /= 10; 
 
		// 计算前10帧的样本方差 
		for (i = 0; i < 10; i++) 
		{ 
			fVar += pow((pZero[i] - fAvgZero), 2); 
		} 
 
		fVar = sqrt(fVar / 10); 
 
		// 计算过零率门限值 
		fZeroDoor = fAvgZero + 2 * fVar; 
		fZeroDoor = (fIF < fZeroDoor ? fIF : fZeroDoor); 
 
		// 求最小能量和最大能量 
		fMinEnergy = pEnergy[i]; 
		fMaxEnergy = pEnergy[i]; 
		for (i = 0; i < nFrameNums; i++) 
		{ 
			fMinEnergy = (pEnergy[i] < fMinEnergy ? pEnergy[i] : fMinEnergy); 
			fMaxEnergy = (fMaxEnergy < pEnergy[i] ? pEnergy[i] : fMaxEnergy); 
		} 
 
		// 计算能量门限值 
		fEnergyI1 = 0.03 * (fMaxEnergy - fMinEnergy) + fMinEnergy; 
		fEnergyI2 = 4 * fMinEnergy; 
		fBeginEnergyDoor = 5 * (fEnergyI1 < fEnergyI2 ? fEnergyI1 : fEnergyI2); 
 
		// 计算结束能量门限值,一般来说结束能量门限都比起始能量小, 
		// 这里的处理方法是令 结束能量门限 = K * 开始能量门限,k按环境取值,不具有普遍意义 
		fEndEnergyDoor = 0.7 * fBeginEnergyDoor; 
 
		// 进行端点检测 
		// 判断语音起点 
		// 判断条件为nEnergyCount<20 and nZeroCount>10 
		// 或nEnergyCount<15 
		for (i = 0; i < nFrameNums; i+= nStep) 
		{ 
			// 统计符合能量和过零率条件的帧数 
			nZeroCount = 0;  
			nEnergyCount = 0; 
 
			for (j = 0; j < nWinSize; j++) 
			{ 
				nZeroCount =  
					(fZeroDoor < pZero[i + j] ? nZeroCount + 1 : nZeroCount); 
				nEnergyCount =  
					(pEnergy[i + j] < fBeginEnergyDoor ? nEnergyCount + 1 : nEnergyCount); 
			} 
 
			// 判断是否找到起点 
			if ((nEnergyCount < 20 && nZeroCount > 10) ||  
				nEnergyCount < 15) 
			{ 
				nBegin = i; 
 
				break; 
			} 
		} 
		 
		// 判断语音终点 
		// 统计窗向前推移3000点 
		for (i = (nBegin + 3000 / nFrameSize); i < nFrameNums; i+= nStep) 
		{ 
			// 统计符合能量和过零率条件的帧数 
			nZeroCount = 0;  
			nEnergyCount = 0; 
 
			for (j = 0; j < nWinSize; j++) 
			{ 
				nZeroCount =  
					(pZero[i + j] < fZeroDoor ? nZeroCount + 1 : nZeroCount); 
				nEnergyCount =  
					(pEnergy[i + j] < fEndEnergyDoor ? nEnergyCount + 1 : nEnergyCount); 
			} 
 
			// 判断是否找到起点 
			if ((nEnergyCount > 20 && nZeroCount < 10) ||  
				nEnergyCount > 40) 
			{ 
				nEnd = i; 
 
				break; 
			} 
		} 
 
		// 拷贝端点检测后数据 
		nRetLen = (nEnd - nBegin) * nFrameSize; 
		memcpy(pDataOut, &pDataIn[nBegin * nFrameSize], sizeof(double) * nRetLen); 
	} 
 
	return nRetLen; 
} 
 
////////////////////////////////////////////////////////////////////////// 
// 对输入采样序列进行端点检测 
unsigned int CSpeech::SubSection( 
								 const double* pDataIn,		// 输入采样序列,已加窗 
								 unsigned int nInLen,		// 输入采样数 
								 unsigned int nFrameSize,	// 每帧包含采样数 
								 double* pDataOut,			// 去掉寂静帧后的采样序列 
								 unsigned int nWinSize		// 统计窗窗包含帧数 
								 ) 
{ 
	// 平均振幅 
	double* pVibration = NULL; 
	// 短时平均过零率 
	double* pZero = NULL; 
	// 计算输入数据包含帧数 
	unsigned int nFrameNums = nInLen / nFrameSize; 
	// 端点检测后剩下的采样点数 
	unsigned int nRetLen = 0; 
 
	if (pDataIn != NULL) 
	{ 
		// 计算平均振幅 
		pVibration = new double[nFrameNums]; 
		CSpeech::GetAvgVibration(nFrameSize, pDataIn, nInLen, pVibration); 
		// 计算短时平均过零率 
		pZero = new double[nFrameNums]; 
		CSpeech::GetZero(nFrameSize, pDataIn, nInLen, pZero); 
 
		// 进行端点检测  
		nRetLen = CSpeech::SubSection( 
			pDataIn, nInLen, pVibration, pZero, nFrameSize, pDataOut, nWinSize); 
 
		// 释放所占用资源 
		delete[] pZero; 
		delete[] pVibration; 
	} 
 
	return nRetLen; 
}