语音信号处理之预处理简述(二)

目录

1. 语音信号的短时平稳性

2. 语音分帧及加窗操作

2.1 相关概念

2.2 分帧加窗的过程

2.3 还原数据帧

3. 示例

3.1 例1:C语言实现汉明窗的计算

3.2 例2:语音的分帧加窗和还原

3.3 对每一帧做FFT及反变换

3.4 在频域上以模和相角的方式还原信号

3.5 python实现汉明窗的计算


计算机获取到音频数据,进行处理之前,首先要进行分帧和加窗操作。

1. 语音信号的短时平稳性

语音信号为非平稳信号,其统计属性是随着时间变化的,以汉语为例,一句话中包含很多生母和韵母,不同的拼音,发音的特点很明显是不一样的;但是,语音又具有短时平稳的特性,比如汉语里的一个声母或者韵母,往往只会持续几十到几百毫秒,这一个发音单元里,语音信号表现出明显的稳定性、规律性。

2. 语音分帧及加窗操作

2.1 相关概念

(1) 帧长:指一帧语音信号的长度,如果用时间来进行度量的话,一帧信号通常取在15ms-30ms之间,经验值一般取为25ms。帧长为25ms的一帧信号指的是时长有25毫秒的语音信号。如果使用采样点数来度量,则需要计算一下,如:一个信号的采样率为16kHz,则一帧信号由 16kHz * 25ms(0.025s) = 400个采样点组成。

(2) 帧移:一般取帧长的一半。

(3) 加窗:分帧后每一帧的开始和结束都会出现间断,因此分割的帧越多,与原始信号的误差就越大,加窗就是为了解决这个问题,使成帧后的信号变得连续,并且每一帧都会表现出周期函数的特性。常见的窗函数有:矩形窗、汉明窗、汉宁窗等,在语音信号处理中,通常使用汉明窗,其公式如下:

 N是窗长,一般取值等于帧长。

2.2 分帧加窗的过程

根据信号长度、帧移、帧长计算出该信号一共可以分的帧数,帧数的计算公式如下:

帧数 = (信号长度-帧长)/帧移 +1

加窗:将分帧的每一帧信号一次与窗函数进行相乘

在分帧操作时,会遇到最后剩下的信号长度不够一帧的情况,此时需要将对这一段信号进行补零操作,使之达到一帧的长度,或者可以直接将之抛弃,因为最后一帧处于句子最末尾部分,大部分为静音片段。

2.3 还原数据帧

取折叠部分相加。

3. 示例

3.1 例1:C语言实现汉明窗的计算

main.c

#include <stdlib.h>
#include <stdio.h>
#define PI 3.14159267

void hamming(int win_len)
{
       double x;
       int n, N;
       N=win_len-1;
       for (n = 0; n <= N; n++) {
              x = 2 * PI * n/N;
              printf("dat[%d]=%f\n", n, (0.54 - 0.46 * cos(x)));
       }
}


int main(void)
{

       hamming(320);
       return 0;
}

运行结果:

