分析語音數據增強及python實現
一、概述
音頻時域波形具有以下特征:音調,響度,質量。我們在進行數據增強時,最好隻做一些小改動,使得增強數據和源數據存在較小差異即可,切記不能改變原有數據的結構,不然將產生“臟數據”,通過對音頻數據進行數據增強,能有助於我們的模型避免過度擬合並變得更加通用。
我發現對聲波的以下改變是有用的:Noise addition(增加噪音)、Add reverb(增加混響)、Time shifting(時移)、Pitch shifting(改變音調)和Time stretching(時間拉伸)。
本章需要使用的python庫:
- matplotlib:繪制圖像
- librosa:音頻數據處理
- numpy:矩陣數據處理
使用先畫出原始語音數據的語譜圖和波形圖
import librosa import numpy as np import matplotlib.pyplot as plt plt.rcParams['font.sans-serif'] = ['SimHei'] # 用來正常顯示中文標簽 plt.rcParams['axes.unicode_minus'] = False # 用來正常顯示符號 fs = 16000 wav_data, _ = librosa.load("./p225_001.wav", sr=fs, mono=True) # ########### 畫圖 plt.subplot(2, 2, 1) plt.title("語譜圖", fontsize=15) plt.specgram(wav_data, Fs=16000, scale_by_freq=True, sides='default', cmap="jet") plt.xlabel('秒/s', fontsize=15) plt.ylabel('頻率/Hz', fontsize=15) plt.subplot(2, 2, 2) plt.title("波形圖", fontsize=15) time = np.arange(0, len(wav_data)) * (1.0 / fs) plt.plot(time, wav_data) plt.xlabel('秒/s', fontsize=15) plt.ylabel('振幅', fontsize=15) plt.tight_layout() plt.show()
二、加噪
添加的噪聲為均值為0,標準差為1的高斯白噪聲,有兩種方法對數據進行加噪
2.1、第一種:控制噪聲因子
def add_noise1(x, w=0.004): # w:噪聲因子 output = x + w * np.random.normal(loc=0, scale=1, size=len(x)) return output Augmentation = add_noise1(x=wav_data, w=0.004)
2.2、第二種:控制信噪比
通過信噪比的公式推導出噪聲。
def add_noise2(x, snr): # snr:生成的語音信噪比 P_signal = np.sum(abs(x) ** 2) / len(x) # 信號功率 P_noise = P_signal / 10 ** (snr / 10.0) # 噪聲功率 return x + np.random.randn(len(x)) * np.sqrt(P_noise) Augmentation = add_noise2(x=wav_data, snr=50)
三、加混響
我這裡使用的是Image Source Method(鏡像源方法)來實現語音加混響,我想用兩種方法來給大傢實現,第一種是直接調用python庫——Pyroomacoustics來實現音頻加混響,第二種就是按照公式推導一步一步來實現,兩種效果一樣,想看細節的可以參考第二種方法,隻想開始實現效果的可以隻看第一種方法:
3.1、方法一:Pyroomacoustics實現音頻加混響
首先需要安裝Pyroomacoustics,這個庫非常強大,感興趣也可以多看看其他API接口
pip install Pyroomacoustics
步驟:
1.創建房間(定義房間大小、所需的混響時間、墻面材料、允許的最大反射次數、)
2.在房間內創建信號源
3.在房間內放置麥克風
4.創建房間沖擊響應
5.模擬聲音傳播
# -*- coding:utf-8 -*- import pyroomacoustics as pra import numpy as np import matplotlib.pyplot as plt import librosa # 1、創建房間 # 所需的混響時間和房間的尺寸 rt60_tgt = 0.5 # 所需的混響時間,秒 room_dim = [9, 7.5, 3.5] # 我們定義瞭一個9m x 7.5m x 3.5m的房間,米 # 我們可以使用Sabine's公式來計算壁面能量吸收和達到預期混響時間所需的ISM的最大階數(RT60,即RIR衰減60分貝所需的時間) e_absorption, max_order = pra.inverse_sabine(rt60_tgt, room_dim) # 返回 墻壁吸收的能量 和 允許的反射次數 # 我們還可以自定義 墻壁材料 和 最大反射次數 # m = pra.Material(energy_absorption="hard_surface") # 定義 墻的材料,我們還可以定義不同墻面的的材料 # max_order = 3 room = pra.ShoeBox(room_dim, fs=16000, materials=pra.Material(e_absorption), max_order=max_order) # 在房間內創建一個位於[2.5,3.73,1.76]的源,從0.3秒開始向仿真中發出wav文件的內容 audio, _ = librosa.load("speech.wav",sr=16000) # 導入一個單通道語音作為源信號 source signal room.add_source([2.5, 3.73, 1.76], signal=audio, delay=0.3) # 3、在房間放置麥克風 # 定義麥克風的位置:(ndim, nmics) 即每個列包含一個麥克風的坐標 # 在這裡我們創建一個帶有兩個麥克風的數組, # 分別位於[6.3,4.87,1.2]和[6.3,4.93,1.2]。 mic_locs = np.c_[ [6.3, 4.87, 1.2], # mic 1 [6.3, 4.93, 1.2], # mic 2 ] room.add_microphone_array(mic_locs) # 最後將麥克風陣列放在房間裡 # 4、創建房間沖擊響應(Room Impulse Response) room.compute_rir() # 5、模擬聲音傳播,每個源的信號將與相應的房間脈沖響應進行卷積。卷積的輸出將在麥克風上求和。 room.simulate() # 保存所有的信號到wav文件 room.mic_array.to_wav("./guitar_16k_reverb_ISM.wav", norm=True, bitdepth=np.float32,) # 測量混響時間 rt60 = room.measure_rt60() print("The desired RT60 was {}".format(rt60_tgt)) print("The measured RT60 is {}".format(rt60[1, 0])) plt.figure() # 繪制其中一個RIR. both can also be plotted using room.plot_rir() rir_1_0 = room.rir[1][0] # 畫出 mic 1和 source 0 之間的 RIR plt.subplot(2, 1, 1) plt.plot(np.arange(len(rir_1_0)) / room.fs, rir_1_0) plt.title("The RIR from source 0 to mic 1") plt.xlabel("Time [s]") # 繪制 microphone 1 處接收到的信號 plt.subplot(2, 1, 2) plt.plot(np.arange(len(room.mic_array.signals[1, :])) / room.fs, room.mic_array.signals[1, :]) plt.title("Microphone 1 signal") plt.xlabel("Time [s]") plt.tight_layout() plt.show()
room = pra.ShoeBox( room_dim, fs=16000, materials=pra.Material(e_absorption), max_order=3, ray_tracing=True, air_absorption=True, ) # 激活射線追蹤 room.set_ray_tracing()
room.simulate(reference_mic=0, snr=10) # 控制信噪比
3.2、方法二:Image Source Method 算法講解
從這裡要講算法和原理瞭,
代碼參考:matlab版本:RIR-Generator,python版本:rir-generator
鏡像源法簡介:
將反射面等效為一個虛像,或者說鏡像。比如說,在一個開放空間裡有一面平整墻面,那麼一個聲源可以等效為2兩個聲源;一個開放空間裡有兩面垂直的平整墻面,那麼一個聲源可以等效為4個;同理三面的話是8個。原理上就是這樣,但是封閉的三維空間裡情況有那麼點復雜,
一般來說,傢裡的空房間可以一定程度上近似為矩形盒子,假設房間尺寸為:
元素大小分別代表長寬高,而聲源的三維坐標為
麥克風的三維坐標為
鏡像聲源$(i,j,k)$到麥克風距離在三個坐標軸上的位置為
那麼聲源$(i,j,k)$距離麥克風的距離為
相對於直達聲的到達延遲時間為
其中$c$為聲速,$r$為聲源到麥克風的直線距離。那麼,混響效果等效為不同延遲的信號的疊加,即混響效果可以表示為一個FIR濾波器與信號源卷積的形式,此濾波器可寫為如下形式
濾波器的抽頭系數與鏡面的反射系數與距離相關,如果每個面的反射系數不同則形式略復雜。詳細代碼還是要看RIR-Generator,我這裡隻做拋轉引玉,寫一個最簡單的。
模擬鏡像源:
房間尺寸(m):4 X 4 X 3
聲源坐標(m):2 X 2 X 0
麥克風坐標(m):2 X 2 X 1.5
混響時間(s):0.2
RIR長度:512
clc;clear; c = 340; % 聲速 (m/s) fs = 16000; % Sample frequency (samples/s) r = [2 2 1.5]; % 麥克風位置 [x y z] (m) s = [2 2 0]; % 揚聲器位置 [x y z] (m) L = [4 4 3]; % 房間大小 [x y z] (m) beta = 0.2; % 混響時間 (s) n = 512; % RIR長度 h = rir_generator(c, fs, r, s, L, beta, n); disp(size(h)) % (1,4096) [speech, fs] = audioread("./test_wav/p225_001.wav"); disp(size(speech)); % (46797,1) y = conv(speech', h); disp(length(y)) % 開始畫圖 figure('color','w'); % 背景色設置成白色 subplot(3,1,1) plot(h) title("房間沖擊響應 RIR","FontSize",14) subplot(3,2,3) plot(speech) title("原語音波形","FontSize",14) subplot(3,2,4) plot(y) title("加混響語音波形","FontSize",14) subplot(3,2,5) specgram(speech,512,fs,512,256); title("原語音頻譜","FontSize",14) subplot(3,2,6) specgram(y,512,fs,512,256); title("加混響語音頻譜","FontSize",14) audiowrite("./test_wav/matlab_p225_001_reverber.wav",y,fs)
四、生成指定SER的混響
SER的公式為
其中E是統計 期望操作,$s(n)$是近端語音,$d(n)$是遠端回聲,
由於我們需要根據指定的SER求混響信號,並且近端語音和遠端混響都是已知的,我們隻需要求得一個系數,來調整回聲信號的能量大小,與遠端混響相乘即可得我們想要的混響語音,即調整後的回聲信號為$kd(n)$
根據以上公式,可以推導出$k$的值
最終$kd(n)$即我們所求的指定SER的混響。
def add_echo_ser(near_speech, far_echo, SER): """根據指定的SER求回聲 :param near_speech: 近端語音 :param far_echo: 遠端回聲 :param SER: 指定的SER :return: 指定SER的回聲 """ p_near_speech = np.mean(near_speech ** 2) # 近端語音功率 p_far_echo = np.mean(far_echo ** 2) # 遠端回聲功率 k = np.sqrt(p_near_speech / (10 ** (SER / 10)) / p_far_echo) return k * far_echo
五、波形位移
語音波形移動使用numpy.roll函數向右移動shift距離
numpy.roll(a,shift,axis=None)
參數:
- a:數組
- shift:滾動的長度
- axis:滾動的維度。0為垂直滾動,1為水平滾動,參數為None時,會先將數組扁平化,進行滾動操作後,恢復原始形狀
x = np.arange(10) # array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) print(np.roll(x, 2)) # array([8, 9, 0, 1, 2, 3, 4, 5, 6, 7])
波形位移函數:
def time_shift(x, shift): # shift:移動的長度 return np.roll(x, int(shift)) Augmentation = time_shift(wav_data, shift=fs//2)
六、波形拉伸
在不影響音高的情況下改變聲音的速度 / 持續時間。這可以使用librosa的time_stretch函數來實現。
def time_stretch(x, rate): # rate:拉伸的尺寸, # rate > 1 加快速度 # rate < 1 放慢速度 return librosa.effects.time_stretch(x, rate) Augmentation = time_stretch(wav_data, rate=2)
七、音高修正(Pitch Shifting)
音高修正隻改變音高而不影響音速,我發現-5到5之間的步數更合適
def pitch_shifting(x, sr, n_steps, bins_per_octave=12): # sr: 音頻采樣率 # n_steps: 要移動多少步 # bins_per_octave: 每個八度音階(半音)多少步 return librosa.effects.pitch_shift(x, sr, n_steps, bins_per_octave=bins_per_octave) # 向上移三音(如果bins_per_octave為12,則六步) Augmentation = pitch_shifting(wav_data, sr=fs, n_steps=6, bins_per_octave=12) # 向上移三音(如果bins_per_octave為24,則3步) Augmentation = pitch_shifting(wav_data, sr=fs, n_steps=3, bins_per_octave=24) # 向下移三音(如果bins_per_octave為12,則六步) Augmentation = pitch_shifting(wav_data, sr=fs, n_steps=-6, bins_per_octave=12)
還有寫沒有跑通,但是總感覺有些價值的代碼,記錄在這裡:
py-RIR-Generator(沒跑通的原因是我是window系統)gpuRIR(這個我跑通瞭,但是需要較大的計算資源)去github找代碼的時候,不一定要搜索“回聲”,“混響”,也可以通過搜索”RIR”同樣可以得到想要的結果
本文畫圖代碼:
# Author:凌逆戰 # -*- coding:utf-8 -*- import matplotlib.pyplot as plt import librosa import numpy as np plt.rcParams['font.sans-serif']=['SimHei'] #用來正常顯示中文標簽 plt.rcParams['axes.unicode_minus']=False #用來正常顯示符號 y1, _ = librosa.load("./speech.wav", sr=16000) y2, _ = librosa.load("./guitar_16k_reverb_ISM.wav", sr=16000) plt.subplot(2, 2, 1) plt.specgram(y1, Fs=16000, scale_by_freq=True, sides='default', cmap="jet") plt.title("語譜圖", fontsize=13) plt.xlabel('時間/s', fontsize=13) plt.ylabel('頻率/Hz', fontsize=13) plt.subplot(2, 2, 2) plt.plot(np.arange(len(y1)) / 16000, y1) plt.title("波形圖", fontsize=13) plt.xlabel('時間/s', fontsize=13) plt.ylabel('振幅', fontsize=13) plt.subplot(2, 2, 3) plt.specgram(y2, Fs=16000, scale_by_freq=True, sides='default', cmap="jet") plt.title("語譜圖(加混響)", fontsize=13) plt.xlabel('時間/s', fontsize=13) plt.ylabel('頻率/Hz', fontsize=13) plt.subplot(2, 2, 4) plt.plot(np.arange(len(y2)) / 16000, y2) plt.title("波形圖(加混響)", fontsize=13) plt.xlabel('時間/s', fontsize=13) plt.ylabel('振幅', fontsize=13) plt.tight_layout() plt.show()
以上就是分析語音數據增強及python實現的詳細內容,更多關於語音數據增強 python實現的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- python實現語音常用度量方法的代碼詳解
- python使用Matplotlib繪制多種常見圖形
- Python Matplotlib數據可視化模塊使用詳解
- Python利用 matplotlib 繪制直方圖
- Python Matplotlib繪圖基礎詳細教程