python求語音信號短時能量、短時過零率、語譜圖

python語音信號處理(二)

一、短時能量

短時能量主要用於區分濁音段和清音段,因爲濁音時E(i)值比清音時大得多;區分聲母與韻母的分界和無話段與有話段分界。

計算第i幀語音信號yi(n)的短時能量公式爲:
E(i)=n=0L1yi2(n),1<=i<=fn E(i)=\sum_{n=0}^{L-1}y_i^2(n),\qquad1<=i<=fn
求一幀語音的短時能量,直接上代碼:

import numpy as np
import wave
import matplotlib.pyplot as plt
wlen=512
inc=128
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
#print(str_data[:10])
wave_data = np.fromstring(str_data, dtype=np.short)
#print(wave_data[:10])
wave_data = wave_data*1.0/(max(abs(wave_data)))
print(wave_data[:10])
time = np.arange(0, wlen) * (1.0 / framerate)
signal_length=len(wave_data) #信號總長度
if signal_length<=wlen: #若信號長度小於一個幀的長度,則幀數定義爲1
        nf=1
else: #否則,計算幀的總長度
        nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc))
pad_length=int((nf-1)*inc+wlen) #所有幀加起來總的鋪平後的長度
zeros=np.zeros((pad_length-signal_length,)) #不夠的長度使用0填補,類似於FFT中的擴充數組操作
pad_signal=np.concatenate((wave_data,zeros)) #填補後的信號記爲pad_signal
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T  #相當於對所有幀的時間點進行抽取,得到nf*nw長度的矩陣
print(indices[:2])
indices=np.array(indices,dtype=np.int32) #將indices轉化爲矩陣
frames=pad_signal[indices] #得到幀信號
a=frames[30:31]
print(a[0])
windown=np.hanning(512)
b=a[0]*windown
c=np.square(b)
plt.figure(figsize=(10,4))
plt.plot(time,c,c="g")
plt.grid()
plt.show()

輸出結果:
在這裏插入圖片描述

信號加的是漢寧窗,上圖爲一幀語音的短時能量,下面爲整段語音信號的短時能量。

import numpy as np
import wave
import matplotlib.pyplot as plt
wlen=512
inc=128
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
print(wave_data[:10])
signal_length=len(wave_data) #信號總長度
if signal_length<=wlen: #若信號長度小於一個幀的長度,則幀數定義爲1
        nf=1
else: #否則,計算幀的總長度
        nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc))
print(nf)
pad_length=int((nf-1)*inc+wlen) #所有幀加起來總的鋪平後的長度
zeros=np.zeros((pad_length-signal_length,)) #不夠的長度使用0填補,類似於FFT中的擴充數組操作
pad_signal=np.concatenate((wave_data,zeros)) #填補後的信號記爲pad_signal
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T  #相當於對所有幀的時間點進行抽取,得到nf*nw長度的矩陣
print(indices[:2])
indices=np.array(indices,dtype=np.int32) #將indices轉化爲矩陣
frames=pad_signal[indices] #得到幀信號
windown=np.hanning(wlen)
d=np.zeros(nf)
x=np.zeros(nf)
time = np.arange(0,nf) * (inc*1.0/framerate)
for i in range(0,nf):
        a=frames[i:i+1]
        b = a[0] * windown
        c=np.square(b)
        d[i]=np.sum(c)
d = d*1.0/(max(abs(d)))
print(d)
plt.figure(figsize=(10,4))
plt.plot(time,d,c="g")
plt.grid()
plt.show()

輸出結果爲:

在這裏插入圖片描述
橫軸爲時間,縱座標爲歸一化後的短時能量。

該語音信號爲普通話的“藍天,白雲”,可以比較清晰的看出短時能量的四個部分。

清音和濁音的區別:發清音時聲帶不振動,發濁音時聲帶振動。

二、短時平均過零率

​ 短時平均過零率表示一幀語音中語音信號波形穿過橫軸(零電平)的次數。過零率分析是.語音時域分析中最簡單的一種。對於連續語音信號,過零即意味着時域波形通過時間軸;而對於離散信號,如果相鄰的取樣值改變符號,則稱爲過零。短時平均過零率就是樣本數值改變符號的次數。