dat[0]=0.080000
dat[1]=0.080089
dat[2]=0.080357
dat[3]=0.080803
dat[4]=0.081427
dat[5]=0.082229
dat[6]=0.083209
dat[7]=0.084365
dat[8]=0.085699
dat[9]=0.087209
dat[10]=0.088894
dat[11]=0.090755
dat[12]=0.092789
dat[13]=0.094997
dat[14]=0.097378
dat[15]=0.099931
dat[16]=0.102654
dat[17]=0.105547
dat[18]=0.108609
dat[19]=0.111837
dat[20]=0.115232
dat[21]=0.118792
dat[22]=0.122515
dat[23]=0.126400
dat[24]=0.130446
dat[25]=0.134650
dat[26]=0.139012
dat[27]=0.143529
dat[28]=0.148200
dat[29]=0.153023
dat[30]=0.157997
dat[31]=0.163118
dat[32]=0.168385
dat[33]=0.173797
dat[34]=0.179351
dat[35]=0.185045
dat[36]=0.190876
dat[37]=0.196843
dat[38]=0.202943
dat[39]=0.209174
dat[40]=0.215533
dat[41]=0.222018
dat[42]=0.228626
dat[43]=0.235355
dat[44]=0.242202
dat[45]=0.249165
dat[46]=0.256241
dat[47]=0.263426
dat[48]=0.270719
dat[49]=0.278117
dat[50]=0.285616
dat[51]=0.293214
dat[52]=0.300907
dat[53]=0.308693
dat[54]=0.316569
dat[55]=0.324532
dat[56]=0.332578
dat[57]=0.340705
dat[58]=0.348909
dat[59]=0.357187
dat[60]=0.365536
dat[61]=0.373953
dat[62]=0.382434
dat[63]=0.390977
dat[64]=0.399577
dat[65]=0.408231
dat[66]=0.416937
dat[67]=0.425690
dat[68]=0.434488
dat[69]=0.443327
dat[70]=0.452203
dat[71]=0.461113
dat[72]=0.470054
dat[73]=0.479022
dat[74]=0.488014
dat[75]=0.497026
dat[76]=0.506054
dat[77]=0.515096
dat[78]=0.524147
dat[79]=0.533205
dat[80]=0.542265
dat[81]=0.551324
dat[82]=0.560379
dat[83]=0.569426
dat[84]=0.578462
dat[85]=0.587482
dat[86]=0.596485
dat[87]=0.605465
dat[88]=0.614420
dat[89]=0.623346
dat[90]=0.632239
dat[91]=0.641097
dat[92]=0.649916
dat[93]=0.658692
dat[94]=0.667422
dat[95]=0.676103
dat[96]=0.684730
dat[97]=0.693302
dat[98]=0.701814
dat[99]=0.710264
dat[100]=0.718647
dat[101]=0.726961
dat[102]=0.735202
dat[103]=0.743368
dat[104]=0.751455
dat[105]=0.759460
dat[106]=0.767380
dat[107]=0.775211
dat[108]=0.782951
dat[109]=0.790597
dat[110]=0.798146
dat[111]=0.805595
dat[112]=0.812940
dat[113]=0.820180
dat[114]=0.827311
dat[115]=0.834331
dat[116]=0.841236
dat[117]=0.848024
dat[118]=0.854693
dat[119]=0.861240
dat[120]=0.867663
dat[121]=0.873958
dat[122]=0.880124
dat[123]=0.886157
dat[124]=0.892057
dat[125]=0.897820
dat[126]=0.903444
dat[127]=0.908927
dat[128]=0.914266
dat[129]=0.919461
dat[130]=0.924509
dat[131]=0.929407
dat[132]=0.934154
dat[133]=0.938749
dat[134]=0.943188
dat[135]=0.947472
dat[136]=0.951597
dat[137]=0.955562
dat[138]=0.959367
dat[139]=0.963008
dat[140]=0.966486
dat[141]=0.969798
dat[142]=0.972943
dat[143]=0.975920
dat[144]=0.978729
dat[145]=0.981367
dat[146]=0.983834
dat[147]=0.986128
dat[148]=0.988250
dat[149]=0.990198
dat[150]=0.991971
dat[151]=0.993568
dat[152]=0.994990
dat[153]=0.996235
dat[154]=0.997303
dat[155]=0.998194
dat[156]=0.998907
dat[157]=0.999442
dat[158]=0.999799
dat[159]=0.999978
dat[160]=0.999978
dat[161]=0.999799
dat[162]=0.999442
dat[163]=0.998907
dat[164]=0.998194
dat[165]=0.997303
dat[166]=0.996235
dat[167]=0.994990
dat[168]=0.993568
dat[169]=0.991971
dat[170]=0.990198
dat[171]=0.988250
dat[172]=0.986128
dat[173]=0.983834
dat[174]=0.981367
dat[175]=0.978729
dat[176]=0.975920
dat[177]=0.972943
dat[178]=0.969798
dat[179]=0.966486
dat[180]=0.963008
dat[181]=0.959367
dat[182]=0.955562
dat[183]=0.951597
dat[184]=0.947472
dat[185]=0.943188
dat[186]=0.938749
dat[187]=0.934154
dat[188]=0.929407
dat[189]=0.924509
dat[190]=0.919461
dat[191]=0.914266
dat[192]=0.908927
dat[193]=0.903444
dat[194]=0.897820
dat[195]=0.892057
dat[196]=0.886157
dat[197]=0.880124
dat[198]=0.873958
dat[199]=0.867663
dat[200]=0.861240
dat[201]=0.854693
dat[202]=0.848024
dat[203]=0.841236
dat[204]=0.834331
dat[205]=0.827311
dat[206]=0.820180
dat[207]=0.812940
dat[208]=0.805595
dat[209]=0.798146
dat[210]=0.790597
dat[211]=0.782951
dat[212]=0.775211
dat[213]=0.767380
dat[214]=0.759460
dat[215]=0.751455
dat[216]=0.743368
dat[217]=0.735202
dat[218]=0.726961
dat[219]=0.718647
dat[220]=0.710264
dat[221]=0.701814
dat[222]=0.693302
dat[223]=0.684730
dat[224]=0.676103
dat[225]=0.667422
dat[226]=0.658692
dat[227]=0.649916
dat[228]=0.641097
dat[229]=0.632239
dat[230]=0.623346
dat[231]=0.614420
dat[232]=0.605465
dat[233]=0.596485
dat[234]=0.587482
dat[235]=0.578462
dat[236]=0.569426
dat[237]=0.560379
dat[238]=0.551324
dat[239]=0.542265
dat[240]=0.533205
dat[241]=0.524147
dat[242]=0.515096
dat[243]=0.506054
dat[244]=0.497026
dat[245]=0.488014
dat[246]=0.479022
dat[247]=0.470054
dat[248]=0.461113
dat[249]=0.452203
dat[250]=0.443327
dat[251]=0.434488
dat[252]=0.425690
dat[253]=0.416937
dat[254]=0.408231
dat[255]=0.399577
dat[256]=0.390977
dat[257]=0.382434
dat[258]=0.373953
dat[259]=0.365536
dat[260]=0.357187
dat[261]=0.348909
dat[262]=0.340705
dat[263]=0.332578
dat[264]=0.324532
dat[265]=0.316569
dat[266]=0.308693
dat[267]=0.300907
dat[268]=0.293214
dat[269]=0.285616
dat[270]=0.278117
dat[271]=0.270719
dat[272]=0.263426
dat[273]=0.256241
dat[274]=0.249165
dat[275]=0.242202
dat[276]=0.235355
dat[277]=0.228626
dat[278]=0.222018
dat[279]=0.215533
dat[280]=0.209174
dat[281]=0.202943
dat[282]=0.196843
dat[283]=0.190876
dat[284]=0.185045
dat[285]=0.179351
dat[286]=0.173797
dat[287]=0.168385
dat[288]=0.163118
dat[289]=0.157997
dat[290]=0.153023
dat[291]=0.148200
dat[292]=0.143529
dat[293]=0.139012
dat[294]=0.134650
dat[295]=0.130446
dat[296]=0.126400
dat[297]=0.122515
dat[298]=0.118792
dat[299]=0.115232
dat[300]=0.111837
dat[301]=0.108609
dat[302]=0.105547
dat[303]=0.102654
dat[304]=0.099931
dat[305]=0.097378
dat[306]=0.094997
dat[307]=0.092789
dat[308]=0.090755
dat[309]=0.088894
dat[310]=0.087209
dat[311]=0.085699
dat[312]=0.084365
dat[313]=0.083209
dat[314]=0.082229
dat[315]=0.081427
dat[316]=0.080803
dat[317]=0.080357
dat[318]=0.080089
dat[319]=0.080000

在实际使用的过程中,一般都是先将窗函数计算好,语音分帧后,直接相乘即可。如下:

