VAD实现 (三) --- 算法计算流程与框架

在实现VAD算法之前,先给出在这里VAD算法的实现流程和算法框架。

调用关系依次是 detect_wav -> detect_frame->process_vad->energy_detect,energy_detect比较复杂,所以暂时不在这里,这只是表示出基本的计算流程和框架

int frame_size = 256 ; 
int sample_rate = 8000 ;//  采样频率
int frame_step =  80;
int msec_per_frame  = (int) 1000 * frame_size/sample_rate; // 一帧占据多少毫秒
int silence_filter_len = 30 ; //由语音进入静音之前的过渡区域,这是一个灰度区域
int speech_filter_len = 30 ; // 由静音进入语音前的过渡区域

int number_of_silence = 0; // 当前处理的静音长度。
int number_of_speech = 0;  // 当前处理的语音长度。
int total_of_silence = 0;  //状态切换之前的静音长度
int total_of_speech = 0;   //状态切换之前的语音长度

int silence_count = 0; //在当前处理时,合法的数据被接受,可以正式计数
int speech_count = 0;

int frame_count = 0;

上面是申明的一些全局变量。

/**
*
* data:整个WAV文件的数据。nsample:WAV文件的采样数。这两个值都是可以从返回的
*
**/
int detect_wav(short *data,int nsample){
	int state = state_SILENCE ;  //初始状态:静音
	int index = 0,offset = 0;
	int cst = 0; // 当前状态时间,暂时以帧表示 
	//index会返回当前是处理的第几帧,这和第几个采样点是不一样的
	int ret = detect_frame(data,&index); // 从第一个帧开始处理,配置每一帧256个采样点,ret返回当前帧的状态,静音、结束、语音等。

	while(ret != state_OVER){  // 直到最后一帧,也就是处理结束。
		if(ret != state_WAIT){  //是否返回静音或者语音段
			if(ret  != st ){ // 返回状态和上一状态不相同
				int k ;
				if(ret == state_SILENCE){
					k = index * frame_step -  silence_filter_len * frame_step ;//过渡区域计为本次的静音。
					if(k <= cst){ //
						ret = st;
						continue;
					}
					//输出静音段
					printf("%3.2f %3.2f Speech\n",cst/(float)sample_rate,k/(float)sample_rate);
				}else{
					k = index * frame_step - speech_filter_len  * frame_step;//之前的过渡区域计为本次的语音段
					if(k <= cst){
						ret = st;
						continue;
					}
					printf("%3.2f %3.2f Silence\n", cst/(float)sample_rate, ((k/(float)sample_rate)>0?(k/(float)sample_rate):0.00));
				}
				cst = k;   //保留上次计算的K 值。
				st = ret ; //保留上次状态值
			}
		}
		// 准备处理下一帧
		offset += frame_step ; // 移动帧的起始位置,可以由此计算帧的重叠
		if(offset <= nsample - frame_size){
			ret = detect_frame(data+offset,&index);
		}else{
			ret = detect_frame(NULL,&index);
		}
	}
        // 这里需要输出最后的段
       if (st == state_SPEEACH){
                printf("%3.2f %3.2f SPeech\n", cst/(float)sample_rate, nSample/(float)sample_rate);
       }else{
                printf("%3.2f %3.2f Silence\n", cst/(float)sample_rate, nSample/(float)sample_rate);
        }
        return 0;
}
上面的函数会对所有的数据进行循环调用,每调用一帧都会调用detect_frame进行处理:

/**
*
* 处理一帧数据,就是从data开始的frame_size个采样。state表示当前状态
* 返回当前帧的处理后的状态
*
**/
int detect_frame(short *data,int *index,int state){
	short *pcm_data = data ;
	int silence_flag = process_vad(pcm_data,index); //处理具体的数据,并返回当前帧的处理结果

	//结束
	if(silence_flag == state_OVER || silence_flag == state_WAIT )
		return silence_flag ;
	if(silence_flag == state_SILENCE){ // 静音部分段
		number_of_silence  ++ ;
		total_of_silence  ++ ;
		number_of_speech = 0;
		if(number_of_silence > 5){ // 五个以上的静音帧,就相当于50ms
			speech_count = 0; // 开始静音统计,语音为0;
		}

		// 如果之前已经是静音,没有问题,这里处理如果之前是语音的情况。
		if(state == state_SPEECH )
			silence_count ++ ;
		/**
		* 这里的处理是为了使得如果静音段过长,就是超过了300ms。此时状态切换
		**/
		if((state == state_SPEECH) && (silence_count > silence_filter_len )){
			state = ((total_of_speech > total_of_silence) ? state_SPEECH : state_SILENCE) ;
			number_of_silence = number_of_speech =total_of_silence = total_of_speech = 0;
		}
	}else{
		total_of_speech ++ ;
		number_of_speech ++ ;
		number_of_silence = 0;
		if(number_of_speech > 5){
			silence_count = 0;
		}
		if(state == state_SILENCE )
			speech_count ++;
		if((state == state_SILENCE ) && ( speech_count > speech_filter_len)){
			state = ((total_of_speech > total_of_silence) ? state_SPEECH : state_SILENCE) ;
			number_of_silence = number_of_speech =total_of_silence = total_of_speech = 0;
		}
	}
	
	frame_count ++; 
	return state;
}
上面的detect_frame其实是一个后处理函数,就是返回当前帧的可能状态之后,进行状态变更和计数的,其数据处理的操作为:

int process_vad(short *data,int *index){
	if(data == NULL)
		return state_OVER;
	//数据去除直流。
	//数据加窗 并返回total_rms 
	energy_detect(total_rms,flag); // 根据均方差计算当前帧的状态,并从flag返回,该参数为引用参数。
	*index = *index++ ;// 这里是简化,有可能多帧处理。
	return flag == 0 ? state_SPEECH : state_SILENCE ;
}
现在就剩下最关键的实现了,就是energy_detect,基于能量的端点检测,这里采用track energy的方法。

你可能感兴趣的:(VAD实现 (三) --- 算法计算流程与框架)