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;
}