苹果ios啸叫频点测试软件_啸叫抑制之陷波法
1. 嘯叫的產(chǎn)生
1.1 嘯叫產(chǎn)生的原理
嘯叫常見于擴音系統(tǒng),比如多媒體會議廳、多媒體教室。 當(dāng)麥克風(fēng)和揚聲器在同一個會場時,聲音從揚聲器擴音后又從被麥克風(fēng)拾取,形成了聲音反饋回路。當(dāng)擴音的增益足夠大,在某些頻率就會產(chǎn)生自激振蕩,形成刺耳的嘯叫, 那就無法正常講話了。
在擴音系統(tǒng)中使用硬件或者軟件方法去除這種嘯叫,就是聲反饋控制(Acoustic Feedback Control),或者叫嘯叫抑制(Howling Suppression)。
聲反饋系統(tǒng)框圖
揚聲器輸出信號與麥克風(fēng)輸入信號之間的頻率響應(yīng)為:
根據(jù)奈奎斯特穩(wěn)定判據(jù),在某些頻點,增益和相位滿足以下條件,將產(chǎn)生自激振蕩:
自激振蕩導(dǎo)致了系統(tǒng)的不穩(wěn)定,信號幅度不斷增大,最終形成刺耳的嘯叫。在頻譜上看到一條連續(xù)的譜峰。
除了擴音系統(tǒng),平常使用手機/電腦進行音視頻通話,如果兩個終端在同一個房間的話,也是可能產(chǎn)生嘯叫的。 一般的商用軟件和開源算法認為,通常通話的參與者不在一個房間的,所以很少遇到嘯叫的問題,只需要解決噪聲抑制和回聲消除問題就可以了, 比如SpeexDsp和WebRTC。 但是從我個人經(jīng)驗而言,有兩種情況是需要嘯叫抑制的:
-在實驗室做多終端通話測試
因資源限制,幾臺終端可能放在一個房間里面測試通話情況,這樣一旦有人開始講話,一臺終端拾音后從另外一臺終端上外放出來。 這樣就形成了反饋回路,常常產(chǎn)生嘯叫,測試就無法進行了。
-視頻會議時多終端在同一個房間
開視頻會議時,有部分參與者是同一個單位的,可能會在一個房間里面參加會議,如果房間里面一個人講話,從同一個房間里面其他終端外放出來,就會產(chǎn)生嘯叫。 這個情況如果讓房間里面其他終端靜音,是可以解決嘯叫問題的。但是根據(jù)往常參加視頻會議的經(jīng)驗,一般用戶并不一定知道怎么操作。
Skype、微信、WebRTC之類應(yīng)用,應(yīng)該都沒有對上面兩種情況作優(yōu)化。如果可以增加嘯叫抑制的算法,語音通話或者視頻會議遇到異常的機會就可以減少。
1.2 嘯叫仿真
#simulate acoustic feekback, point-by-point
# _______________
# clean speech: x --> mic: x1 --> | Internal Gain | --> x2 -- > speaker : y
# ^ |______G________| |
# | |
# | _______________ |
#
# |____Response___|
#
N = min(2000, len(rir)) #limit room impulse response length
x2 = np.zeros(N) #buffer N samples of speaker output to generate acoustic feedback
y = np.zeros(len(x)) #save speaker output to y
y1 = 0.0 #init as 0
for i in range(len(x)):
x1 = x[i] + y1
y[i] = G*x1
y[i] = min(2, y[i]) #amplitude clipping
y[i] = max(-2, y[i])
x2[1:] = x2[:N-1]
x2[0] = y[i]
y1 = np.dot(x2, rir[:N])
下面將一段干凈語音經(jīng)過反饋路徑仿真,產(chǎn)生嘯叫。反饋路徑RIR直接使用參考資料[2]的path數(shù)據(jù)。簡書非會員無法插入音頻,音頻可以上GibHub下載。下面分別是干凈語音和嘯叫語音的語譜圖。
干凈語音波形圖和語譜圖
加嘯叫后的音頻,幅度已飽和,無法分辨原本語音,語譜圖可以看見多個嘯叫峰值。
嘯叫語音波形圖和語譜圖。
2. 嘯叫抑制的方法
嘯叫抑制的方法,主要有三種:頻移/相移法、陷波法、和自適應(yīng)方法。本文只實現(xiàn)第二種,陷波濾波器法。
顧名思義,陷波法就是要在聲反饋系統(tǒng)的極點頻率插入一個陷波濾波器,抑制極點的增益,使之無法達到嘯叫的增益條件。因此陷波法需要分成兩步:第一步,嘯叫檢測,將產(chǎn)生嘯叫的頻率找出來; 第二步,嘯叫抑制,在找出來的嘯叫頻率設(shè)計陷波濾波器,并對麥克風(fēng)信號進行濾波。
陷波法嘯叫抑制框圖
從前面嘯叫語音的語譜圖手動選擇兩個最大峰值,分別是603Hz和1745Hz (大概值,不是特別精確)。并且使用Python生成IIR Notch Filter 的系數(shù)b和a。
#notch filter
fs = Srate # Sample frequency (Hz)
f0 = 603 # Frequency to be removed from signal (Hz)
Q = 1 # Quality factor
# Design notch filter
b1, a1 = signal.iirnotch(f0, Q, fs)
sos1 = np.append(b1,a1)
#plot_notch_filter(b1, a1, fs)
f0 = 1745 # Frequency to be removed from signal (Hz)
Q = 5 # Quality factor
# Design notch filter
b2, a2 = signal.iirnotch(f0, Q, fs)
sos2 = np.append(b2,a2)
#plot_notch_filter(b2, a2, fs)
sos = np.vstack((sos1,sos2))
b, a = signal.sos2tf(sos)
plot_notch_filter(b, a, fs)
選兩個嘯叫頻點的陷器頻率響應(yīng)
使用固定系數(shù)的陷波濾波器,插入到前面的聲反饋系統(tǒng)中進行仿真。
#==============================Notch Filtering ==================================
# _______________ ______________
#clean speech: x --> mic: x1 --> | Internal Gain |-x2--> | Notch Filter |--> speaker: y
# ^ |______G________| |_____IIR______| |
# | |
# | _______________ |
#
# |____Response___|
#
N = min(2000, len(rir)) #limit room impulse response length
x2 = np.zeros(len(b)) #
x3 = np.zeros(N) #buffer N samples of speaker output to generate acoustic feedback
y = np.zeros(len(x)) #save speaker output to y
y1 = 0.0 #init as 0
for i in range(len(x)):
x1 = x[i] + y1
x2[1:] = x2[:len(x2)-1]
x2[0] = G*x1
x2[0] = min(1, x2[0]) #amplitude clipping
x2[0] = max(-1, x2[0])
y[i] = np.dot(x2, b) - np.dot(x3[:len(a)-1], a[1:]) #IIR filter
x3[1:] = x3[:N-1]
x3[0] = y[i]
y1 = np.dot(x3, rir[:N])
插入陷波濾波器后的揚聲器輸出信號頻譜明顯看見在603Hz和1745Hz 被抑制了,整體語音的譜圖重現(xiàn),可以聽到清楚的語音信號 (除了陷波頻率附近頻率有損)。
固定系數(shù)陷波濾波器進行嘯叫抑制
2.1 嘯叫檢測
實際系統(tǒng)中,無法實現(xiàn)確定嘯叫頻率,所以需要出入嘯叫檢測。嘯叫頻點的檢測,必須結(jié)合嘯叫的時域和頻域特征來進行分析。
頻域上,嘯叫頻點功率很高,是一個峰值,遠超其他語音或噪聲頻率的功率。
時域上,嘯叫頻點的功率有一個迅速增大的過程,達到飽和幅度后一直保持。
根據(jù)嘯叫的時頻特征,參考資料[1]總結(jié)了以下檢測特征。
2.1.1 峰值閾值功率比(Peak-to-Threshold Power Raio, PTPR)
嘯叫的功率遠大于正常播放的音頻。故設(shè)定一個閾值,只有功率超過閾值的頻點,才會進行嘯叫檢測,減少無意義的檢測判決。
2.1.2 峰值均值功率比(Peak-to-Average Power Raio, PAPR)
產(chǎn)生嘯叫的頻點功率遠大于其他頻點的功率,故可以先計算出整個頻譜的平均功率,然后計算每個頻點功率與平均功率之比。比值大于預(yù)設(shè)閾值的頻點,記為候選嘯叫頻率。
2.1.3 峰值鄰近功率比(Peak-to-Neighboring Power Raio, PNPR)
PNPR尋找功率譜的峰值點,加入候選嘯叫頻率。可以選取左右各M個相鄰頻點進行比較,當(dāng)前頻點功率比鄰值都高時,記為候選嘯叫頻率。M選取5點左右
2.1.4 峰值諧波功率比(Peak-to-Harmonics Power Raio, PHPR)
語音譜有諧波峰,而嘯叫頻率是不含諧波峰的,故可以根據(jù)一個峰值點的諧波頻率功率是不是也很大,來判斷該峰值是否嘯叫點。
2.1.5 幀間峰值保持度(Interframe Peak Magnitude Persistence, IPMP)
IPMP是時域特征,如果一個頻點,連續(xù)幾幀都是檢測出來的候選嘯叫峰值,那就認為這個點確實發(fā)生了嘯叫。實現(xiàn)時可以選定5幀,超過3幀是候選嘯叫頻點的位置,判定為嘯叫點。
2.1.6 幀間幅度斜率偏差度(Interframe Magnitude Slope Deviation, IMSD)
IMSD也是時域特征,是從嘯叫開始發(fā)生時判斷,這是嘯叫頻點幅度線性增長,那么幀間斜率就會保持不變。取
幀進行區(qū)間觀察,計算
幀平均斜率,與區(qū)間內(nèi)更短區(qū)間的斜率之間的差值,如果差值在設(shè)定閾值以下,就認為該區(qū)間斜率保持不變,可能是發(fā)生了嘯叫。
image.png
頻域特征PTPR PAPR PNPR PHPR都是對一幀內(nèi)頻點進行分析,而時域特征是對多幀間的特征進行分析。所以在進行判決時,一般先對每幀頻譜進行頻域特征分析,然后對累計的時域特許證進行分析。
為了不影響原音頻的頻譜、以及限制濾波器計算量考慮,最后還需要限制嘯叫頻點的數(shù)量。一般系統(tǒng)可以選擇五六個頻點,簡單的系統(tǒng)也可以嘗試只選擇嘯叫程度最嚴重的一個或者兩個頻點。
參考資料[1]中的嘯叫檢測流程圖
下面進行仿真,暫時只把PTPR, PAPR, PNPR IPMP考慮進去。并且做一個Screening, 把離得太近的候選頻點去除。
def howling_detect(frame, win, nFFT, Slen, candidates, frame_id):
insign = win * frame
spec = np.fft.fft(insign, nFFT, axis=0)
#========== Howling Detection Stage =====================#
ptpr_idx = pyHowling.ptpr(spec[:Slen], 10)
papr_idx, papr = pyHowling.papr(spec[:Slen], 10)
pnpr_idx = pyHowling.pnpr(spec[:Slen], 15)
intersec_idx = np.intersect1d(ptpr_idx, np.intersect1d(papr_idx,pnpr_idx))
for idx in intersec_idx:
candidates[idx][frame_id] = 1
ipmp = pyHowling.ipmp(candidates, frame_id)
result = pyHowling.screening(spec, ipmp)
return result
2.2 陷波法抑制嘯叫
將嘯叫檢測和陷波濾波器都插入到聲反饋系統(tǒng)中,動態(tài)監(jiān)測嘯叫頻率,進行仿真。
#=============================Notch Filtering =======================================================
# ___________________
# -------> | Howling Detection | ______
# | |___________________| |
# | |
# | _______________ _______V______
#clean speech: x --> mic: x1 --> | Internal Gain |-x2--> | Notch Filter | -->speaker:y
# ^ |______G________| |_____IIR______| |
# | |
# | _______________ |
#
# |____Response___|
#
b = [1.0, 0 ,0]
a = [0, 0, 0]
N = min(2000, len(rir)) #limit room impulse response length
x2 = np.zeros(100) #
x3 = np.zeros(N) #buffer N samples of speaker output to generate acoustic feedback
y = np.zeros(len(x)) #save speaker output to y
y1 = 0.0 #init as 0
current_frame = np.zeros(Slen)
pos = 0
candidates = np.zeros([Slen, Nframes+1], dtype='int')
frame_id = 0
notch_freqs = []
for i in range(len(x)):
x1 = x[i] + y1
current_frame[pos] = x1
pos = pos + 1
if pos==Slen:
#update notch filter frame by frame
freq_ids = howling_detect(current_frame, win, nFFT, Slen, candidates, frame_id)
#freq_ids = [46]
if(len(freq_ids)>0 and (len(freq_ids)!=len(notch_freqs) or not np.all(np.equal(notch_freqs, freqs[freq_ids])))):
notch_freqs = freqs[freq_ids]
sos = np.zeros([len(notch_freqs), 6])
for i in range(len(notch_freqs)):
b0, a0 = signal.iirnotch(notch_freqs[i], 1, Srate)
sos[i,:] = np.append(b0,a0)
b, a = signal.sos2tf(sos)
print("frame id: ", frame_id, "/", Nframes, "notch freqs:", notch_freqs)
current_frame[:Slen-len2] = current_frame[len2:] #shift by len2
pos = len2
frame_id = frame_id + 1
x2[1:] = x2[:len(x2)-1]
x2[0] = G*x1
x2[0] = min(2, x2[0]) #amplitude clipping
x2[0] = max(-2, x2[0])
y[i] = np.dot(x2[:len(b)], b) - np.dot(x3[:len(a)-1], a[1:]) #IIR filter
y[i] = min(2, y[i]) #amplitude clipping
y[i] = max(-2, y[i])
x3[1:] = x3[:N-1]
x3[0] = y[i]
y1 = np.dot(x3, rir[:N])
從結(jié)果上看,除了初始階段產(chǎn)生了嘯叫,后面基本抑制住了,可以聽到語音信號。但是有斷斷續(xù)續(xù)的低幅度嘯叫產(chǎn)生。IPMP等需要嘯叫達到一定程度才能檢測出來,如果換用IMSD,可能可以更快地動態(tài)抑制嘯叫。
加入嘯叫檢測和陷波濾波器的聲反饋系統(tǒng)輸出
參考資料
[1] T. van Waterschoot and M. Moonen, "Fifty Years of Acoustic Feedback Control: State of the Art and Future Challenges," in Proceedings of the IEEE, vol. 99, no. 2, pp. 288-327, Feb. 2011, doi: 10.1109/JPROC.2010.2090998.
總結(jié)
以上是生活随笔為你收集整理的苹果ios啸叫频点测试软件_啸叫抑制之陷波法的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 3.2-点云配准原理概述
- 下一篇: 医疗图像配准-点云配准总结