python語(yǔ)音信號(hào)處理詳細(xì)教程
1.語(yǔ)音信號(hào)的產(chǎn)生與特性
我們要對(duì)語(yǔ)音進(jìn)行分析,首先要提取能夠表示該語(yǔ)音的特征參數(shù),有了特征參數(shù)才可能利用這些參數(shù)進(jìn)行有效的處理,在對(duì)語(yǔ)音信號(hào)處理的過(guò)程中,語(yǔ)音信號(hào)的質(zhì)量不僅取決于處理方法,同時(shí)取決于時(shí)候選對(duì)了合適的特征參數(shù)。
語(yǔ)音信號(hào)是一個(gè)非平穩(wěn)的時(shí)變信號(hào),但語(yǔ)音信號(hào)是由聲門(mén)的激勵(lì)脈沖通過(guò)聲道形成的,而聲道(人的口腔、鼻腔)的肌肉運(yùn)動(dòng)是緩慢的,所以“短時(shí)間”(10~30ms)內(nèi)可以認(rèn)為語(yǔ)音信號(hào)是平穩(wěn)時(shí)不變的。由此構(gòu)成了語(yǔ)音信號(hào)的“短時(shí)分析技術(shù)”。
提取的不同的語(yǔ)音特征參數(shù)對(duì)應(yīng)著不同的語(yǔ)音信號(hào)分析方法:時(shí)域分析、頻域分析、倒譜域分析…由于語(yǔ)音信號(hào)最重要的感知特性反映在功率譜上,而相位變化只起到很小的作用,所有語(yǔ)音頻域分析更加重要。
2.語(yǔ)音的讀取
本實(shí)驗(yàn)使用wave庫(kù),實(shí)現(xiàn)語(yǔ)音文件的讀取、波形圖繪制,相關(guān)的庫(kù)還有librosa、scipy等
import wave #調(diào)用wave模塊
import matplotlib.pyplot as plt #調(diào)用matplotlib.pyplot模塊作為Plt
import numpy as np #調(diào)用numpy模塊記作np
import scipy.signal as signal
import pyaudio
%matplotlib inline
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來(lái)正常顯示中文標(biāo)簽
plt.rcParams['axes.unicode_minus'] = False # 用來(lái)正常顯示符號(hào)
f = wave.open(r"C:\Users\zyf\Desktop\Jupyter\1.wav", "rb")#讀取語(yǔ)音文件
params = f.getparams() #返回音頻參數(shù)
nchannels, sampwidth, framerate, nframes = params[:4] #賦值聲道數(shù),量化位數(shù),采樣頻率,采樣點(diǎn)數(shù)
print(nchannels,sampwidth,framerate,nframes)# 輸出聲道數(shù),量化位數(shù),采樣頻率,采樣點(diǎn)數(shù)
str_data = f.readframes(nframes) # 讀取nframes個(gè)數(shù)據(jù),返回字符串格式
f.close()
wave_data = np.frombuffer(str_data, dtype=np.short) # 將字符串轉(zhuǎn)換為數(shù)組,得到一維的short類(lèi)型的數(shù)組
wave_data = wave_data * 1.0 / (max(abs(wave_data))) # 賦值的歸一化
time = np.arange(0, nframes) * (1.0 / framerate) # 最后通過(guò)采樣點(diǎn)數(shù)和取樣頻率計(jì)算出每個(gè)取樣的時(shí)間
# 整合左聲道和右聲道的數(shù)據(jù),如果語(yǔ)音為雙通道語(yǔ)音,具體代碼需做調(diào)整
#wave_data = np.reshape(wave_data, [nframes, nchannels])
# wave_data.shape = (-1, 2) # -1的意思就是沒(méi)有指定,根據(jù)另一個(gè)維度的數(shù)量進(jìn)行分割
plt.figure() # 單通道語(yǔ)音波形圖
plt.plot(time, wave_data[:])
plt.xlabel("時(shí)間/s",fontsize=14)
plt.ylabel("幅度",fontsize=14)
plt.title("波形圖",fontsize=14)
plt.grid() # 標(biāo)尺
plt.tight_layout() # 緊密布局
plt.show()