定義語音信號x(n)分幀後有yi(n),幀長爲L,短時平均過零率爲
Z(i)=12n=0L1sgn[yi(n)]sgn[yi(n1)],1<=i<=fn Z(i)=\frac{1}{2}\sum_{n=0}^{L-1}|sgn[y_{i}(n)]-sgn[y_i(n-1)]|,\qquad1<=i<=fn

sgn[n]={1x>=01u<0 sgn[n]=\begin{cases} -1,x>=0\\ 1, u<0\end{cases}

​ 在實際計算短時平均過零率參數時,需要十分注意的一個問題是,如果輸人信號中包含漂移,即信號在通往AD轉換器前就有一個直流分量,使AD轉換後繼續帶有這個直流分量。因爲直流分量的存在影響了短時平均過零率的正確估算,所以建議在語音信號處理前先消除直流分量。

上代碼:

import numpy as np
import wave
wlen=512
inc=128
import matplotlib.pyplot as plt
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
signal_length=len(wave_data) #信號總長度
if signal_length<=wlen: #若信號長度小於一個幀的長度,則幀數定義爲1
        nf=1
else: #否則,計算幀的總長度
        nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc))
pad_length=int((nf-1)*inc+wlen) #所有幀加起來總的鋪平後的長度
zeros=np.zeros((pad_length-signal_length,)) #不夠的長度使用0填補,類似於FFT中的擴充數組操作
pad_signal=np.concatenate((wave_data,zeros)) #填補後的信號記爲pad_signal
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T  #相當於對所有幀的時間點進行抽取,得到nf*nw長度的矩陣
print(indices[:2])
indices=np.array(indices,dtype=np.int32) #將indices轉化爲矩陣
frames=pad_signal[indices]
windown=np.hanning(wlen)
c=np.zeros(nf)
for i in range(nf):
    a=frames[i:i+1]
    b=windown*a[0]
    for j in range(wlen-1):
        if b[j]*b[j+1]<0:
            c[i]=c[i]+1
time = np.arange(0,nf) * (inc*1.0/framerate)
plt.figure(figsize=(10,4))
plt.plot(time,c,c="g")
plt.grid()
plt.show()

輸出結果:

在這裏插入圖片描述

​ 通過分析語音信號發現,發濁音時,儘管聲道有若干共振峯,但由於聲門波引起譜的高頻跌落,所以其語音能量約集中在3 kHz以下;而發清音時,多數能量出現在較高頻率上。因爲高頻意味着高的短時平均過零率,低頻意味着低的平均過零率,所以可以認爲,濁音時具有較低的過零率,而清音時具有較高的過零率。當然,這種高低僅是相對而言的,並沒有精確的數值關係。

​ 利用短時平均過零率還可以從背景噪聲中找出語音信號,可用於判斷寂靜無話段與有話段的起點和終點位置。在背景噪聲較小時,用平均能量識別較爲有效;而在背景噪聲較大時,用短時平均過零率識別較爲有效。

​ 我們主要應用短時平均過零率來判斷清音和濁音,有話段和無話段。

三、語音信號頻域處理

​ 在語音信號處理中,信號在頻域或其他變換上的分析和處理佔有重要地位,在頻域和其他變換域上研究語音信號,可以使信號在時域上無法表現出來的某些特徵變得十分明顯。

下面將“藍天,白雲”這段語音信號轉化爲頻域

import numpy as np
import wave
import matplotlib.pyplot as plt
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
fft_signal = np.fft.fft(wave_data)     #語音信號FFT變換
fft_signal = abs(fft_signal)#取變換結果的模
plt.figure(figsize=(10,4))
time=np.arange(0,nframes)*framerate/nframes
plt.plot(time,fft_signal,c="g")
plt.grid()
plt.show()

輸出結果:

在這裏插入圖片描述

短時傅里葉變換

