python對語音信號處理(常用操作)

一、語音信號讀取
1、語音信號三個重要的參數:聲道數、取樣頻率和量化位數。

  • 聲道數:單聲道、雙聲道
  • 取樣頻率:一秒鐘對聲音採樣的次數
  • 量化位數:用多少bit表達一次採樣所採集的數據,通常有8bit、16bit、24bit和32bit等幾種
    例:CD中所儲存的聲音信號是雙聲道、44.1kHz、16bit

2、 讀取.wav文件
scipy方法:

from scipy.io import wavfile
import numpy as np

sample_rate, sig = wavfile.read('new.wav')
#sample_rate 採樣率

pysoundfile方法:

import soundfile as sf

sig, sample_rate = sf.read('new.wav')

wave.open 用法:

wave.open(file,mode)
#mode可以是:
‘rb’,讀取文件;
‘wb’,寫入文件;
不支持同時讀/寫操作。

Wave_read.getparams用法:

f = wave.open(file,'rb')
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]  
#nchannels:聲道數 sampwidth:量化位數(byte) framerate:採樣頻率 nframes:採樣點數
  • 單通道
import wave
import matplotlib.pyplot as plt
import numpy as np
import os
 
filepath = "./data/" #添加路徑
filename= os.listdir(filepath) #得到文件夾下的所有文件名稱 
f = wave.open(filepath+filename[1],'rb')
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
strData = f.readframes(nframes)#讀取音頻,字符串格式
waveData = np.fromstring(strData,dtype=np.int16)#將字符串轉化爲int
waveData = waveData*1.0/(max(abs(waveData)))#wave幅值歸一化
# plot the wave
time = np.arange(0,nframes)*(1.0 / framerate)
plt.plot(time,waveData)
plt.xlabel("Time(s)")
plt.ylabel("Amplitude")
plt.title("Single channel wavedata")
plt.grid('on')#標尺,on:有,off:無。
  • 雙通道
# -*- coding: utf-8 -*-
import wave				            
import matplotlib.pyplot as plt		
import numpy as np 	            	

f = wave.open(r"./.wav", "rb")   #wave模塊讀取語音文件
params = f.getparams()			#一次性返回所有的音頻參數,返回的是一個元組(聲道數,量化位數(byte單位),採樣頻率,採樣點數,壓縮類型,壓縮類型的描述)
nchannels, sampwidth, framerate, nframes = params[:4] #賦值聲道數,量化位數,採樣頻率,採樣點數
print(nchannels)# 輸出聲道數
print(sampwidth)# 輸出量化位數
print(framerate)# 輸出採樣頻率
print(nframes)  # 輸出採樣點數

str_data = f.readframes(nframes)   #指定需要讀取的長度(以取樣點爲單位),返回的是字符串類型的數據
f.close()
wave_data = np.frombuffer(str_data, dtype=np.int16)  #將讀取的字符串數據轉換爲一維short類型的數組。通過np.fromstring函數將字符串轉換爲數組,通過其參數dtype指定轉換後的數據格式
wave_data = wave_data*1.0/(max(abs(wave_data)))      #將數組歸一化
wave_data = np.reshape(wave_data,[nframes,nchannels])

time = np.arange(0, nframes) * (1.0 / framerate)

#通過採樣點數和取樣頻率計算出每個取樣的時間
#畫出語音的波形
plt.figure(figsize=(10,9))  #設置窗口屬性
plt.subplot(211)
plt.plot(time, wave_data[:,0],c="g")
plt.xlabel("time (seconds)")
plt.ylabel("Amplitude")
plt.title("CH-1 wave_data")
plt.grid()
plt.subplot(212)
plt.plot(time, wave_data[:,1],c="g")
plt.xlabel("time (seconds)")
plt.ylabel("Amplitude")
plt.title("CH-2 wave_data")
plt.grid()
plt.show()

二、語音信號處理
1、預加重
語音信號的預加重,目的是爲了對語音的高頻部分進行加重,去除口脣輻射的影響,增加語音的高頻分辨率。一般通過傳遞函數爲
在這裏插入圖片描述
一階FIR高通數字濾波器來實現預加重,其中a爲預加重係數,0.9<a<1.0。設n時刻的語音採樣值爲x(n),經過預加重處理後的結果爲y(n))=x(n)-ax(n-1),這裏取a=0.97。

pre_emphasis = 0.98
emphasized_signal = np.append(audio[0], audio[1:] - pre_emphasis * audio[:-1])
plt.plot(np.arange(emphasized_signal.shape[0]),emphasized_signal)
plt.show()

2、分幀
分幀是將不定長的音頻切分成固定長度的小段。爲了避免窗邊界對信號的遺漏,因此對幀做偏移時候,幀間要有幀移(幀與幀之間需要重疊一部分),幀長(wlen) = 重疊(overlap)+幀移(inc)。inc爲幀移,表示後一幀第前一幀的偏移量,fs表示採樣率,fn表示一段語音信號的分幀數,N爲語音數據長度
wlen爲幀長,重疊部分爲overlap,overlap=wlen - inc

fn=(N−overlap)/inc=(N−wlen)/inc+1

通常的選擇是幀長25ms,幀移爲10ms。接下來的操作是對單幀進行的。要分幀是因爲語音信號是快速變化的,而傅里葉變換適用於分析平穩的信號。幀和幀之間的時間差常常取爲10ms,這樣幀與幀之間會有重疊,否則,由於幀與幀連接處的信號會因爲加窗而被弱化,這部分的信息就丟失了。

#分幀函數
def enframe(signal, nw, inc, windown):
    '''將音頻信號轉化爲幀。
    signal:原始音頻型號
    nw:每一幀的長度(這裏指採樣點的長度,即採樣頻率乘以時間間隔)
    inc:相鄰幀的間隔
    '''
    signal_length=len(signal) #信號總長度
    if signal_length<=nw: #若信號長度小於一個幀的長度,則幀數定義爲1
        nf=1
    else: #否則,計算幀的總長度
        nf=int(np.ceil((1.0*signal_length-nw+inc)/inc))
        
    pad_length=int((nf-1)*inc+nw) #所有幀加起來總的鋪平後的長度
    zeros=np.zeros((pad_length-signal_length,)) #不夠的長度使用0填補,類似於FFT中的擴充數組操作
    pad_signal=np.concatenate((signal,zeros)) #填補後的信號記爲pad_signal
    indices=np.tile(np.arange(0,nw),(nf,1))+np.tile(np.arange(0,nf*inc,inc),(nw,1)).T  #相當於對所有幀的時間點進行抽取,得到nf*nw長度的矩陣
    indices=np.array(indices,dtype=np.int32) #將indices轉化爲矩陣
    frames=pad_signal[indices] #得到幀信號
    win=np.tile(windown,(nf,1))  #window窗函數,這裏默認取1
    return frames*win   #返回幀信號矩陣

3、加窗
通常對信號截斷、分幀需要加窗,因爲截斷都有頻域能量泄露,而窗函數可以減少截斷帶來的影響。窗函數在scipy.signal信號處理工具箱中,如hanning窗:

import matplotlib.pyplot as plt
import scipy.signal as signal
plt.figure(figsize=(6,2))
plt.plot(signal.hanning(512))
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章