3.語(yǔ)音的播放
# 音頻的播放,本實(shí)驗(yàn)使用pyaudio(代碼相對(duì)matlab較麻煩,后期簡(jiǎn)化)
import pyaudio
import wave
chunk = 1024
wf = wave.open(r"C:\Users\zyf\Desktop\Jupyter\1.wav", 'rb')
p = pyaudio.PyAudio()
# 打開(kāi)聲音輸出流
stream = p.open(format = p.get_format_from_width(wf.getsampwidth()),
channels = wf.getnchannels(),
rate = wf.getframerate(),
output = True)
# 寫(xiě)聲音輸出流到聲卡進(jìn)行播放
while True:
data = wf.readframes(chunk)
if data == "":
break
stream.write(data)
stream.stop_stream()
stream.close()
p.terminate() # 關(guān)閉PyAudio
4.音頻文件的寫(xiě)入
# 音頻文件的寫(xiě)入、存儲(chǔ) # 使用wave庫(kù),相關(guān)的庫(kù)還有l(wèi)ibrosa、scipy等,讀寫(xiě)操作上的差異參閱博客: https://blog.csdn.net/weixin_38346042/article/details/119906391 import wave import numpy as np import scipy.signal as signal framerate = 44100 # 采樣頻率 time = 10 # 持續(xù)時(shí)間 t = np.arange(0, time, 1.0/framerate) # 調(diào)用scipy.signal庫(kù)中的chrip函數(shù), # 產(chǎn)生長(zhǎng)度為10秒、取樣頻率為44.1kHz、100Hz到1kHz的頻率掃描波 wave_data = signal.chirp(t, 100, time, 1000, method='linear') * 10000 # 由于chrip函數(shù)返回的數(shù)組為float64型, # 需要調(diào)用數(shù)組的astype方法將其轉(zhuǎn)換為short型。 wave_data = wave_data.astype(np.short) # 打開(kāi)WAV音頻用來(lái)寫(xiě)操作 f = wave.open(r"sweep.wav", "wb") f.setnchannels(1) # 配置聲道數(shù) f.setsampwidth(2) # 配置量化位數(shù) f.setframerate(framerate) # 配置取樣頻率 comptype = "NONE" compname = "not compressed" # 也可以用setparams一次性配置所有參數(shù) # outwave.setparams((1, 2, framerate, nframes,comptype, compname)) # 將wav_data轉(zhuǎn)換為二進(jìn)制數(shù)據(jù)寫(xiě)入文件 f.writeframes(wave_data.tobytes()) f.close()
5.語(yǔ)音的分幀加窗
5.1 分幀
語(yǔ)音數(shù)據(jù)和視頻數(shù)據(jù)不同,本沒(méi)有幀的概念,但是為了傳輸與存儲(chǔ),我們采集的音頻數(shù)據(jù)都是一段一段的。為了程序能夠進(jìn)行批量處理,會(huì)根據(jù)指定的長(zhǎng)度(時(shí)間段或者采樣數(shù))進(jìn)行分段,結(jié)構(gòu)化為我們編程的數(shù)據(jù)結(jié)構(gòu),這就是分幀。語(yǔ)音信號(hào)在宏觀上是不平穩(wěn)的,在微觀上是平穩(wěn)的,具有短時(shí)平穩(wěn)性(10—30ms內(nèi)可以認(rèn)為語(yǔ)音信號(hào)近似不變),這個(gè)就可以把語(yǔ)音信號(hào)分為一些短段來(lái)進(jìn)行處理,每一個(gè)短段稱(chēng)為一幀(CHUNK)。
5.2 幀移
由于我們常用的信號(hào)處理方法都要求信號(hào)是連續(xù)的,也就說(shuō)必須是信號(hào)開(kāi)始到結(jié)束,中間不能有斷開(kāi)。然而我們進(jìn)行采樣或者分幀后數(shù)據(jù)都斷開(kāi)了,所以要在幀與幀之間保留重疊部分?jǐn)?shù)據(jù),以滿(mǎn)足連續(xù)的要求,這部分重疊數(shù)據(jù)就是幀移。

幀長(zhǎng)=重疊+幀移
5.3 加窗
我們處理信號(hào)的方法都要求信號(hào)是連續(xù)條件,但是分幀處理的時(shí)候中間斷開(kāi)了,為了滿(mǎn)足條件我們就將分好的幀數(shù)據(jù)乘一段同長(zhǎng)度的數(shù)據(jù),這段數(shù)據(jù)就是窗函數(shù)整個(gè)周期內(nèi)的數(shù)據(jù),從最小變化到最大,然后最小。
常用的窗函數(shù):矩形窗、漢明窗、海寧窗