#define FRAMELEN 320
#define STEP 160

const double Hamming_Win[FRAMELEN]={
0.080000,0.080089,0.080357,0.080803,0.081427,
0.082229,0.083209,0.084365,0.085699,0.087209,
0.088894,0.090755,0.092789,0.094997,0.097378,
0.099931,0.102654,0.105547,0.108609,0.111837,
0.115232,0.118792,0.122515,0.126400,0.130446,
0.134650,0.139012,0.143529,0.148200,0.153023,
0.157997,0.163118,0.168385,0.173797,0.179351,
0.185045,0.190876,0.196843,0.202943,0.209174,
0.215533,0.222018,0.228626,0.235355,0.242202,
0.249165,0.256241,0.263426,0.270719,0.278117,
0.285616,0.293214,0.300907,0.308693,0.316569,
0.324532,0.332578,0.340705,0.348909,0.357187,
0.365536,0.373953,0.382434,0.390977,0.399577,
0.408231,0.416937,0.425690,0.434488,0.443327,
0.452203,0.461113,0.470054,0.479022,0.488014,
0.497026,0.506054,0.515096,0.524147,0.533205,
0.542265,0.551324,0.560379,0.569426,0.578462,
0.587482,0.596485,0.605465,0.614420,0.623346,
0.632239,0.641097,0.649916,0.658692,0.667422,
0.676103,0.684730,0.693302,0.701814,0.710264,
0.718647,0.726961,0.735202,0.743368,0.751455,
0.759460,0.767380,0.775211,0.782951,0.790597,
0.798146,0.805595,0.812940,0.820180,0.827311,
0.834331,0.841236,0.848024,0.854693,0.861240,
0.867663,0.873958,0.880124,0.886157,0.892057,
0.897820,0.903444,0.908927,0.914266,0.919461,
0.924509,0.929407,0.934154,0.938749,0.943188,
0.947472,0.951597,0.955562,0.959367,0.963008,
0.966486,0.969798,0.972943,0.975920,0.978729,
0.981367,0.983834,0.986128,0.988250,0.990198,
0.991971,0.993568,0.994990,0.996235,0.997303,
0.998194,0.998907,0.999442,0.999799,0.999978,
0.999978,0.999799,0.999442,0.998907,0.998194,
0.997303,0.996235,0.994990,0.993568,0.991971,
0.990198,0.988250,0.986128,0.983834,0.981367,
0.978729,0.975920,0.972943,0.969798,0.966486,
0.963008,0.959367,0.955562,0.951597,0.947472,
0.943188,0.938749,0.934154,0.929407,0.924509,
0.919461,0.914266,0.908927,0.903444,0.897820,
0.892057,0.886157,0.880124,0.873958,0.867663,
0.861240,0.854693,0.848024,0.841236,0.834331,
0.827311,0.820180,0.812940,0.805595,0.798146,
0.790597,0.782951,0.775211,0.767380,0.759460,
0.751455,0.743368,0.735202,0.726961,0.718647,
0.710264,0.701814,0.693302,0.684730,0.676103,
0.667422,0.658692,0.649916,0.641097,0.632239,
0.623346,0.614420,0.605465,0.596485,0.587482,
0.578462,0.569426,0.560379,0.551324,0.542265,
0.533205,0.524147,0.515096,0.506054,0.497026,
0.488014,0.479022,0.470054,0.461113,0.452203,
0.443327,0.434488,0.425690,0.416937,0.408231,
0.399577,0.390977,0.382434,0.373953,0.365536,
0.357187,0.348909,0.340705,0.332578,0.324532,
0.316569,0.308694,0.300907,0.293214,0.285616,
0.278117,0.270719,0.263426,0.256241,0.249165,
0.242202,0.235355,0.228626,0.222018,0.215533,
0.209174,0.202943,0.196843,0.190876,0.185045,
0.179351,0.173797,0.168385,0.163118,0.157997,
0.153023,0.148200,0.143529,0.139012,0.134650,
0.130446,0.126400,0.122515,0.118792,0.115232,
0.111838,0.108609,0.105547,0.102654,0.099931,
0.097378,0.094998,0.092789,0.090755,0.088894,
0.087209,0.085699,0.084365,0.083209,0.082229,
0.081427,0.080803,0.080357,0.080089,0.080000
};

