📄 mfcc_xhg.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 + -