Speech.cpp
上传用户:goak128
上传日期:2013-07-17
资源大小:155k
文件大小:9k
源码类别:

控制台编程

开发平台:

C/C++

  1. #include "StdAfx.h"
  2. #include ".speech.h"
  3. CSpeech::CSpeech(void)
  4. {
  5. }
  6. CSpeech::~CSpeech(void)
  7. {
  8. }
  9. //////////////////////////////////////////////////////////////////////////
  10. // 对输入数据加窗
  11. void CSpeech::AddWindow( 
  12. unsigned int nWinSize, // 处理窗宽度
  13. double* pData, // 输入数据
  14. unsigned int nInLen // 输入数据长度
  15. )
  16. {
  17. // 计算处理次数
  18. unsigned int nCount = nInLen / nWinSize;
  19. unsigned int nOffSet = 0;
  20. for (unsigned int i = 0; i < nCount; i++)
  21. {
  22. nOffSet = i * nWinSize;
  23. for (unsigned int j = 0; j < nWinSize; j++)
  24. {
  25. pData[nOffSet + j] = 
  26. pData[nOffSet + j] * CSpeech::HammingWinFunc(j, nWinSize);
  27. }
  28. }
  29. }
  30. //////////////////////////////////////////////////////////////////////////
  31. // 对输入数据加窗
  32. void CSpeech::AddWindow(
  33. unsigned int nWinSize, // 处理窗宽度
  34. const double* pDataIn, // 输入数据
  35. unsigned int nInLen, // 输入数据长度
  36. double* pDataOut // 输出数据
  37. )
  38. {
  39. // 拷贝输入数据到输出数据
  40. memcpy(pDataOut, pDataIn, sizeof(double) * nInLen);
  41. // 对输入数据加窗
  42. CSpeech::AddWindow(nWinSize, pDataOut, nInLen);
  43. }
  44. //////////////////////////////////////////////////////////////////////////
  45. // 输入信号序列的短时平均过零率,输入数据为加窗后的数据
  46. void CSpeech::GetZero( 
  47.   unsigned int nWinSize, /*处理窗口大小*/
  48.   const double* pDataIn, /*输入信号序列*/
  49.   unsigned int nInLen, /*输入信号序列长度*/
  50.   double* pDataOut /*输出过零率序列*/
  51.   )
  52. {
  53. // 保存窗口函数值
  54. // double nWinValue = 0;
  55. unsigned int nOutLen = nInLen / nWinSize;
  56. unsigned int nOffSet = 0;
  57. // 清空输出序列
  58. memset(pDataOut, 0, sizeof(double)* nOutLen);
  59. // 利用海明窗作为窗口函数计算短时平均过零率
  60. for (unsigned int i = 0; i < nOutLen; i ++)
  61. {
  62. nOffSet = i * nWinSize;
  63. // 计算当前窗口的过零率
  64. for (unsigned int j = 0; j < (nWinSize - 1); j ++)
  65. {
  66. // 计算窗口函数值
  67. //nWinValue = HammingWinFunc(j, nWinSize);
  68. pDataOut[i] += 
  69. abs(Sgn(pDataIn[nOffSet + j]) - Sgn(pDataIn[nOffSet + j + 1]));
  70. // abs(Sgn(pDataIn[nOffSet + j]) - Sgn(pDataIn[nOffSet + j + 1])) * nWinValue;
  71. }
  72. }
  73. }
  74. //////////////////////////////////////////////////////////////////////////
  75. // 计算输入信号序列的短时能量,输入数据为加窗后的数据
  76. void CSpeech::GetEnergy( 
  77. unsigned int nWinSize, /*处理窗口大小*/
  78. const double* pDataIn, /*输入信号序列*/
  79. unsigned int nInLen, /*输入信号序列长度*/
  80. double* pDataOut /*输出短时能量序列*/
  81. )
  82. {
  83. // 保存窗口函数值
  84. // double nWinValue = 0;
  85. unsigned int nOutLen = nInLen / nWinSize;
  86. unsigned int nOffSet = 0;
  87. // 清空输出序列
  88. memset(pDataOut, 0, sizeof(double)* nOutLen);
  89. // 若输出序列和输出序列可用
  90. if (pDataIn != NULL && pDataOut != NULL)
  91. {
  92. // 利用海明窗作为窗口函数计算短时能量
  93. for (unsigned int i = 0; i < nOutLen; i ++)
  94. {
  95. nOffSet = i * nWinSize;
  96. // 计算当前窗口的过零率
  97. for (unsigned int j = 0; j < nWinSize; j ++)
  98. {
  99. // 计算窗口函数值
  100. // nWinValue = HammingWinFunc(j, nWinSize);
  101. // pDataOut[i] += pow(pDataIn[nOffSet + j], 2) * nWinValue;
  102. pDataOut[i] += pow(pDataIn[nOffSet + j], 2);
  103. }
  104. }
  105. }
  106. }
  107. //////////////////////////////////////////////////////////////////////////
  108. // 计算输入信号序列的平均振幅,输入数据为加窗后的数据
  109. void CSpeech::GetAvgVibration(
  110.   unsigned int nWinSize, /*处理窗口大小*/
  111.   const double* pDataIn, /*输入信号序列*/
  112.   unsigned int nInLen, /*输入信号序列长度*/
  113.   double* pDataOut /*输出平均振幅序列*/
  114.   )
  115. {
  116. // 保存窗口函数值
  117. // double nWinValue = 0;
  118. unsigned int nOutLen = nInLen / nWinSize;
  119. unsigned int nOffSet = 0;
  120. // 清空输出序列
  121. memset(pDataOut, 0, sizeof(double)* nOutLen);
  122. // 若输出序列和输出序列可用
  123. if (pDataIn != NULL && pDataOut != NULL)
  124. {
  125. // 利用海明窗作为窗口函数计算平均振幅
  126. for (unsigned int i = 0; i < nOutLen; i ++)
  127. {
  128. nOffSet = i * nWinSize;
  129. // 计算当前窗口的过零率
  130. for (unsigned int j = 0; j < nWinSize; j ++)
  131. {
  132. // 计算窗口函数值
  133. // nWinValue = HammingWinFunc(j, nWinSize);
  134. // pDataOut[i] += abs(pDataIn[nOffSet + j]) * nWinValue;
  135. pDataOut[i] += abs(pDataIn[nOffSet + j]);
  136. }
  137. }
  138. }
  139. }
  140. //////////////////////////////////////////////////////////////////////////
  141. // 对输入采样序列进行端点检测,端点检测时的K1,k2和具体环境有关
  142. // 不具备普遍意义。PEnergy指的是平均振幅
  143. unsigned int CSpeech::SubSection(
  144. const double* pDataIn, // 输入采样序列
  145. unsigned int nInLen, // 输入采样数
  146. const double* pEnergy, // 输入样本序列分帧的短时能量
  147. const double* pZero, // 输入样本序列分帧的短时过零率
  148. unsigned int nFrameSize,// 每帧包含采样数
  149. double* pDataOut, // 去掉寂静帧后的采样序列
  150. unsigned int nWinSize // 滑动窗包含帧数(统计窗长度)
  151. )
  152. {
  153. // 起始能量门限值
  154. double fBeginEnergyDoor = 0;
  155. // 结束能量门限值
  156. double fEndEnergyDoor = 0;
  157. // 过零率门限值
  158. double fZeroDoor = 0;
  159. // 滑动窗内符合能量门限值的帧数
  160. unsigned int nEnergyCount = 0;
  161. // 滑动窗内符合过零率门限值的帧数
  162. unsigned int nZeroCount = 0;
  163. // 连续滑动帧法中的IF
  164. double fIF = 20;
  165. // 计算前10帧过零率均值
  166. double fAvgZero = 0;
  167. // 计算前10帧的样本方差
  168. double fVar = 0;
  169. // 最小能量
  170. double fMinEnergy = 0;
  171. // 最大能量
  172. double fMaxEnergy = 0;
  173. // 连续滑动帧算法中的I1
  174. double fEnergyI1 = 0;
  175. // 连续滑动帧算法中的I2
  176. double fEnergyI2 = 0;
  177. // 循环变量
  178. unsigned int i = 0; 
  179. unsigned int j = 0;
  180. // 输入样本序列分帧数
  181. unsigned int nFrameNums = nInLen / nFrameSize;
  182. // 进行帧统计时的,若条件判断失败,进行偏移时的偏移窗大小(窗间叠接)
  183. unsigned int nStep = 10;
  184. // 语音起始点,按帧
  185. unsigned int nBegin = 0;
  186. unsigned int nEnd = nFrameNums - 1;
  187. // 端点前测后的字节数
  188. unsigned int nRetLen = 0;
  189. if (pDataIn != NULL)
  190. {
  191. // 计算前10帧的过零率均值
  192. for (i = 0; i < 10; i++)
  193. {
  194. fAvgZero += pZero[i];
  195. }
  196. fAvgZero /= 10;
  197. // 计算前10帧的样本方差
  198. for (i = 0; i < 10; i++)
  199. {
  200. fVar += pow((pZero[i] - fAvgZero), 2);
  201. }
  202. fVar = sqrt(fVar / 10);
  203. // 计算过零率门限值
  204. fZeroDoor = fAvgZero + 2 * fVar;
  205. fZeroDoor = (fIF < fZeroDoor ? fIF : fZeroDoor);
  206. // 求最小能量和最大能量
  207. fMinEnergy = pEnergy[i];
  208. fMaxEnergy = pEnergy[i];
  209. for (i = 0; i < nFrameNums; i++)
  210. {
  211. fMinEnergy = (pEnergy[i] < fMinEnergy ? pEnergy[i] : fMinEnergy);
  212. fMaxEnergy = (fMaxEnergy < pEnergy[i] ? pEnergy[i] : fMaxEnergy);
  213. }
  214. // 计算能量门限值
  215. fEnergyI1 = 0.03 * (fMaxEnergy - fMinEnergy) + fMinEnergy;
  216. fEnergyI2 = 4 * fMinEnergy;
  217. fBeginEnergyDoor = 5 * (fEnergyI1 < fEnergyI2 ? fEnergyI1 : fEnergyI2);
  218. // 计算结束能量门限值,一般来说结束能量门限都比起始能量小,
  219. // 这里的处理方法是令 结束能量门限 = K * 开始能量门限,k按环境取值,不具有普遍意义
  220. fEndEnergyDoor = 0.7 * fBeginEnergyDoor;
  221. // 进行端点检测
  222. // 判断语音起点
  223. // 判断条件为nEnergyCount<20 and nZeroCount>10
  224. // 或nEnergyCount<15
  225. for (i = 0; i < nFrameNums; i+= nStep)
  226. {
  227. // 统计符合能量和过零率条件的帧数
  228. nZeroCount = 0; 
  229. nEnergyCount = 0;
  230. for (j = 0; j < nWinSize; j++)
  231. {
  232. nZeroCount = 
  233. (fZeroDoor < pZero[i + j] ? nZeroCount + 1 : nZeroCount);
  234. nEnergyCount = 
  235. (pEnergy[i + j] < fBeginEnergyDoor ? nEnergyCount + 1 : nEnergyCount);
  236. }
  237. // 判断是否找到起点
  238. if ((nEnergyCount < 20 && nZeroCount > 10) || 
  239. nEnergyCount < 15)
  240. {
  241. nBegin = i;
  242. break;
  243. }
  244. }
  245. // 判断语音终点
  246. // 统计窗向前推移3000点
  247. for (i = (nBegin + 3000 / nFrameSize); i < nFrameNums; i+= nStep)
  248. {
  249. // 统计符合能量和过零率条件的帧数
  250. nZeroCount = 0; 
  251. nEnergyCount = 0;
  252. for (j = 0; j < nWinSize; j++)
  253. {
  254. nZeroCount = 
  255. (pZero[i + j] < fZeroDoor ? nZeroCount + 1 : nZeroCount);
  256. nEnergyCount = 
  257. (pEnergy[i + j] < fEndEnergyDoor ? nEnergyCount + 1 : nEnergyCount);
  258. }
  259. // 判断是否找到起点
  260. if ((nEnergyCount > 20 && nZeroCount < 10) || 
  261. nEnergyCount > 40)
  262. {
  263. nEnd = i;
  264. break;
  265. }
  266. }
  267. // 拷贝端点检测后数据
  268. nRetLen = (nEnd - nBegin) * nFrameSize;
  269. memcpy(pDataOut, &pDataIn[nBegin * nFrameSize], sizeof(double) * nRetLen);
  270. }
  271. return nRetLen;
  272. }
  273. //////////////////////////////////////////////////////////////////////////
  274. // 对输入采样序列进行端点检测
  275. unsigned int CSpeech::SubSection(
  276.  const double* pDataIn, // 输入采样序列,已加窗
  277.  unsigned int nInLen, // 输入采样数
  278.  unsigned int nFrameSize, // 每帧包含采样数
  279.  double* pDataOut, // 去掉寂静帧后的采样序列
  280.  unsigned int nWinSize // 统计窗窗包含帧数
  281.  )
  282. {
  283. // 平均振幅
  284. double* pVibration = NULL;
  285. // 短时平均过零率
  286. double* pZero = NULL;
  287. // 计算输入数据包含帧数
  288. unsigned int nFrameNums = nInLen / nFrameSize;
  289. // 端点检测后剩下的采样点数
  290. unsigned int nRetLen = 0;
  291. if (pDataIn != NULL)
  292. {
  293. // 计算平均振幅
  294. pVibration = new double[nFrameNums];
  295. CSpeech::GetAvgVibration(nFrameSize, pDataIn, nInLen, pVibration);
  296. // 计算短时平均过零率
  297. pZero = new double[nFrameNums];
  298. CSpeech::GetZero(nFrameSize, pDataIn, nInLen, pZero);
  299. // 进行端点检测 
  300. nRetLen = CSpeech::SubSection(
  301. pDataIn, nInLen, pVibration, pZero, nFrameSize, pDataOut, nWinSize);
  302. // 释放所占用资源
  303. delete[] pZero;
  304. delete[] pVibration;
  305. }
  306. return nRetLen;
  307. }