​ 在信號處理髮展史上,每一次理論上的突破都帶來了信號處理領域的重大變革。傳統傅里葉變換( Fourier Transform,FT)是以應用數學爲基礎建立起來的一門學科,它將信號分解爲各個不同頻率分量的組合,使信號的時域特徵與頻域特徵聯繫起來,成爲信號處理的有力工具。但是傅里葉變換使用的是一種全局變換,因此它無法表述信號的時頻局域性質。爲了能夠分析處理非平穩信號,人們對傅里葉變換進行了推廣,提出了短時傅里葉變換(Short Time Fou-rier Transform, STFT)和其他變換域上的處理,這些理論都可應用於語音信號分析處理。

​ 短時傅里葉分析(Short Time Fourier Analysis, STFA)適用於分析緩慢時變信號的頻譜分析,在語音分析處理中已經得到廣泛應用。其方法是先將語音信號分幀,再將各幀進行傅里葉變換。每一幀語音信號可以被認爲是從各個不同的平穩信號波形中截取出來的,各幀語音的短時頻譜就是各個平穩信號波形頻譜的近似。

由於語音信號是短時平穩的,因此可以·對·語音·進行·分幀處理,計算某一幀的傅里葉變換,這樣得到的就是短時傅里葉變換,其定義爲
Xn(ejw)=m=x(m)w(nm)ejw X_n(e^{jw})=\sum_{m=-\infty}^{\infty}x(m)w(n-m)e^{jw}
式中,下x(n)爲語音信號序列:w(n)爲實數窗序列,短時傅里葉變換是時間n和角頻率w的函數,他反應了語音信號的頻率隨時間變化的特性。

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-RbV9gnE9-1573277367864)(C:\Users\jh\AppData\Roaming\Typora\typora-user-images\1572942290950.png)]

短時傅里葉變換有兩種不同的解釋,一種是當n固定不變時,X(ejw)爲序列w(n-m)x(m)的標準傅里葉變換,此時X(ejw)具有與標準傅里葉變換相同的性質。另一種是當w固定不變時,可以將X(ejw)視爲信號x(n)與窗函數指數加權w(n)ejw的卷積。

下面取語音的第50幀的語音信號做FFT變換:

import numpy as np
import wave
import matplotlib.pyplot as plt
wlen=512
inc=128
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
print(wave_data[:10])
signal_length=len(wave_data) #信號總長度
if signal_length<=wlen: #若信號長度小於一個幀的長度,則幀數定義爲1
        nf=1
else: #否則,計算幀的總長度
        nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc))
print(nf)
pad_length=int((nf-1)*inc+wlen) #所有幀加起來總的鋪平後的長度
zeros=np.zeros((pad_length-signal_length,)) #不夠的長度使用0填補,類似於FFT中的擴充數組操作
pad_signal=np.concatenate((wave_data,zeros)) #填補後的信號記爲pad_signal
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T  #相當於對所有幀的時間點進行抽取,得到nf*nw長度的矩陣
print(indices[:2])
indices=np.array(indices,dtype=np.int32) #將indices轉化爲矩陣
frames=pad_signal[indices] #得到幀信號
windown=np.hanning(wlen)
a=frames[50:51]
b=a[0]*windown
fft_signal = np.fft.fft(b)
fft_signal=abs(fft_signal)
time=np.arange(0,wlen)*framerate/nframes
plt.figure(figsize=(10,4))
plt.plot(time,fft_signal,c="g")
plt.grid()
plt.show()

輸出結果:

在這裏插入圖片描述

語普圖

​ 通過語音的短時傅里葉分析可以研究語音的短時頻譜隨時間的變化關係。在數字信號處理(Digital Signal Processing ,DSP)技術發展起來以前,人們就已經利用語譜儀來分析和記錄語音信號的短時頻譜。語譜儀是把語音的電信號送人一-組頻率依次相接的窄帶濾波器中,各個窄帶濾波器的輸出經整流均方後按頻率由低到高的順序記錄在一卷記錄紙上。信號的強弱由記錄在紙上的灰度來表示:如果某個濾波器輸出的信號強,相應的記錄將濃黑;反之,則淺淡一些。記錄紙按照一定的速度旋轉,相當於按不同的時間記錄了相應的濾波器輸出。由此得到的圖形就是語音信號的語譜圖,其水平方向是時間軸,垂直方向是頻率軸,圖上的灰度條紋代表各個時刻的語音短時譜。語譜圖反映了語音信號的動態頻譜特性,在語音分析中具有重要的實用價值,被稱爲可視語音。

