HCopy源码,提取MFCC特征部分函数 调用关系记录

本文记录了调试HCopy源码过程中,关于提取MFCC特征的部分函数调用关系。从OpenSpeechFile开始,详细介绍了OpenWaveInput、SetUpForCoding、FillBufFromChannel等函数的作用,涉及音频文件处理、预处理、快速傅里叶变换、梅尔滤波器和MFCC计算等步骤。文章适合对语音处理和HTK有一定了解的读者。
摘要由CSDN通过智能技术生成

前期准备

  • HTK代码开源,http://htk.eng.cam.ac.uk/, 注册账号,阅读并同意许可协议,申请下载源码即可。
  • 笔者下载的是3.4.1版本。使用VS2013编译源码,具体步骤请参考这篇博客:HTK3.4.1在VS2013建立工程编译(在此感谢博主)。

配置文件

  1. config文件,指定所有转换参数
    #[MODULE]	PARAMETER		= VALUE
    
    SOURCEKIND		= WAVEFORM
    
    TARGETKIND 		= MFCC_0_D_A
    TARGETRATE 		= 100000.0	
    SOURCEFORMAT 		= WAV
    SAVECOMPRESSSED 	= T
    SAVEWITHCRC 		= T
    ZMEANSOURCE 		= T	
    
    WINDOWSIZE 		= 250000.0	
    USEHAMMING 		= T
    PREEMCOEF 		= 0.97		
    NUMCHANS 		= 26		
    
    CEPLIFTER 		= 22		
    NUMCEPS 		= 12		
    ENORMALISE 		= F			
    
  2. 源文件及其相应输出文件的列表
    S0001.wav	S0001.mfc
    S0002.wav	S0002.mfc
    S0003.wav	S0003.mfc
    (etc.)
    

源码之函数调用关系浅析

1. HCopy的main函数可以划分成3部分
  1. 初始化
InitMem();   InitLabel();
………… ………… (etc.)
CreateHeap(&tStack, "Trace", MSTAK, 1, 0.0, 100, 200);
  1. 根据命令行参数做相应处理
while (NextArg() == SWITCHARG)
{
	s = GetSwtArg();
	switch (s[0])
	{
	case 'a':
	…………(etc.)
	…………(etc.)
	default:
	}
}
  1. 根据命令行参数,对输入进行处理(这是本文解析的重点)及收尾处理
while (NumArgs() > 1)
{ /* process group S1 + S2 + ... TGT */
	off = 0.0;
	s = GetStrArg();
	OpenSpeechFile(s); 
	…………(etc.)
	if (trans != NULL)
	{
		trans = NULL;
		ResetHeap(&lStack);
	}
	ResetHeap(&iStack);
	ResetHeap(&oStack);
	if (chopF) ResetHeap(&cStack);
}
if (useMLF) CloseMLFSaveFile();
if (NumArgs() != 0) HError(-1019, "HCopy: Unused args ignored");
Exit(0);
return (0); 
2. HCopy对输入音频文件的处理
  1. 在main函数 “3. 根据命令行参数,对输入进行处理” 中调用OpenSpeechFile(char *s)函数,处理输入的wav文件或参数文件。char *s是文件名,即在config文件中罗列的输入文件名。
  2. OpenSpeechFile调用OpenParmFile
  3. OpenParmFile调用OpenBuffer
  4. OpenBuffer调用OpenAsChannel
  5. OpenAsChannel函数的作用是打开并创建一个音频输入缓冲区。
    1. OpenAsChannel调用OpenWaveInput函数,该函数的作用是打开输入的wav文件并读取文件信息。
      1. OpenWaveInput函数
        1. 调用GetWAVHeaderInfo读取wav文件头信息(编码方式、采样率、位深etc.),并返回音频数据的长度。

          wav文件的具体格式可以参考我的另一篇博客:wav文件分析(实例+代码)

        typedef enum
        {
        	DoCVT = 1,     /* input conversion needed */
        	DoBSWAP = 2,     /* byte swap needed */
        	DoSPACK = 4,     /* SHORT PACK decompression needed */
        	DoSHORT = 8,     /* SHORTEN decompression needed */
        	DoMULAW = 16,    /* 8 bit Mu-Law expansion needed */
        	DoALAW = 32,    /* 8 bit A-Law expansion needed */
        	Do8_16 = 64,    /* 8 bit PCM expansion needed */
        	DoSTEREO = 128     /* Convert stereo to mono*/
        }InputAction;
        
        InputAction ia = (InputAction)0;      /* flags to enable conversions etc */
        
        /* GetWAVHeaderInfo: get header for Microsoft WAVE format sound file */
        static long GetWAVHeaderInfo(FILE *f, Wave w, InputAction *ia)
        {
        	char magic[4];
        	int32 len, lng, numBytes;
        	char c;
        	short sht, sampSize, type, chans;
        
        	if (MustSwap(VAXSO))
        		*ia = (InputAction)(*ia | DoBSWAP);
        
        	fread(magic, 4, 1, f);
        	if (strncmp("RIFF", magic, 4))
        	{
        		HRError(6251, "Input file is not in RIFF format");
        		return -1;
        	}
        
        	fread(&lng, 4, 1, f);
        	fread(magic, 4, 1, f);
        	if (strncmp("WAVE", magic, 4))
        	{
        		HRError(6251, "Input file is not in WAVE format");
        		return -1;
        	}
        
        	/* Look for "fmt " before end of file */
        	while (1)
        	{
        		if (feof(f))
        		{
        			HRError(6251, "No data portion in WAVE file");
        			return -1;
        		}
        		fread(magic, 4, 1, f); // fmt 
        		fread(&len, 4, 1, f); 
        		if (*ia & DoBSWAP) SwapInt32(&len);
        		/* Check for data chunk */
        		if (strncmp("data", magic, 4) == 0) break;
        		if (strncmp("fmt ", magic, 4) == 0)
        		{
        			fread(&type, 2, 1, f); // 十进制是1 线性的PCM编码
        			if (*ia & DoBSWAP) SwapShort(&type);
        			if (type != WAVE_FORMAT_PCM && type != WAVE_FORMAT_MULAW && type != WAVE_FORMAT_ALAW)
        			{
        				HRError(6251, "Only standard PCM, mu-law & a-law supported");
        				return -1;
        			}
        			if (type == WAVE_FORMAT_MULAW)
        				*ia = (InputAction)(*ia | (DoMULAW | DoCVT));
        			else if (type == WAVE_FORMAT_ALAW)
        				*ia = (InputAction)(*ia | (DoALAW | DoCVT));
        			fread(&chans, 2, 1, f); /* Number of Channels */
        			if (*ia & DoBSWAP) SwapShort(&chans);
        			if (chans != 1 && chans != 2)
        			{
        				HRError(6251, "Neither mono nor stereo!");
        				return -1;
        			}
        			if (chans == 2)
        				*ia = (InputAction)(*ia | (DoSTEREO | DoCVT));
        			fread(&lng, 4, 1, f);   /* Sample Rate */
        			if (*ia & DoBSWAP) SwapInt32(&lng);
        			w->sampPeriod = 1.0E7 / (float)lng;
        			fread(&lng, 4, 1, f);                  /* Average bytes/second */
        	
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值