加窗即與一個(gè)窗函數(shù)相乘,加窗之后是為了進(jìn)行傅里葉展開(kāi).
1.使全局更加連續(xù),避免出現(xiàn)吉布斯效應(yīng)
2.加窗時(shí)候,原本沒(méi)有周期性的語(yǔ)音信號(hào)呈現(xiàn)出周期函數(shù)的部分特征。
加窗的代價(jià)是一幀信號(hào)的兩端部分被削弱了,所以在分幀的時(shí)候,幀與幀之間需要有重疊。

# 加窗分幀(接上)
# 語(yǔ)音分幀、加窗
wlen=512 # 每幀信號(hào)長(zhǎng)度
inc=128 # 幀移
signal_length=len(wave_data) #信號(hào)總長(zhǎng)度
print(signal_length)
if signal_length<=wlen: #若信號(hào)長(zhǎng)度小于一個(gè)幀的長(zhǎng)度,則幀數(shù)定義為1
nf=1
else: #否則,計(jì)算幀的總長(zhǎng)度
nf=int(np.ceil((1.0*signal_length-wlen+inc)/inc)) # nf 為幀數(shù)
# np.ceil向上取整,所以會(huì)導(dǎo)致實(shí)際分幀后的長(zhǎng)度大于信號(hào)本身的長(zhǎng)度,所以要對(duì)原來(lái)的信號(hào)進(jìn)行補(bǔ)零
pad_length=int((nf-1)*inc+wlen) #所有幀加起來(lái)總的鋪平后的長(zhǎng)度
zeros=np.zeros((pad_length-signal_length,)) #不夠的長(zhǎng)度使用0填補(bǔ),類(lèi)似于FFT中的擴(kuò)充數(shù)組操作
pad_signal=np.concatenate((wave_data,zeros)) #填補(bǔ)后的信號(hào)記為pad_signal
indices=np.tile(np.arange(0,wlen),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(wlen,1)).T #相當(dāng)于對(duì)所有幀的時(shí)間點(diǎn)進(jìn)行抽取,得到nf*wlen長(zhǎng)度的矩陣
indices=np.array(indices,dtype=np.int32) #將indices轉(zhuǎn)化為矩陣
frames=pad_signal[indices] #得到幀信號(hào),587*512的矩陣信號(hào)
#a=frames[30:31]
#print(frames.shape)
winfunc = signal.hamming(wlen) # 調(diào)用窗函數(shù),本初以漢明窗為例
#print(winfunc.shape)
win=np.tile(winfunc,(nf,1)) #窗函數(shù)為一維數(shù)組(512,),因此需要按照信號(hào)幀數(shù)進(jìn)行變換得到(587*512)矩陣信號(hào)
#print(win.shape)
my = frames*win # 這里的*指的是計(jì)算矩陣的數(shù)量積(即對(duì)位相乘)。
# python中矩陣運(yùn)算分為兩種形式,一是np.array,而是np.matrix
# ----------------------------------
# 繪制分幀加窗后的圖像(接上)
# 因?yàn)榉謳哟昂蟮男盘?hào)為587*512的矩陣信號(hào),為了繪圖,將其轉(zhuǎn)換為一維信號(hào)
t=my.flatten()
t=t.T
print(t.shape)
time = np.arange(0, len(t)) * (1.0 / framerate) # 調(diào)整時(shí)間軸
plt.figure()
plt.plot(time,t,c="g")
plt.grid()
plt.show()

6.語(yǔ)音的頻譜分析
6.1 頻譜圖
通過(guò)FFT對(duì)時(shí)域語(yǔ)音信號(hào)進(jìn)行處理,得到頻譜圖
import numpy as np
from scipy.io import wavfile
import matplotlib.pyplot as plt
%matplotlib inline
sampling_freq, audio = wavfile.read(r"C:\Users\zyf\Desktop\Jupyter\1.wav") # 讀取文件
audio = audio / np.max(audio) # 歸一化,標(biāo)準(zhǔn)化
# 應(yīng)用傅里葉變換
fft_signal = np.fft.fft(audio)
print(fft_signal)
fft_signal = abs(fft_signal)
print(fft_signal)
# 建立時(shí)間軸
Freq = np.arange(0, len(fft_signal))
# 繪制語(yǔ)音信號(hào)的
plt.figure()
plt.plot(Freq, fft_signal, color='blue')
plt.xlabel('Freq (in kHz)')
plt.ylabel('Amplitude')
plt.show()