import numpy as np
import wave
import matplotlib.pyplot as plt
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
plt.specgram(wave_data,Fs = framerate, scale_by_freq = True, sides = 'default')
plt.show()

specgram()官網描述是:

matplotlib.pyplot.specgram(x, NFFT=None, Fs=None, Fc=None, detrend=None, window=None, noverlap=None, cmap=None, xextent=None, pad_to=None, sides=None, scale_by_freq=None, mode=None, scale=None, vmin=None, vmax=None, *, data=None, **kwargs)

參數:

x : 1-D array or sequence 數組或序列
Array or sequence containing the data.
數組或序列包含的數據。

Fs : scalar 標量
The sampling frequency (samples per time unit). It is used to calculate the Fourier frequencies, freqs, in cycles per time unit. The default value is 2.
抽樣頻率(每秒抽樣數),用來計算傅里葉頻率,以週期/時間單位表示。默認值爲2

window : callable or ndarray 可調用的或無期限的
A function or a vector of length NFFT. To create window vectors see window_hanning, window_none, numpy.blackman, numpy.hamming, numpy.bartlett, scipy.signal, scipy.signal.get_window, etc. The default is window_hanning. If a function is passed as the argument, it must take a data segment as an argument and return the windowed version of the segment.
長度爲nfft的函數或向量。要創建窗口向量,請參見window_hanning、window_none、numpy.blackman、numpy.hamming、numpy.bartlett、scipy.signal、scipy.signal.get_window等。默認值爲window_hanning。如果函數作爲參數傳遞,則必須將數據段作爲參數,並返回該段的窗口版本。

sides : {‘default’, ‘onesided’, ‘twosided’}
Specifies which sides of the spectrum to return. Default gives the default behavior, which returns one-sided for real data and both for complex data. ‘onesided’ forces the return of a one-sided spectrum, while ‘twosided’ forces two-sided.
指定要返回頻譜的哪一側。默認值提供默認行爲,對於實際數據和複雜數據都返回單面。單側的力是單側譜的返回,而“雙側的”力是雙側譜的返回。

pad_to : int
The number of points to which the data segment is padded when performing the FFT. This can be different from NFFT, which specifies the number of data points used. While not increasing the actual resolution of the spectrum (the minimum distance between resolvable peaks), this can give more points in the plot, allowing for more detail. This corresponds to the n parameter in the call to fft(). The default is None, which sets pad_to equal to NFFT
執行FFT時填充數據段的點數。這可能不同於NFFT,它指定使用的數據點數量。雖然不能增加頻譜的實際分辨率(可分辨峯之間的最小距離),但這可以在圖中給出更多的點,從而提供更多的細節。這對應於對fft()的調用中的n參數。默認值爲無,它將pad_設置爲等於nfft

NFFT : int
The number of data points used in each block for the FFT. A power 2 is most efficient. The default value is 256. This should NOT be used to get zero padding, or the scaling of the result will be incorrect. Use pad_to for this instead.
執行FFT時填充數據段的點數。這可能不同於NFFT,它指定使用的數據點數量。雖然不能增加光譜的實際分辨率(可分辨峯之間的最小距離),但這可以在圖中給出更多的點,從而提供更多的細節。這對應於對fft()的調用中的n參數。默認值爲無,它將pad_設置爲等於nfft

