Speech.cpp
上传用户:goak128
上传日期:2013-07-17
资源大小:155k
文件大小:9k
- #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;
- }