谱减法技术大多数都是在频域上进行, 也有在时域的。 由于在谱减法处理过程中,是以无声期间统计平均的噪声方差代替当前分析帧各频率点的噪声频率分量,而噪声频谱具有高斯分布,即其幅度随机变化范围很宽,因此相减时,若该帧某频率点噪声分量较大, 就会有很大一部分保留, 具体来讲, 由谱减所产生的噪声称为残余噪声, 与语音信号不相关, 是由具有随机频率和幅度的窄带信号所组成。在频谱上呈现随机出现的尖峰, 便产生了间歇短暂的突发声调, 在听觉上形成有节奏性起伏的类似音乐噪声的残留噪声。这种具有音乐特性的残余噪声是各帧内在随机频率上出现的许多声调的群体结果。它比原始语音中的噪声清楚的多,也更易令人反感。这种噪声具有“音乐”的听觉效果,听起来像有“咕咕”的流水声,因此称为“音乐噪声” 。听者常常能发现处理后的语音中的“音乐噪声” 比原始信号中的噪声更为清晰,这是由于在短时谱估计中,在各帧的随机频率点上出现多种频率的组合而产生的。“音乐噪声”可以分为下面两种:一是与原来背景噪音有同样听觉特性的噪音;二是在谱减过程中生成的短时谱峰,造成人为的音乐噪音。

一、使用正弦数据序列产生WAV文件

1、使用正弦数据序列生成WAV文件

先定义正弦数据序列为sin((size_buf - i)*(pi*2)/128*sin_hz)*128*sin_db/100+128,时间t=2s,频率为1000hz,db=10,buf=256,采样率为8000hz,单声道。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <sys/types.h>
#include <sys/stat.h>
//#include <unistd.h>
#include <fcntl.h>
#define sin_t       2
#define sin_hz      1000
#define sin_db      10
#define size_buf    sin_t*128
#define pi 3.1415926

unsigned char buf[size_buf*2];

#if 1
typedef struct
{
char chRIFF[4];                 // "RIFF" 标志  
int  total_Len;                 // 文件长度      
char chWAVE[4];                 // "WAVE" 标志  
char chFMT[4];                  // "fmt" 标志 
int  dwFMTLen;                  // 过渡字节(不定)  一般为16
short fmt_pcm;                  // 格式类别  
short  channels;                // 声道数  
int fmt_samplehz;               // 采样率 
int fmt_bytepsec;               // 位速  
short fmt_bytesample;           // 一个采样多声道数据块大小  
short fmt_bitpsample;    // 一个采样占的 bit 数  
char chDATA[4];                 // 数据标记符"data "  
int  dwDATALen;                 // 语音数据的长度,比文件长度小42一般。这个是计算音频播放时长的关键参数~  
}WaveHeader;

WaveHeader WavInf = {
"RIFF",
sin_t*500*2+36,
"WAVE",
"fmt ",
16,
1,
1,
8000,
8000,
1,
8,
"data",
500*sin_t*2
};


void InitWavHeader(void)
{
    unsigned long a = sin_t*128+36;
    data2array(a,&wav_header[4],4);
    a = 128;
    data2array(a,&wav_header[24],4);
    data2array(a,&wav_header[28],4);
    a = 256;
    data2array(a,&wav_header[40],4);
}
#endif


int main()
{
long i;
    int j = 0;
    int ret;
    //InitWavHeader();
for(i=0;i<size_buf;i++)
{
       
	buf[j++] = sin((size_buf - i)*(pi*2)/128*sin_hz)*128*sin_db/100+128;
        buf[j++] = 0;
}
int fd;
fd = open("./test1.wav",O_CREAT|O_RDWR);
if(fd<0)
{
printf("open error\n");
return 0;
}
ret = write(fd,(char *)&WavInf,sizeof(WavInf));
//write(fd,wav_header,44);
ret = write(fd,buf,128*sin_t*2);
    close(fd);


return 0;
}

2、使用matlab观察WAV文件的信号波形和信号频谱。

C语言使用正弦数据序列产生WAV文件并使用谱减法降噪

3、使用matlab观察WAV文件的语谱图

语谱图的x是时间,y轴是频率,z轴是幅度。幅度用亮色如红色表示高,用深色表示低。利用语谱图可以查看指定频率端的能量分布。