detrend : {‘none’, ‘mean’, ‘linear’} or callable, default ‘none’
The function applied to each segment before fft-ing, designed to remove the mean or linear trend. Unlike in MATLAB, where the detrend parameter is a vector, in Matplotlib is it a function. The mlab module defines detrend_none, detrend_mean, and detrend_linear, but you can use a custom function as well. You can also use a string to choose one of the functions: ‘none’ calls detrend_none. ‘mean’ calls detrend_mean. ‘linear’ calls detrend_linear.
在FFT之前應用到每個段的函數,其被設計爲去除平均值或線性趨勢。與Matlab不同的是,dTrend參數是矢量,matplottlib是函數。MLAB模塊定義DETrend_None、DeTrend_mean和DeTrend_Linear,但也可以使用自定義函數。您還可以使用字符串來選擇其中一個函數:“無”調用deTrend_none。“mean”調用dTrend_mean。“線性”調用dTrend_linear。

scale_by_freq : bool, optional
Specifies whether the resulting density values should be scaled by the scaling frequency, which gives density in units of Hz^-1. This allows for integration over the returned frequency values. The default is True for MATLAB compatibility.
指定所產生的密度值是否應通過縮放頻率進行縮放,該頻率提供以Hz^-1爲單位的密度。這允許對返回的頻率值進行集成。默認情況下,MATLAB兼容性是正確的

mode : {‘default’, ‘psd’, ‘magnitude’, ‘angle’, ‘phase’}
What sort of spectrum to use. Default is ‘psd’, which takes the power spectral density. ‘magnitude’ returns the magnitude spectrum. ‘angle’ returns the phase spectrum without unwrapping. ‘phase’ returns the phase spectrum with unwrapping.
使用什麼樣的頻譜。默認值是“psd”,它採用功率譜密度。Magnition“返回幅度譜。”Angle“返回相位譜而不展開。”“phase”返回展開後的相位譜。

noverlap : int
The number of points of overlap between blocks. The default value is 128.
塊之間重疊點的數目。默認值是128。
scale : {‘default’, ‘linear’, ‘dB’}
The scaling of the values in the spec. ‘linear’ is no scaling. ‘dB’ returns the values in dB scale. When mode is ‘psd’, this is dB power (10 * log10). Otherwise this is dB amplitude (20 * log10). ‘default’ is ‘dB’ if mode is ‘psd’ or ‘magnitude’ and ‘linear’ otherwise. This must be ‘linear’ if mode is ‘angle’ or ‘phase’.
規範中值的縮放。“線性”不是縮放。‘db’返回db比例尺中的值。當模式爲‘PSD’時,這是db功率(10log 10)。否則,這是db振幅(20log 10)。默認的是‘db’,如果模式是‘PSD’或‘大小’,‘線性’,否則。這必須是‘線性’,如果模式是‘角度’或‘相位’。

Fc : int
The center frequency of x (defaults to 0), which offsets the x extents of the plot to reflect the frequency range used when a signal is acquired and then filtered and downsampled to baseband.
x的中心頻率(默認值爲0),它抵消了圖中的x個範圍,以反映在獲取信號、然後濾波和降採樣到基帶時所使用的頻率範圍。

cmap
A matplotlib.colors.Colormap instance; if None, use default determined by rc
matplotlib.chros.colmap實例;如果沒有,則使用rc確定的默認值。

xextent : None or (xmin, xmax)
The image extent along the x-axis. The default sets xmin to the left border of the first bin (spectrum column) and xmax to the right border of the last bin. Note that for noverlap>0 the width of the bins is smaller than those of the segments.
沿x軸的圖像範圍。默認值爲第一個bin(頻譜列)的左邊界和最後一個bin的右邊界的xmax。請注意,對於NoVerlap>0,bin的寬度小於段的寬度。

**kwargs
Additional kwargs are passed on to imshow which makes the specgram image.
更多的kwargs被傳遞到圖像中,這就產生了散斑圖像。

返回值:
spectrum : 2-D array
Columns are the periodograms of successive segments.
列是連續段的週期圖

freqs : 1-D array
The frequencies corresponding to the rows in spectrum.
與光譜中的行相對應的頻率。

t : 1-D array
The times corresponding to midpoints of segments (i.e., the columns in spectrum).
對應於分段中點(即頻譜中的列)的時間。

im : instance of class AxesImage
The image created by imshow containing the spectrogram
包含頻譜圖的圖像