int main()
{
…………………………………..
for (frame_index=0; frame_index<n_frame; frame_index++) {
		for (i=0;i<FRAMELEN;i++) {
frame_array[frame_index][i]= frame_array[frame_index][i]*Hamming_Win[i];
		}
}
………………………

3.2 例2:语音的分帧加窗和还原

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include "baselib.h"
#include "typedefs.h"

#define FRAME_LEN 256
#define STEP_LEN 128

const float hamming_win[FRAME_LEN]={
	0.080000,
	0.080140,0.080558,0.081256,0.082232,0.083487,
	0.085018,0.086825,0.088908,0.091264,0.093893,
	0.096793,0.099962,0.103398,0.107099,0.111063,
	0.115287,0.119769,0.124506,0.129496,0.134734,
	0.140219,0.145946,0.151913,0.158115,0.164549,
	0.171211,0.178097,0.185203,0.192524,0.200056,
	0.207794,0.215734,0.223871,0.232200,0.240716,
	0.249413,0.258287,0.267332,0.276542,0.285912,
	0.295437,0.305110,0.314925,0.324878,0.334960,
	0.345168,0.355493,0.365931,0.376474,0.387117,
	0.397852,0.408674,0.419575,0.430550,0.441591,
	0.452692,0.463845,0.475045,0.486285,0.497557,
	0.508854,0.520171,0.531500,0.542834,0.554166,
	0.565489,0.576797,0.588083,0.599340,0.610560,
	0.621738,0.632866,0.643938,0.654946,0.665885,
	0.676747,0.687527,0.698217,0.708810,0.719302,
	0.729684,0.739951,0.750097,0.760115,0.770000,
	0.779745,0.789345,0.798793,0.808084,0.817212,
	0.826172,0.834958,0.843565,0.851988,0.860222,
	0.868261,0.876100,0.883736,0.891163,0.898377,
	0.905373,0.912148,0.918696,0.925015,0.931100,
	0.936947,0.942554,0.947916,0.953030,0.957894,
	0.962504,0.966858,0.970952,0.974785,0.978353,
	0.981656,0.984690,0.987455,0.989948,0.992168,
	0.994113,0.995782,0.997175,0.998290,0.999128,
	0.999686,0.999965,0.999965,0.999686,0.999128,
	0.998290,0.997175,0.995782,0.994113,0.992168,
	0.989948,0.987455,0.984690,0.981656,0.978353,
	0.974785,0.970952,0.966857,0.962504,0.957894,
	0.953030,0.947916,0.942554,0.936947,0.931100,
	0.925015,0.918696,0.912148,0.905373,0.898377,
	0.891163,0.883736,0.876100,0.868261,0.860222,
	0.851988,0.843565,0.834958,0.826172,0.817212,
	0.808084,0.798793,0.789345,0.779745,0.770000,
	0.760115,0.750097,0.739951,0.729684,0.719301,
	0.708810,0.698216,0.687527,0.676747,0.665885,
	0.654946,0.643938,0.632866,0.621738,0.610560,
	0.599340,0.588083,0.576797,0.565489,0.554166,
	0.542834,0.531500,0.520171,0.508854,0.497557,
	0.486285,0.475045,0.463845,0.452691,0.441591,
	0.430550,0.419575,0.408674,0.397852,0.387117,
	0.376474,0.365931,0.355493,0.345168,0.334960,
	0.324877,0.314925,0.305110,0.295437,0.285912,
	0.276542,0.267331,0.258287,0.249413,0.240716,
	0.232200,0.223871,0.215734,0.207794,0.200056,
	0.192524,0.185203,0.178097,0.171211,0.164549,
	0.158115,0.151913,0.145946,0.140219,0.134734,
	0.129496,0.124506,0.119769,0.115287,0.111063,
	0.107099,0.103398,0.099962,0.096793,0.093893,
	0.091264,0.088908,0.086825,0.085018,0.083487,
	0.082232,0.081256,0.080558,0.080140,0.080000,
};

int main(int argc, char** argv)
{
	int frame_index;
	int i, j, k, h, count;
	FILE *fpin = NULL;
	FILE *fpout = NULL;

	fpin = fopen("input.pcm", "rb");
	fseek(fpin, 0, SEEK_END);
	long inputdata_length = ftell(fpin);
	inputdata_length /= 2;
	printf("input_file_len:% ld\n", inputdata_length);
	rewind(fpin);

	//总帧数
	int n_frame = (inputdata_length - FRAME_LEN)/STEP_LEN + 1;
	printf("n_frame:%d\n", n_frame);

	short *wavin = (short *)malloc(inputdata_length * sizeof(short));
	short *wavout = (short *)malloc(inputdata_length * sizeof(short));
	//动态申请n_frame行,FRAME_LEN列的空间
	COMPLEX (*frame_data)[FRAME_LEN] = (COMPLEX(*)[FRAME_LEN])malloc((n_frame)*FRAME_LEN * sizeof(COMPLEX));
	short (*voice_timedomain)[FRAME_LEN] = (short(*)[FRAME_LEN])malloc((n_frame)*FRAME_LEN * sizeof(short));

	//读取音频数据流
	count = fread(wavin, sizeof(short), inputdata_length, fpin);

	/* 1. 分帧(取出一帧一帧数据, 能取n_frame帧) */
	//frame_data:n_frame行,FRAME_LEN列
	//行下标就是帧索引,列下标就是帧元素索引
	for (frame_index=0; frame_index<n_frame; frame_index++) {
		int n1, n2;
		//抽取因子step_len决定每次抽取数据的起始位置
		n1=frame_index*STEP_LEN;
		//帧长frame_len决定每次抽取多少个元素
		n2=frame_index*STEP_LEN + FRAME_LEN;
		for (j=n1, k=0; j<n2; j++) {
			frame_data[frame_index][j-n1].real = (float)wavin[j]*hamming_win[k++];
			frame_data[frame_index][j-n1].image = 0.0;
		}

	}

	/* 2.对数据帧进行操作(比如:FFT以及算法处理) */

	//这里直接还原数据
	for (frame_index=0; frame_index<n_frame; frame_index++) {
		for (i = 0; i < FRAME_LEN; i++) {
			voice_timedomain[frame_index][i]=frame_data[frame_index][i].real;
		}
	}

	/* 3. 还原 */
	//在时域上从二维数组中拿出数据到一维数组(从分帧中还原原始数据)
	for (i=0; i < inputdata_length; i++) {
		wavout[i]=0.0;
	}

	h=0;
	for (frame_index=0; frame_index<n_frame-1; frame_index++) {
		for (i = 0; i < STEP_LEN; i++) {
			//取帧和帧之间重叠部分进行相加
			 wavout[h++]= voice_timedomain[frame_index][i+STEP_LEN]+voice_timedomain[frame_index+1][i];
		}
	}

	fpout = fopen("output.pcm", "wb");
	fwrite(wavout, sizeof(short), inputdata_length, fpout);
	printf("process finished.\r\n");

	fclose(fpout);
	fclose(fpin);
	free(wavin);
	free(wavout);
	free(frame_data);
	free(voice_timedomain);

	return 0;
}

输入输出文件对比如下:

3.3 对每一帧做FFT及反变换

main.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include "baselib.h"
#include "typedefs.h"
#if 1
#define FRAME_LEN 256
#define STEP_LEN 128
#define FFT_ORDER ((int)log2(FRAME_LEN))

const float hamming_win[FRAME_LEN]={
	0.080000,
	0.080140,0.080558,0.081256,0.082232,0.083487,
	0.085018,0.086825,0.088908,0.091264,0.093893,
	0.096793,0.099962,0.103398,0.107099,0.111063,
	0.115287,0.119769,0.124506,0.129496,0.134734,
	0.140219,0.145946,0.151913,0.158115,0.164549,
	0.171211,0.178097,0.185203,0.192524,0.200056,
	0.207794,0.215734,0.223871,0.232200,0.240716,
	0.249413,0.258287,0.267332,0.276542,0.285912,
	0.295437,0.305110,0.314925,0.324878,0.334960,
	0.345168,0.355493,0.365931,0.376474,0.387117,
	0.397852,0.408674,0.419575,0.430550,0.441591,
	0.452692,0.463845,0.475045,0.486285,0.497557,
	0.508854,0.520171,0.531500,0.542834,0.554166,
	0.565489,0.576797,0.588083,0.599340,0.610560,
	0.621738,0.632866,0.643938,0.654946,0.665885,
	0.676747,0.687527,0.698217,0.708810,0.719302,
	0.729684,0.739951,0.750097,0.760115,0.770000,
	0.779745,0.789345,0.798793,0.808084,0.817212,
	0.826172,0.834958,0.843565,0.851988,0.860222,
	0.868261,0.876100,0.883736,0.891163,0.898377,
	0.905373,0.912148,0.918696,0.925015,0.931100,
	0.936947,0.942554,0.947916,0.953030,0.957894,
	0.962504,0.966858,0.970952,0.974785,0.978353,
	0.981656,0.984690,0.987455,0.989948,0.992168,
	0.994113,0.995782,0.997175,0.998290,0.999128,
	0.999686,0.999965,0.999965,0.999686,0.999128,
	0.998290,0.997175,0.995782,0.994113,0.992168,
	0.989948,0.987455,0.984690,0.981656,0.978353,
	0.974785,0.970952,0.966857,0.962504,0.957894,
	0.953030,0.947916,0.942554,0.936947,0.931100,
	0.925015,0.918696,0.912148,0.905373,0.898377,
	0.891163,0.883736,0.876100,0.868261,0.860222,
	0.851988,0.843565,0.834958,0.826172,0.817212,
	0.808084,0.798793,0.789345,0.779745,0.770000,
	0.760115,0.750097,0.739951,0.729684,0.719301,
	0.708810,0.698216,0.687527,0.676747,0.665885,
	0.654946,0.643938,0.632866,0.621738,0.610560,
	0.599340,0.588083,0.576797,0.565489,0.554166,
	0.542834,0.531500,0.520171,0.508854,0.497557,
	0.486285,0.475045,0.463845,0.452691,0.441591,
	0.430550,0.419575,0.408674,0.397852,0.387117,
	0.376474,0.365931,0.355493,0.345168,0.334960,
	0.324877,0.314925,0.305110,0.295437,0.285912,
	0.276542,0.267331,0.258287,0.249413,0.240716,
	0.232200,0.223871,0.215734,0.207794,0.200056,
	0.192524,0.185203,0.178097,0.171211,0.164549,
	0.158115,0.151913,0.145946,0.140219,0.134734,
	0.129496,0.124506,0.119769,0.115287,0.111063,
	0.107099,0.103398,0.099962,0.096793,0.093893,
	0.091264,0.088908,0.086825,0.085018,0.083487,
	0.082232,0.081256,0.080558,0.080140,0.080000,
};

int main(int argc, char** argv)
{
	int frame_index;
	int i, j, k, h, count;
	FILE *fpin = NULL;
	FILE *fpout = NULL;

	fpin = fopen("input.pcm", "rb");
	fseek(fpin, 0, SEEK_END);
	long inputdata_length = ftell(fpin);
	inputdata_length /= 2;
	printf("input_file_len:% ld\n", inputdata_length);
	rewind(fpin);

	//总帧数
	int n_frame = (inputdata_length - FRAME_LEN)/STEP_LEN + 1;
	printf("n_frame:%d\n", n_frame);

	short *wavin = (short *)malloc(inputdata_length * sizeof(short));
	short *wavout = (short *)malloc(inputdata_length * sizeof(short));
	//动态申请n_frame行,FRAME_LEN列的空间
	COMPLEX (*frame_data)[FRAME_LEN] = (COMPLEX(*)[FRAME_LEN])malloc((n_frame)*FRAME_LEN * sizeof(COMPLEX));

	float *frame_data_tmp_R = (float *)malloc(FRAME_LEN * sizeof(float));
	float *frame_data_tmp_I = (float *)malloc(FRAME_LEN * sizeof(float));
	short (*voice_timedomain)[FRAME_LEN] = (short(*)[FRAME_LEN])malloc((n_frame)*FRAME_LEN * sizeof(short));

	//读取音频数据流
	count = fread(wavin, sizeof(short), inputdata_length, fpin);

	/* 1. 分帧(取出一帧一帧数据, 能取n_frame帧) */
	//frame_data:n_frame行,FRAME_LEN列
	//行下标就是帧索引,列下标就是帧元素索引
	for (frame_index=0; frame_index<n_frame; frame_index++) {
		int n1, n2;
		//抽取因子step_len决定每次抽取数据的起始位置
		n1=frame_index*STEP_LEN;
		//帧长frame_len决定每次抽取多少个元素
		n2=frame_index*STEP_LEN + FRAME_LEN;
		for (j=n1, k=0; j<n2; j++) {
			frame_data[frame_index][j-n1].real = (float)wavin[j]*hamming_win[k++];
			frame_data[frame_index][j-n1].image = 0.0;
		}

	}

	/* 2.对数据帧进行操作(比如:FFT以及算法处理) */
	for (frame_index=0; frame_index<n_frame; frame_index++) {
		//逐帧取出数据
		for(i=0; i<FRAME_LEN; i++){
			frame_data_tmp_R[i]=frame_data[frame_index][i].real;
			frame_data_tmp_I[i]=0.f;
		}

#if 0
		FILE* fp_dbg_01=NULL;
		fp_dbg_01 = fopen("output01.txt", "wb");
		for (i=0; i<frame_len; i++) {
			fprintf(fp_dbg_01, "[%d]:%f %f\n", i, frame_data_tmp_R[i], frame_data_tmp_I[i]);
		}

		fclose(fp_dbg_01);
		printf("data output done\n");
		return 0;
#endif
		//对当前帧做FFT(时域变换到频域)
		FFT(frame_data_tmp_R, frame_data_tmp_I, NULL, FRAME_LEN, FFT_ORDER);

		//对当前帧做IFFT(频域变换回时域)
		IFFT(frame_data_tmp_R, frame_data_tmp_I, FRAME_LEN, FFT_ORDER);

		//保存这帧语音信号的傅立叶反变换的结果
		for (i = 0; i < FRAME_LEN; i++)
		{
			voice_timedomain[frame_index][i] = frame_data_tmp_R[i];
		}
	}

	/* 3. 还原 */
	//在时域上从二维数组中拿出数据到一维数组(从分帧中还原原始数据)
	for (i=0; i < inputdata_length; i++) {
		wavout[i]=0.0;
	}

	h=0;
	for (frame_index=0; frame_index<n_frame-1; frame_index++) {
		for (i = 0; i < STEP_LEN; i++) {
			//取帧和帧之间重叠部分进行相加
			 wavout[h++]= voice_timedomain[frame_index][i+STEP_LEN]+voice_timedomain[frame_index+1][i];
		}
	}

	fpout = fopen("output.pcm", "wb");
	fwrite(wavout, sizeof(short), inputdata_length, fpout);
	printf("process finished.\r\n");

	fclose(fpout);
	fclose(fpin);
	free(wavin);
	free(wavout);
	free(frame_data);
	free(frame_data_tmp_R);
	free(frame_data_tmp_I);
	free(voice_timedomain);

	return 0;
}
#else
int main(void)
{
	float dat_r[8], dat_i[8], dat_a[8];
	for (int i=0; i<8; i++) {
		dat_r[i]=i+0.0;
		dat_i[i]=0.0;
	}

	FFT(dat_r, dat_i, dat_a, 8, 3);
	for (int i=0; i<8; i++) {
		printf("dat_r[%d]=%f\n", i, dat_r[i]);
	}
	printf("\n");
	for (int i=0; i<8; i++) {
		printf("dat_i[%d]=%f\n", i, dat_i[i]);
	}
	printf("\n");
	for (int i=0; i<8; i++) {
		printf("dat_a[%d]=%f\n", i, dat_a[i]);
	}
	printf("****************************************\n\n");

	IFFT(dat_r, dat_i, 8, 3);
	for (int i=0; i<8; i++) {
		printf("dat_r[%d]=%f\n", i, dat_r[i]);
	}
	printf("\n");
	for (int i=0; i<8; i++) {
		printf("dat_i[%d]=%f\n", i, dat_i[i]);
	}

	return 0;
}
#endif

输出结果是不变的(仅仅是做了变换,没有任何计算),如下图:

3.4 在频域上以模和相角的方式还原信号

简单测试程序:

double angle(float dat_r, float dat_i)
{
	double m;
	m = atan2(dat_i, dat_r);
	return m;
}

int main(void)
{
	float rel=8.0, img=7.0; //z=rel+i*img

	//z=a+ib
	//atan2(b,a)=arctan(b/a)
	//printf("rel:%f\n", sqrt(rel*rel + img*img)*cos(atan2(img, rel)));
	//printf("img:%f\n", sqrt(rel*rel + img*img)*sin(atan2(img, rel)));

	printf("rel:%f\n", sqrt(rel*rel + img*img)*cos(angle(rel, img)));
	printf("img:%f\n", sqrt(rel*rel + img*img)*sin(angle(rel, img)));
	return 0;
}

 测试结果

rel:8.000000

img:7.000000

方式1:直接进行FFT及其反变换

#define N 8
#define M 3
int main(void)
{
	int i, j;
	short timedomain[N]={24948, 17818, 0, 1, -5, -7, 9, 14};
	float data_tmp_R[N];
	float data_tmp_I[N];
	float data_tmp_A[N];

	for (i=0; i<N; i++){
		data_tmp_R[i]=timedomain[i];
		data_tmp_I[i]=0.0;
	}

	FFT(data_tmp_R, data_tmp_I, NULL, N, M);

	IFFT(data_tmp_R, data_tmp_I, N, M);

	for (i=0; i<N; i++){
		printf("data_tmp_R[%d]=%f; data_tmp_I[%d]=%f;\n", i, data_tmp_R[i], i, data_tmp_I[i]);
	}

	return 0;
}

结果:
data_tmp_R[0]=24948.000000;data_tmp_I[0]=0.000000;
data_tmp_R[1]=17818.000000;data_tmp_I[1]=-0.000087;
data_tmp_R[2]=0.000000;data_tmp_I[2]=0.000006;
data_tmp_R[3]=1.000176;data_tmp_I[3]=0.000229;
data_tmp_R[4]=-5.000000;data_tmp_I[4]=0.000000;
data_tmp_R[5]=-7.000000;data_tmp_I[5]=-0.000001;
data_tmp_R[6]=9.000000;data_tmp_I[6]=-0.000006;
data_tmp_R[7]=13.999824;data_tmp_I[7]=-0.000142;

方式2:通过模和幅角来等价计算,还原每个频点,再进行逆变换

#define N 8
#define M 3

//计算幅角
float angle(float dat_r, float dat_i)
{
	float m;
	m = atan2(dat_i, dat_r);
	return m;
}

int main(void)
{
	int i, j;
	short timedomain[N]={24948, 17818, 0, 1, -5, -7, 9, 14};
	float data_tmp_R[N];
	float data_tmp_I[N];
	float data_tmp_RR[N];
	float data_tmp_II[N];
	float data_tmp_A[N];
	float data_tmp_Angle[N];

	for (i=0; i<N; i++) {
		data_tmp_R[i]=timedomain[i];
		data_tmp_I[i]=0.0;
	}

	FFT(data_tmp_R, data_tmp_I, data_tmp_A, N, M);
	//计算每个频点的幅角
	for (i=0; i<N; i++) {
		data_tmp_Angle[i]=angle(data_tmp_R[i], data_tmp_I[i]);
	}

	//频域上:用模和幅角来等价计算每个频点的实部和虚部。
	for (i=0; i<N; i++) {
		data_tmp_RR[i]=data_tmp_A[i]*cos(data_tmp_Angle[i]);
		data_tmp_II[i]=data_tmp_A[i]*sin(data_tmp_Angle[i]);
	}

	IFFT(data_tmp_RR, data_tmp_II, N, M);

	for (i=0; i<N; i++){
		printf("data_tmp_RR[%d]=%f; data_tmp_II[%d]=%f;\n", i, data_tmp_RR[i], i, data_tmp_II[i]);
	}

	return 0;
}

结果:
data_tmp_RR[0]=24948.000000; data_tmp_II[0]=0.000122;
data_tmp_RR[1]=17818.000000; data_tmp_II[1]=-0.000087;
data_tmp_RR[2]=-0.000366; data_tmp_II[2]=-0.000482;
data_tmp_RR[3]=1.000867; data_tmp_II[3]=-0.000116;
data_tmp_RR[4]=-5.000000; data_tmp_II[4]=-0.000122;
data_tmp_RR[5]=-6.999023; data_tmp_II[5]=-0.000001;
data_tmp_RR[6]=9.000366; data_tmp_II[6]=0.000482;
data_tmp_RR[7]=13.999133; data_tmp_II[7]=0.000203;

测试实例:音频文件的读取,变换和还原

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include "baselib.h"
#include "typedefs.h"

#define FRAME_LEN 256
#define STEP_LEN 128
#define FFT_ORDER ((int)log2(FRAME_LEN))

float angle(float dat_r, float dat_i);

const float hamming_win[FRAME_LEN]={
	0.080000,
	0.080140,0.080558,0.081256,0.082232,0.083487,
	0.085018,0.086825,0.088908,0.091264,0.093893,
	0.096793,0.099962,0.103398,0.107099,0.111063,
	0.115287,0.119769,0.124506,0.129496,0.134734,
	0.140219,0.145946,0.151913,0.158115,0.164549,
	0.171211,0.178097,0.185203,0.192524,0.200056,
	0.207794,0.215734,0.223871,0.232200,0.240716,
	0.249413,0.258287,0.267332,0.276542,0.285912,
	0.295437,0.305110,0.314925,0.324878,0.334960,
	0.345168,0.355493,0.365931,0.376474,0.387117,
	0.397852,0.408674,0.419575,0.430550,0.441591,
	0.452692,0.463845,0.475045,0.486285,0.497557,
	0.508854,0.520171,0.531500,0.542834,0.554166,
	0.565489,0.576797,0.588083,0.599340,0.610560,
	0.621738,0.632866,0.643938,0.654946,0.665885,
	0.676747,0.687527,0.698217,0.708810,0.719302,
	0.729684,0.739951,0.750097,0.760115,0.770000,
	0.779745,0.789345,0.798793,0.808084,0.817212,
	0.826172,0.834958,0.843565,0.851988,0.860222,
	0.868261,0.876100,0.883736,0.891163,0.898377,
	0.905373,0.912148,0.918696,0.925015,0.931100,
	0.936947,0.942554,0.947916,0.953030,0.957894,
	0.962504,0.966858,0.970952,0.974785,0.978353,
	0.981656,0.984690,0.987455,0.989948,0.992168,
	0.994113,0.995782,0.997175,0.998290,0.999128,
	0.999686,0.999965,0.999965,0.999686,0.999128,
	0.998290,0.997175,0.995782,0.994113,0.992168,
	0.989948,0.987455,0.984690,0.981656,0.978353,
	0.974785,0.970952,0.966857,0.962504,0.957894,
	0.953030,0.947916,0.942554,0.936947,0.931100,
	0.925015,0.918696,0.912148,0.905373,0.898377,
	0.891163,0.883736,0.876100,0.868261,0.860222,
	0.851988,0.843565,0.834958,0.826172,0.817212,
	0.808084,0.798793,0.789345,0.779745,0.770000,
	0.760115,0.750097,0.739951,0.729684,0.719301,
	0.708810,0.698216,0.687527,0.676747,0.665885,
	0.654946,0.643938,0.632866,0.621738,0.610560,
	0.599340,0.588083,0.576797,0.565489,0.554166,
	0.542834,0.531500,0.520171,0.508854,0.497557,
	0.486285,0.475045,0.463845,0.452691,0.441591,
	0.430550,0.419575,0.408674,0.397852,0.387117,
	0.376474,0.365931,0.355493,0.345168,0.334960,
	0.324877,0.314925,0.305110,0.295437,0.285912,
	0.276542,0.267331,0.258287,0.249413,0.240716,
	0.232200,0.223871,0.215734,0.207794,0.200056,
	0.192524,0.185203,0.178097,0.171211,0.164549,
	0.158115,0.151913,0.145946,0.140219,0.134734,
	0.129496,0.124506,0.119769,0.115287,0.111063,
	0.107099,0.103398,0.099962,0.096793,0.093893,
	0.091264,0.088908,0.086825,0.085018,0.083487,
	0.082232,0.081256,0.080558,0.080140,0.080000,
};

int main(int argc, char** argv)
{
	int frame_index;
	int i, j, k, h, count;
	FILE *fpin = NULL;
	FILE *fpout = NULL;

	fpin = fopen("input.pcm", "rb");
	fseek(fpin, 0, SEEK_END);
	long inputdata_length = ftell(fpin);
	inputdata_length /= 2;
	printf("input_file_len:% ld\n", inputdata_length);
	rewind(fpin);

	//总帧数
	int n_frame = (inputdata_length - FRAME_LEN)/STEP_LEN + 1;
	printf("n_frame:%d\n", n_frame);

	short *wavin = (short *)malloc(inputdata_length * sizeof(short));
	short *wavout = (short *)malloc(inputdata_length * sizeof(short));
	//动态申请n_frame行,FRAME_LEN列的空间
	COMPLEX (*frame_data)[FRAME_LEN] = (COMPLEX(*)[FRAME_LEN])malloc((n_frame)*FRAME_LEN * sizeof(COMPLEX));

	float *frame_data_tmp_R = (float *)malloc(FRAME_LEN * sizeof(float));
	float *frame_data_tmp_I = (float *)malloc(FRAME_LEN * sizeof(float));
	float *frame_data_tmp_A = (float *)malloc(FRAME_LEN * sizeof(float));

	//存储幅度谱
	float (*frame_data_a)[FRAME_LEN] = (float(*)[FRAME_LEN])malloc((n_frame)*FRAME_LEN * sizeof(float));
	//存储相位谱
	float (*frame_data_angle)[FRAME_LEN] = (float(*)[FRAME_LEN])malloc((n_frame)*FRAME_LEN * sizeof(float));

	short (*voice_timedomain)[FRAME_LEN] = (short(*)[FRAME_LEN])malloc((n_frame)*FRAME_LEN * sizeof(short));

	//读取音频数据流
	count = fread(wavin, sizeof(short), inputdata_length, fpin);

	/* 1. 分帧(取出一帧一帧数据, 能取n_frame帧) */
	//frame_data:n_frame行,FRAME_LEN列
	//行下标就是帧索引,列下标就是帧元素索引
	for (frame_index=0; frame_index<n_frame; frame_index++) {
		int n1, n2;
		//抽取因子step_len决定每次抽取数据的起始位置
		n1=frame_index*STEP_LEN;
		//帧长frame_len决定每次抽取多少个元素
		n2=frame_index*STEP_LEN + FRAME_LEN;
		for (j=n1, k=0; j<n2; j++) {
			frame_data[frame_index][j-n1].real = (float)wavin[j]*hamming_win[k++];
			frame_data[frame_index][j-n1].image = 0.0;
		}

	}

	/* 2.对数据帧进行操作(比如:FFT以及算法处理) */
	for (frame_index=0; frame_index<n_frame; frame_index++) {
		//逐帧取出数据
		for(i=0; i<FRAME_LEN; i++){
			frame_data_tmp_R[i]=frame_data[frame_index][i].real;
			frame_data_tmp_I[i]=0.f;
		}

		//对当前帧做FFT(时域变换到频域)
		FFT(frame_data_tmp_R, frame_data_tmp_I, frame_data_tmp_A, FRAME_LEN, FFT_ORDER);

		//保存每一帧的模和相角
		for (i=0; i<FRAME_LEN; i++) {
			frame_data_a[frame_index][i]=frame_data_tmp_A[i];
			frame_data_angle[frame_index][i]=angle(frame_data_tmp_R[i], frame_data_tmp_I[i]);
		}
	}

	//组合幅度谱和相位谱进行等价计算,还原频点
	for (frame_index=0; frame_index<n_frame; frame_index++) {
		for (j = 0; j < FRAME_LEN; j++) {
			frame_data_tmp_R[j] = frame_data_a[frame_index][j] * cos(frame_data_angle[frame_index][j]);
			frame_data_tmp_I[j] = frame_data_a[frame_index][j] * sin(frame_data_angle[frame_index][j]);
		}

		//对当前帧做IFFT(频域变换回时域)
		IFFT(frame_data_tmp_R, frame_data_tmp_I, FRAME_LEN, FFT_ORDER);

		//保存这帧语音信号的傅立叶反变换的结果
		for (j = 0; j < FRAME_LEN; j++)
		{
			voice_timedomain[frame_index][j] = frame_data_tmp_R[j];
		}
	}

	/* 3. 还原 */
	//在时域上从二维数组中拿出数据到一维数组(从分帧中还原原始数据)
	for (i=0; i < inputdata_length; i++) {
		wavout[i]=0.0;
	}

	h=0;
	for (frame_index=0; frame_index<n_frame-1; frame_index++) {
		for (i = 0; i < STEP_LEN; i++) {
			//取帧和帧之间重叠部分进行相加
			 wavout[h++]= voice_timedomain[frame_index][i+STEP_LEN]+voice_timedomain[frame_index+1][i];
		}
	}

	fpout = fopen("output.pcm", "wb");
	fwrite(wavout, sizeof(short), inputdata_length, fpout);
	printf("process finished.\r\n");

	fclose(fpout);
	fclose(fpin);
	free(wavin);
	free(wavout);
	free(frame_data);
	free(frame_data_tmp_R);
	free(frame_data_tmp_I);
	free(frame_data_tmp_A);
	free(frame_data_a);
	free(frame_data_angle);
	free(voice_timedomain);

	return 0;
}

float angle(float dat_r, float dat_i)
{
	float m;
	m = atan2(dat_i, dat_r);
	return m;
}

3.5 python实现汉明窗的计算

Hanmming.py

import numpy as np
 
def create_win(name='Hamming', N=128):
    # Rect/Hanning/Hamming
    if name == 'Hamming':
        dat_array = np.array([0.54 - 0.46 * np.cos(2 * np.pi * n / (N - 1)) for n in range(N)])
    elif name == 'Hanning':
        dat_array = np.array([0.5 - 0.5 * np.cos(2 * np.pi * n / (N - 1)) for n in range(N)])
    elif name == 'Rect':
        window = np.ones(N)
    return dat_array
    
win=create_win("Hamming", N=256)
print(win)

上一篇:语音信号处理之预处理简述(一)_卡卡6的博客-CSDN博客

猜你喜欢

转载自blog.csdn.net/qq_40088639/article/details/127231931