C语言使用正弦数据序列产生WAV文件并使用谱减法降噪

二、对WAV文件进行加入自定义随机噪声

1、使用rand()函数产生自定义随机数

//给正弦数据序列加入随机数
	srand(time(NULL));//种种子 
	int a,b;
	FILE *fp = NULL;
	for(a = 0 ; a < size_buf; a ++) //产生50以内的随机数 
	{
		b = rand()%50 ;
	}

2、使用matlab观察加噪后WAV文件的信号波形和信号频谱。

C语言使用正弦数据序列产生WAV文件并使用谱减法降噪

3、使用matlab观察家噪声后WAV文件的语谱图

C语言使用正弦数据序列产生WAV文件并使用谱减法降噪

通过对比发现,加入噪声前的WAV文件和加入噪声后的WAV文件的波形图和语谱图发生明显变化。

三、使用C语言对WAV文件进行谱减法去噪。



/**************************主程序*****************************/
void main(void)
{ 
double wavin[7680],  wavout[7680];
complex S[60][256],voice[256];
complex   noise1[256];
double noise_foward15frame[15][256];
double am_noise[256];
int fs=8000;
double sum[15][256],voice_timedomain[60][256];
double phase[256];
double am_signal[256];
double am_voice[256];
int frame_len=256,step_len=128,n_frame=15,wav_length=7680,i,j,size_x=256,nifrm,ifrm;
int kk=0,n=1;


for(i=0;i<wav_length;i++)
{
wavin[i]=i;
}

switch (fs)
{ 
case 8000:
frame_len=256;step_len=128;break;
case 10000:
frame_len=400;step_len=200;break;
case 12000:
frame_len=480;step_len=240;break;
case 16000:
frame_len=640;step_len=320;break;
case 44100:
frame_len=1800;step_len=900;break;
default:
frame_len=1800;step_len=900;break;
}
n_frame=(wav_length-frame_len)/step_len+1; 

for(i=0;i<n_frame;i++)   //分帧
{ 
int n1,n2; 
n1=i*step_len;
n2=i*step_len+frame_len;
for(j=n1;j<=n2;j++)
{
S[i][j-n1].real=wavin[j];
S[i][j-n1].img=0.0;
}

}

for(i=0;i<winsize;i++)          //初始化W数组
{
W[i].real=sin(2*PI/size_x*i);
W[i].img=-1*sin(2*PI/size_x*i);
}  

for(i=0;i<winsize;i++)          //初始化W数组
{
W1[i].real=sin(2*PI/size_x*i);
W1[i].img=sin(2*PI/size_x*i);
}  


for(nifrm=0;nifrm<15;nifrm++)
{

hamming(S[nifrm]);

for(i=0;i<frame_len;i++)
{ 
noise1[i].real=S[nifrm][i].real;
noise1[i].img=0.0;
}



fft(noise1,256,W); 
for( i=0;i<frame_len;i++)
{
noise_foward15frame[nifrm][i]=abs1(noise1[i]);  //noise_foward15frame保存前15帧的噪音短时傅立叶变换幅度结果
}   
}

for(j=0;j<frame_len;j++)
{	
double w=0.0;
for(i=0;i<15;i++)
{
w=w+noise_foward15frame[i][j];
}
am_noise[j]=w/15;
   
}

for(ifrm=0;ifrm<n_frame;ifrm++)
{		   
hamming(S[ifrm]);

for( i=0;i<frame_len;i++)
{
sum[ifrm][i]=S[ifrm][i].real; //sum_timedomain保存整个语音信号的时域和的结果
}


FILE *fp;
fp= fopen("Sine.dat","w");
for(i=0;i<wav_length;i++)
{
fprintf(fp,"%d  %f;\n", i,sum[ifrm][i]);
}
fclose(fp);


fft(S[ifrm],256,W);   //快速傅里叶变换参数
for(i=0;i<frame_len;i++)
{
phase[i]=angle(S[ifrm][i]);                   ;  //保存这帧语音信号的傅立叶变换的结果的相位
}
for(i=0;i<frame_len;i++)
{
am_signal[i]=abs1(S[ifrm][i]);          //  保存这帧语音信号的傅立叶变换的结果的幅度      
} 
 
for( i=0;i<frame_len;i++)
{
am_voice[i]=am_signal[i]-am_noise[i]; //谱减  %用信号的幅度减去噪声的幅度得到纯净语音的幅度
}
for (i=0;i<frame_len;i++)
{
if(am_voice[i]<0)
am_voice[i]=0.0;
}
for(i=0;i<frame_len;i++)
{
voice[i].real=am_voice[i]; //组合相位与幅度得到去噪后的纯净语音信号
voice[i].img=phase[i]*voice[i].real;
}

FILE *fp2;
fp2= fopen("Sine2.dat","w");
for(i=0;i<wav_length;i++)
{
fprintf(fp2,"%d  %f;\n", i,voice[i].img);
}
fclose(fp2);

ifft(voice,frame_len,W1);
for(i=0;i<frame_len;i++)
{
voice_timedomain[ifrm][i]=voice[i].real;    // 求这帧纯净语音信号的傅立叶反变换的实部
}

}
//%求出纯净语音信号的真实幅度 
FILE *fp1; 
for(i=0;i<wav_length;i++)
{
wavout[i]=0.0;
}
for(i=0;i<n_frame;i++)
{
int m1,m2,m3;
m3=0;
m1=i*step_len;
m2=i*step_len+frame_len;

for(j=m1;j<=m2;j++)
{
m3=j-m1;
wavout[j]=wavout[j]+voice_timedomain[i][m3];
}
}

for(i=0;i<wav_length;i++)
{ 
printf("%d  %f\n",i,wavout[i]);
}  
fp1 = fopen("Sine1.dat","w");
for(i=0;i<wav_length;i++)
{
fprintf(fp1,"%d  %f;\n", i,wavout[i]);
}
fclose(fp1);
}