Notes

The parameters detrend and scale_by_freq do only apply when mode is set to ‘psd’.
只有當“模式”設置爲“PSD”時,參數“按頻率遞減和縮放”才適用。

Note

In addition to the above described arguments, this function can take a data keyword argument. If such a data argument is given, the following arguments are replaced by data[]:

All arguments with the following names: ‘x’.
Objects passed as data must support item access (data[]) and membership test ( in data).

返回:

spectrum:頻譜矩陣
freqs:頻譜圖每行對應的頻率
ts:頻譜圖每列對應的時間
fig :圖像

輸出結果:

在這裏插入圖片描述

​ 語譜圖的橫座標是時間,縱座標是頻率,座標點值爲語音數據能量。由於是採用二維平面表達三維信息,所以能量值的大小是通過顏色來表示的,顏色深,表示該點的語音能量越強。

​ 我們可以觀察語音不同頻段的信號強度隨時間的變化情況。由於音樂信號本身頻率豐富,不太容易看出規律,我們可以觀察一下純粹的語音數據的語譜圖。從圖中可以看到明顯的一條條橫方向的條紋,我們稱爲“聲紋”,有很多應用。條紋的地方實際是顏色深的點聚集的地方,隨時間延續,就延長成條紋,也就是表示語音中頻率值爲該點橫座標值的能量較強,在整個語音中所佔比重大,那麼相應影響人感知的效果要強烈得多。而一般語音中數據是週期性的,所以,能量強點的頻率分佈是頻率週期的,即存在300Hz強點,則一般在n*300Hz點也會出現強點,所以我們看到的語譜圖都是條紋狀的。儘管客觀人發聲器官的音域是有限度的,即一般人發聲最高頻率爲4000Hz,樂器的音域要比人寬很多,打擊樂器的上限可以到20KHz。但是,由於我們數字分析頻率時,採用的是算法實現的,一般是FFT,所以其結果是由採樣率決定的,即儘管是上限爲4000Hz的語音數據,如果採用16Khz的採樣率來分析,則仍然可以在4000Hz以上的頻段發現有數據分佈,則可以認爲是算法誤差,非客觀事實

語譜圖生成流程(引用)

引用blog:
語音信號處理之(四)梅爾頻率倒譜系數(MFCC)
我們處理的是語音信號,那麼如何去描述它很重要。因爲不同的描述方式放映它不同的信息。那怎樣的描述方式才利於我們觀測,利於我們理解呢?這裏我們先來了解一個叫聲譜圖的東西。

img

​ 這裏,這段語音被分爲很多幀,每幀語音都對應於一個頻譜(通過短時FFT計算),頻譜表示頻率與能量的關係。在實際使用中,頻譜圖有三種,即線性振幅譜、對數振幅譜、自功率譜(對數振幅譜中各譜線的振幅都作了對數計算,所以其縱座標的單位是dB(分貝)。這個變換的目的是使那些振幅較低的成分相對高振幅成分得以拉高,以便觀察掩蓋在低幅噪聲中的週期信號)。

img

​ 我們先將其中一幀語音的頻譜通過座標表示出來,如上圖左。現在我們將左邊的頻譜旋轉90度。得到中間的圖。然後把這些幅度映射到一個灰度級表示(也可以理解爲將連續的幅度量化爲256個量化值?),0表示黑,255表示白色。幅度值越大,相應的區域越黑。這樣就得到了最右邊的圖。那爲什麼要這樣呢?爲的是增加時間這個維度,這樣就可以顯示一段語音而不是一幀語音的頻譜,而且可以直觀的看到靜態和動態的信息。優點稍後呈上。

​ 這樣我們會得到一個隨着時間變化的頻譜圖,這個就是描述語音信號的spectrogram聲譜圖。

img

下圖是一段語音的聲譜圖,很黑的地方就是頻譜圖中的峯值(共振峯formants)。

img

那我們爲什麼要在聲譜圖中表示語音呢?