6.2 語(yǔ)譜圖
語(yǔ)譜圖綜合了時(shí)域和頻域的特點(diǎn),明顯的顯示出來(lái)了語(yǔ)音頻率隨時(shí)間的變化情況**,語(yǔ)譜圖的橫軸為時(shí)間,縱軸為頻率任意給定頻率成分在給定時(shí)刻的強(qiáng)弱用顏色深淺表示。**顏色深表示頻譜值大,顏色淺表示頻譜值小,譜圖上不同的黑白程度形成不同的紋路,稱(chēng)為聲紋,不用講話(huà)者的聲紋是不一樣的,可以用做聲紋識(shí)別。
import wave
import matplotlib.pyplot as plt
import numpy as np
f = wave.open(r"C:\Users\zyf\Desktop\Jupyter\1.wav", "rb")
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
strData = f.readframes(nframes)#讀取音頻,字符串格式
waveData = np.fromstring(strData,dtype=np.int16)#將字符串轉(zhuǎn)化為int
waveData = waveData*1.0/(max(abs(waveData)))#wave幅值歸一化
waveData = np.reshape(waveData,[nframes,nchannels]).T
f.close()
plt.specgram(waveData[0],Fs = framerate, scale_by_freq = True, sides = 'default')
plt.ylabel('Frequency(Hz)')
plt.xlabel('Time(s)')
plt.colorbar()
plt.show()

參考博客:
- https://www.cnblogs.com/zhenmeili/p/14830176.html
- https://blog.csdn.net/sinat_18131557/article/details/105340416
- https://blog.csdn.net/weixin_38346042/article/details/119906391
- http://www.dhdzp.com/article/126984.htm
總結(jié)
到此這篇關(guān)于python語(yǔ)音信號(hào)處理的文章就介紹到這了,更多相關(guān)python語(yǔ)音信號(hào)處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
python中列表推導(dǎo)式與生成器表達(dá)式對(duì)比詳解
python當(dāng)然不是一門(mén)編譯型語(yǔ)言,但是它還是要被解析成二進(jìn)制的字節(jié)碼才能被執(zhí)行,執(zhí)行它的正是python解釋器,下面這篇文章主要給大家介紹了關(guān)于python中列表推導(dǎo)式與生成器表達(dá)式對(duì)比的相關(guān)資料,需要的朋友可以參考下2023-01-01
Github?Copilot結(jié)合python的使用方法詳解
最近也是聽(tīng)說(shuō)github出了一種最新的插件叫做copilot,于是申請(qǐng)了,下面這篇文章主要給大家介紹了關(guān)于Github?Copilot結(jié)合python使用的相關(guān)資料,需要的朋友可以參考下2022-04-04
詳解django的serializer序列化model幾種方法
序列化是將對(duì)象狀態(tài)轉(zhuǎn)換為可保持或傳輸?shù)母袷降倪^(guò)程。這篇文章主要介紹了詳解django的serializer序列化model幾種方法。具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-10-10
python服務(wù)器與android客戶(hù)端socket通信實(shí)例
這篇文章主要介紹了python服務(wù)器與android客戶(hù)端socket通信的實(shí)現(xiàn)方法,實(shí)例形式詳細(xì)講述了Python的服務(wù)器端實(shí)現(xiàn)原理與方法,以及對(duì)應(yīng)的Android客戶(hù)端實(shí)現(xiàn)方法,需要的朋友可以參考下2014-11-11
django的分頁(yè)器Paginator 從django中導(dǎo)入類(lèi)
這篇文章主要介紹了django的分頁(yè)器Paginator 從django中導(dǎo)入類(lèi),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-07-07
python?pygame實(shí)現(xiàn)五子棋雙人聯(lián)機(jī)
這篇文章主要為大家詳細(xì)介紹了python?pygame實(shí)現(xiàn)五子棋雙人聯(lián)機(jī),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-05-05
Python實(shí)現(xiàn)批量解壓文件夾下所有壓縮包
這篇文章主要為大家詳細(xì)介紹了如何使用Python實(shí)現(xiàn)批量解壓文件夾下所有壓縮包,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-02-02