1、使用gnuplot画加入噪声后的WAV文件

 

C语言使用正弦数据序列产生WAV文件并使用谱减法降噪

2、使用gnuplot画使用谱减法后的WAV文件

C语言使用正弦数据序列产生WAV文件并使用谱减法降噪

通过对比加噪前后信号波形图可以看出,谱减法在噪声一定的情况下能够明显起到降噪作用,但是当噪声达到一定程度时,谱减法去噪性能大大降低。

四、使用MATLAB对WAV文件进行谱减法去噪。

目前大部分语音增强使用matlab进行仿真,因为matlab工具强大并且语法简单易懂,语音处理结果比较完美。

% 选择.wav音频文件
[fname,pname]=uigetfile(...
    {'*.wav';'*.*'},...
    'Input wav File');
[x,fs] = audioread(fullfile(pname,fname));
x = x(1:8912,1);     % 如果是双声道,取单通道
N = length(x);       % 帧长
max_x = max(x);
noise_add = random('norm', 0, 0.1*max_x, [N,1]);
    
% 添加高斯噪声
y = x + noise_add;
noise_estimated = random('norm', 0, 0.1*max_x, [N,1]);
fft_y = fft(y);
fft_n = fft(noise_estimated);
E_noise = sum(abs(fft_n)) / N;
mag_y = abs(fft_y);
phase_y = angle(fft_y);   % 保留相位信息
mag_s = mag_y - E_noise;
mag_s(mag_s<0)=0;
 
% 恢复`
fft_s = mag_s .* exp(1i.*phase_y);
s = ifft(fft_s);
 
subplot(311);plot(x);ylim([-0.15,0.15]);title('原始干净信号');
subplot(312);plot(y);ylim([-0.15,0.15]);title('加噪信号');
subplot(313);plot(real(s));ylim([-0.15,0.15]);title('谱减法去噪后信号');

C语言使用正弦数据序列产生WAV文件并使用谱减法降噪

通过图像对比可以看出,基于谱减法的语音增强算法,并用 MATLAB 进行模拟仿真,仿真结果表明谱减算法取得了较好的去噪效果,谱减法可以有效降低噪声,起到语音增强的效果。

 

 

相关文章:

  • 2021-11-02
  • 2022-12-23
  • 2022-12-23
  • 2021-06-11
  • 2021-08-03
  • 2022-12-23
  • 2021-09-22
  • 2022-12-23
猜你喜欢
  • 2021-08-17
  • 2021-06-21
  • 2021-04-25
  • 2021-10-17
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案