​ 首先,音素(Phones)的屬性可以更好的在這裏面觀察出來。另外,通過觀察共振峯和它們的轉變可以更好的識別聲音。隱馬爾科夫模型(Hidden Markov Models)就是隱含地對聲譜圖進行建模以達到好的識別性能。還有一個作用就是它可以直觀的評估TTS系統(text to speech)的好壞,直接對比合成的語音和自然的語音聲譜圖的匹配度即可。

研究加不同長度的窗函數對語譜圖的影響

​ 語譜圖的時間分辨率和頻率分辨率是由窗函數的特性決定的,可以按照短時傅里葉變換的第一種解釋來分析頻率分辨率。如果需要觀察語音諧波的細節,則需要提高語譜圖的頻率分辨率,也就是減小窗函數的帶通寬度。由於帶通寬度是與窗函數成反比的,因此提高頻率分辨率必須要增加窗長。這種情況下得到的語譜圖稱爲窄帶語譜圖。

實際上就是信號的時寬越大(小),信號的頻帶寬度越小(大)

窄帶語譜圖

依然是上面的“藍天,白雲”的語音,語音採樣頻率爲8000HZ,取窗長爲512個數據點,幀移爲窗長的1/4,即128個數據點。

import numpy as np
import wave
import matplotlib.pyplot as plt
def windows(name='Hamming', N=20):
    # Rect/Hanning/Hamming
    if name == 'Hamming':
        window = np.array([0.54 - 0.46 * np.cos(2 * np.pi * n / (N - 1)) for n in range(N)])
    elif name == 'Hanning':
        window = 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 window
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
plt.specgram(wave_data,Fs = framerate,NFFT=512,window=windows("Hanning",512),noverlap=384,scale_by_freq = True, sides = 'default')
plt.show()

在這裏插入圖片描述
從結果圖中可以清楚看到諧波的結構,頻率分辨率非常好,但是時間上的分辨率就不理想.

窄帶語譜圖,頻率分辨率太過精細,不能很好體現出共振峯的大致位置,即反映不出基波的變化特性。

寬帶語譜圖

依然是上面的“藍天,白雲”的語音,語音採樣頻率爲8000HZ,取窗長爲128個數據點,幀移爲窗長的1/4,即32個數據點.

import numpy as np
import wave
import matplotlib.pyplot as plt
def windows(name='Hamming', N=20):
    # Rect/Hanning/Hamming
    if name == 'Hamming':
        window = np.array([0.54 - 0.46 * np.cos(2 * np.pi * n / (N - 1)) for n in range(N)])
    elif name == 'Hanning':
        window = 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 window
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
plt.specgram(wave_data,Fs = framerate,NFFT=128,window=windows("Hanning",128),noverlap=96,scale_by_freq = True, sides = 'default')
plt.show()

在這裏插入圖片描述
與窄帶語譜圖相反,寬帶語譜圖的時間分辨率很好,頻率分辨率較低,不能很好反映聲音的紋理特性,反映了頻譜的時變特性,能很好分辨出共振峯的大致位置,但分辨不清諧波結構。

研究加不同窗對語譜圖的影響

import numpy as np
import wave
import matplotlib.pyplot as plt
def windows(name='Hamming', N=20):
    # Rect/Hanning/Hamming
    if name == 'Hamming':
        window = np.array([0.54 - 0.46 * np.cos(2 * np.pi * n / (N - 1)) for n in range(N)])
    elif name == 'Hanning':
        window = 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 window
f = wave.open(r"lantian.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
str_data = f.readframes(nframes)
wave_data = np.fromstring(str_data, dtype=np.short)
wave_data = wave_data*1.0/(max(abs(wave_data)))
plt.specgram(wave_data,Fs = framerate, window=windows("Hanning",256),scale_by_freq = True, sides = 'default')
plt.show()

加海l寧窗的效果圖:
在這裏插入圖片描述

加漢明窗的效果圖:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-MndsS23j-1573277367867)(C:\Users\jh\AppData\Roaming\Typora\typora-user-images\1573191021110.png)]

加矩形窗的效果圖:

在這裏插入圖片描述寫的比較亂,哪裏有錯誤還請大佬們指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章