主頁 >  其他 > 聲學回聲消除(Acoustic Echo Cancellation)原理與實作

聲學回聲消除(Acoustic Echo Cancellation)原理與實作

2020-09-17 18:16:55 其他

文章作者:凌逆戰

文章地址:https://www.cnblogs.com/LXP-Never/p/11703440.html


   回聲就是聲音信號經過一系列反射之后,又聽到了自己講話的聲音,這就是回聲,一些回聲是必要的,比如劇院里的音樂回聲以及延遲時間較短的房間回聲;而大多數回聲會造成負面影響,比如在有線或者無線通信時重復聽到自己講話的聲音(回想那些年我們開黑打游戲時,如果其中有個人開了外放,他的聲音就會回蕩來回蕩去),因此消除回聲的負面影響對通信系統是十分必要的,

  針對回聲消除(Acoustic Echo Cancellation,AEC )問題,現如今最流行的演算法就是基于自適應濾波的回聲消除演算法,本文從回聲信號的兩種分類以及 AEC 的基本原理出發,介紹幾種經典的 AEC 演算法并對其性能進行闡釋,

回聲分類

  在通信系統中,回聲主要分為兩類:電路回聲聲學回聲

電路回聲

  電路回聲通常產生于有線通話中,為了降低電話中心局與電話用戶之間電話線的價格,用戶間線的連接采用兩線制;而電話中心局之間連接采用四線制(上面兩條線路用于發送給用戶端信號,下面兩條線路用于接收用戶端信號),問題就出來了,造成電路回聲的根本原因是轉換混合器的二線-四線阻抗不能完全匹配(使用的不同型號的電線或者負載線圈沒有被使用的原因),導致混合器 接收線路 上的語音信號流失到了 發送線路 ,產生了回聲信號,使得另一端的用戶在接收信號的同時聽到了自己的聲音,

電路回聲產生原理

  在現如今的數字通信網路中,轉換混合器與數模轉換器融為一體,但無論是模擬電子線路還是數字電子線路,二-四線的轉換都會造成阻抗不匹配問題,從而導致其產生電路回聲,影響現代通信質量,由于電路回聲的線性以及穩定性,用一個簡單的線性疊加器就可以實作電路回聲消除,首先將產生的回聲信號在數值上取反,線性地疊加在回聲信號上,將產生的回聲信號抵消,實作電路回聲的初步消除,然而由于技術缺陷,線性疊加器不能完整地將回聲信號抹去,因此需要添加一個非線性處理器,其實質是一個阻擋信號的開關,將殘余的回聲信號經過非線性處理之后,就可以實作電路回聲的消除,或者得到噪聲很小的靜音信號,由于電路回聲信號是線性且穩定的,所以比較容易將其消除,而本文主要研究的是如何消除非線性的聲學回聲,

電路回聲消除的基本原理

聲學回聲

  在麥克風與揚聲器互相作用影響的雙工通信系統中極易產生聲學回聲,如下圖所示

遠端講話者A-->麥克風A-->電話A-->電話B---->揚聲器B--->麥克風B-->電話B-->電話A-->揚聲器A--->麥克風A--->.........就這樣無限回圈,

詳細講解:遠端講話者A的話語被麥克風采集并傳入至通信設備,經過無線或有線傳輸之后達到近端的通信設備,并通過近端 B 的揚聲器播放,這個聲音又會被近端 B 的麥克風拾取至其通信設備形成聲學回聲,經傳輸又回傳了遠端 A 的通信設備,并通過遠端 A 的揚聲器播放出來,從而遠端講話者就聽到了自己的回聲,

聲學回聲產生原理

近端(near-end) 遠端(far-end)
近端麥克風 遠端麥克風
近端揚聲器 遠端揚聲器

近端語音信號:說話人說話 被近端麥克風采集的信號

遠端語音信號:遠端的語音信號通過近端揚聲器播放,與房間的回聲路徑卷積后 被近端麥克風采集到的語音信號

近端混合回聲信號:整個對話程序中,近端麥克風接收到的信號有近端說話人的語音信號遠端說話人通過揚聲器播放的語音信號,這樣疊加的語音信號通過傳輸線路傳到遠端揚聲器播放導致遠端人聽到自己剛剛檢測出的語音信號,即所謂的回聲,

聲學回聲信號根據傳輸途徑的差別可以分別直接回聲信號和間接回聲信號,

直接回聲:近端揚聲器B將語音信號播放出來后,近端麥克風B直接將其采集后得到的回聲,

  直接回聲不受環境的印象,與揚聲器到麥克風的距離及位置有很大的關系,因此直接回聲是一種線性信號

間接回聲:近端揚聲器B將語音信號播放出來后,語音信號經過復雜多變的墻面反射后由近端麥克風B將其拾取,

  間接回聲的大小與房間環境、物品擺放以及墻面吸引系數等等因素有關,因此間接回聲是一種非線性信號

回聲消除技術主要用于在免提電話、電話會議系統等情形中,

AEC的基本原理

回聲消除常用方法

  • 聲場環境材料處理
    • 墻壁、天花板換成吸音材料,有效的較少聲音的反射,
    • 可以較為直接的抑制間接噪聲,但是直接噪聲無法抑制;成本較高
  • 回聲抑制器
    • 通過一個電平對比單元 輪流打開和關閉揚聲器和麥克風
    • 導致功放設備的輸出信號不連續,總體效果不好
  • 自適應回聲抵消(用的最多)
    • 首先通過自適應演算法來調整濾波器的迭代更新系數來估計出與實際回聲路徑逼近的期望信號,也就是模擬回聲,然后從麥克風采集的混合信號中減去這個模擬回聲,達到回聲抵消的功能

  如今解決 AEC 問題最常用的方法,就是

使用不同的自適應濾波演算法調整濾波器的權值向量,估計一個近似的回聲路徑來逼近真實回聲路徑,從而得到估計的回聲信號,并在純凈語音和回聲的混合信號中除去此信號來實作回聲的消除,

 AEC的基本原理

  $x(n)$為遠端輸入信號,經過未知的回聲路徑$w(n)$得到$y(n)=x(n)*w(n)$,再加上觀測噪聲$v(n)$,得到期望信號$d(n)= y(n) + v(n)$,x(n)通過自適應濾波器$\hat{w}(n)$得到估計的回聲信號,并與期望信號$d(n)$相減得到誤差信號$e(n)$,即$e(n)=d(n)-\hat{w}^T(n)x(n)$,誤差信號的值越小說明自適應濾波演算法所估計的回聲路徑就越接近實際的回聲路徑,

  濾波器采用特定的自適應演算法不停地調整權值向量,使估計的回聲路徑$\hat{w}(n)$逐漸趨近于真實回聲路徑$w(n)$,顯然,在 AEC 問題中,自適應濾波器的選擇對回聲消除的性能好壞起著十分關鍵的作用,

自適應濾波器的基本原理

  自適應濾波器是一個對輸入信號進行處理并不停學習,直到其達到期望值的器件,自適應濾波器在輸入信號非平穩條件下,也可以根據環境不斷調節濾波器權值向量,使演算法達到特定的收斂條件,從而實作自適應濾波程序,

  自適應濾波器按輸入信號型別可分為模擬濾波器和離散濾波器,本文中使用的是離散濾波器中的數字濾波器數字濾波器按結構可劃分為輸入不僅與過去和當前的輸入有關、還與過去的輸出有關的無限沖激回應濾波器(IIR),以及輸出與有限個過去和當前的輸入有關的有限沖激回應濾波器(FIR))為了使得自適應濾波器具有更強的穩定性,并且具有足夠的濾波器系數可以用來調整以達到特定的收斂準則,一般選取橫向的 FIR 濾波器進行來進行回聲的消除,(解釋:無限沖激回應型別的優勢在于零極點都包含于傳遞函式中,所以 IIR 能夠實作較低的階數構造較為理想化的尖銳帶通特性的傳遞函式,而劣勢也很明顯,即極點會造成量化誤差大,使系統魯棒性下降,并且對相位特性的控制變得困難;FIR(有限沖擊回應濾波器)的傳遞函式都是零點,所以魯棒性很穩定,相對而言更能實作線性特性)

 橫向FIR濾波器結構框圖

  $x(n)$是遠端輸入信號,$\hat{w}_i(n)$是濾波器系數,其中$i=0,1,...,L-1$,$L$為濾波器的長度,$n$為采樣點數,$\hat{w}(n)$為濾波器的權值向量且$\hat{w}(n)=[\hat{w}_0(n),\hat{w}_1(n),...,\hat{w}_{L-1}(n)]^T$,根據誤差信號$e(n)=d(n)-\hat{w}^T(n)x(n)$的值以及不同演算法的收斂準則調整濾波器的權值向量,

  然而自適應濾波演算法的選擇從根本上決定了回聲消除的效果是否良好,接下來將介紹幾種解決 AEC 問題的經典自適應濾波演算法,

回聲消除常用演算法

LSM演算法

  通過上面AEC的基本原理我們知道了誤差信號$e(n)$等于期望信號減去濾波器輸出信號:

$$e(n)=d(n)-\hat{w}^T(n)x(n)$$

運用最小均方誤差準則,通過對其求導并令其等于0,求到使得誤差$|e(n)|^2$最小時的$w$($|e(n)|$在最小點不可導,所以使用的是$|e(n)|^2$),對于LMS演算法,其濾波器系數迭代公式為

$$w(n+1)=w(n)+\mu\frac{\partial e(n)^2}{\partial w}\\\qquad\qquad =w(n)+2\mu e(n)\frac{\partial (d(n)-w*x(n))}{\partial w}\\\qquad\qquad =w(n)+2\mu e(n)x(n)$$

式中,$\mu$為固定步長因子,$\mu$的大小很大程度上決定了演算法的收斂與穩態性能,$\mu$越大,演算法收斂越快,但穩態誤差也越大;$\mu$越小,演算法收斂越慢,但穩態誤差也越小,為保證演算法穩態收斂,應使$\mu$在以下范圍取值:

$$0 < \mu  < \frac{2}{{\sum\limits_{i = 1}^N {x(i)_{}^2} }}$$

LMS演算法實作

LMS演算法的每次迭代都需要三個不同的步驟,順序如下

1、FIR濾波器輸出$y(n)$

$$y(n)=\sum_{i=0}^{N-1}w(n)x(n-i)=w^T(n)x(n)$$

2、估計誤差

$$e(n)=d(n)-y(n)$$

3、FIR向量的抽頭權值更新,以準備下一次迭代

$$w(n+1)=w(n)+2\mu e(n)x(n)$$

clear;clc;
snr=20;     % 信噪比
order=8;    % 自適應濾波器的階數為8
% Hn是濾波器權重
Hn =[0.8783 -0.5806 0.6537 -0.3223 0.6577 -0.0582 0.2895 -0.2710 0.1278 ...     % ...表示換行的意思
    -0.1508 0.0238 -0.1814 0.2519 -0.0396 0.0423 -0.0152 0.1664 -0.0245 ...
    0.1463 -0.0770 0.1304 -0.0148 0.0054 -0.0381 0.0374 -0.0329 0.0313 ...
    -0.0253 0.0552 -0.0369 0.0479 -0.0073 0.0305 -0.0138 0.0152 -0.0012 ...
    0.0154 -0.0092 0.0177 -0.0161 0.0070 -0.0042 0.0051 -0.0131 0.0059 ...
    -0.0041 0.0077 -0.0034 0.0074 -0.0014 0.0025 -0.0056 0.0028 -0.0005 ...
    0.0033 -0.0000 0.0022 -0.0032 0.0012 -0.0020 0.0017 -0.0022 0.0004 -0.0011 0 0];
Hn=Hn(1:order);
mu=0.02;             % mu表示步長
N=1000;             % 設定1000個音頻采樣點
Loop=150;           % 150次回圈
EE_LMS = zeros(N,1);    % 誤差初始化
for nn=1:Loop       % epoch=150
    win_LMS = zeros(1,order);   % 權重初始化w
    error_LMS=zeros(1,N)';      % 初始化誤差
    % 均勻分布的語音資料輸入
    r=sign(rand(N,1)-0.5);          % shape=(1000,1)的(0,1)均勻分布-0.5,sign(n)>0=1;<0=-1
    % 聲學環境語音輸出:輸入卷積Hn得到 輸出
    output=conv(r,Hn);              % r卷積Hn,output長度=length(u)+length(v)-1
    output=awgn(output,snr,'measured');     % 加一點環境噪聲:將白高斯噪聲添加到信號中

    for i=order:N         % i=81000
      input=r(i:-1:i-order+1);  % 每次迭代取8個資料進行處理
      e_LMS = output(i)-win_LMS*input;
      win_LMS = win_LMS+2*mu*e_LMS*input';
      error_LMS(i)=error_LMS(i)+e_LMS^2;
    end
    % 把總誤差相加
    EE_LMS = EE_LMS+error_LMS;

end
% 對總誤差求平均值
error_LMS = EE_LMS/Loop;

figure;
error1_LMS=10*log10(error_LMS(order:N));
plot(error1_LMS,'b.');              % 藍色
axis tight;                         % 使用緊湊的坐標軸
legend('LMS演算法');                  % 圖例
title('LMS演算法誤差曲線');           % 圖示題
xlabel('樣本');                     % x軸標簽
ylabel('誤差/dB');                  % y軸標簽
grid on;                            % 網格線
MATLAB演算法實作

我們將演算法用到音頻效果中,音頻獲取地址,代碼如下

clear;clc;
snr=20;     % 信噪比
order=8;    % 自適應濾波器的階數為8
[d, fs_orl] = audioread('./audio/handel.wav');      % 期望輸出(73113,1)
[x, fs_echo] = audioread('./audio/handel_echo.wav');       % 回聲輸入

mu=0.02;             % mu表示步長 0.02,
N=length(x);
Loop=10;           % 150次回圈

EE_LMS = zeros(N,1);
y_LMS = zeros(N,1);
for nn=1:Loop       % epoch=150
    win_LMS = zeros(order,1);   % 自適應濾波器權重初始化w
    y = zeros(N,1);             % 輸出
    error_LMS=zeros(N,1);       % 誤差初始化

    for i=order:N         % i=873113
      input=x(i:-1:i-order+1);  % 每次迭代取8個資料進行處理,(8,1)->(9,2)
      y(i)=win_LMS'*input;   % (8,1)'*(8*1)=1
      error_LMS(i) = d(i)-y(i);     % (8,1)'*(8,1)=1
      win_LMS = win_LMS+2*mu*error_LMS(i)*input;
      error_LMS(i)=error_LMS(i)^2;        % 記錄每個采樣點的誤差
    end
    % 把總誤差相加
    EE_LMS = EE_LMS+error_LMS;
    y_LMS=y_LMS+y;
end
error_LMS = EE_LMS/Loop;    % 對總誤差求平均值
y_LMS=y_LMS/Loop;           % 對輸出求平均

audiowrite("audio/done.wav", y_LMS, fs_orl);
sound(y_LMS)    % 聽一聽回聲消除后的音效

figure;
error1_LMS=10*log10(error_LMS(order:N));
plot(error1_LMS,'b.');              % 藍色
axis tight;                         % 使用緊湊的坐標軸
legend('LMS演算法');                  % 圖例
title('LMS演算法誤差曲線');           % 圖示題
xlabel('樣本');                     % x軸標簽
ylabel('誤差/dB');                  % y軸標簽
grid on;                            % 網格線

  LMS演算法在自適應濾波中流行的主要原因是其計算簡單,比其他常用的自適應演算法更易于實作,對于每次迭代,LMS演算法需要2N加法和2N+1乘法,但是總體而言,LMS 演算法復雜性低,它的收斂速度還是慢,而且是固定步長,這就需要在開始自適應濾波操作之前了解輸入信號的統計資訊,實際上,這是很難實作的,即使我們假設自適應回聲抵消系統的唯一輸入信號是語音,但仍有許多因素如信號輸入功率和振幅會影響其性能,為改善 LMS 這個不足之處,科研人員提出一系列改進演算法,NLMS 演算法就是其中一種,

NLMS演算法

  歸一化最小均方(NLMS)演算法是LMS演算法的一個擴展,利用可變的步長因子代替固定的步長因子,就得到了NLMS演算法,它通過計算最大步長值繞過了這個問題,

$$步長=\frac{1}{x^T(n)x(n)}$$

這個步長與輸入向量$x(n)$的系數的瞬時值的總期望能量的倒數成正比,輸入樣本的期望能量之和也等于輸入向量與自身的點積,

  NLMS演算法迭代方程為

$$w(n+1)=w(n)+\mu(n)e(n)x(n)=w(n)+\frac{1}{x^T(n)x(n)}e(n)x(n)$$

NLMS演算法實作

  由于步長引數是根據當前的輸入值來選擇的,因此NLMS演算法在未知信號下具有更大的穩定性,該演算法具有良好的收斂速度和相對簡單的計算能力,是實時自適應回波抵消系統的理想演算法,

  NLMS演算法的每次迭代都需要按照以下順序執行這些步驟,

1、計算了自適應濾波器的輸出

$$y(n)=\sum_{i=0}^{N-1}w(n)x(n-i)=w^T(n)x(n)$$

2、計算誤差信號:期望信號和濾波器輸出之間的差值

$$e(n)=d(n)-y(n)$$

3、計算輸入向量的步長值

$$\mu(n)=\frac{1}{x^T(n)x(n)}$$

4、更新濾波器抽頭權重,為下一次迭代做準備

$$w(n+1)=w(n)+\mu (n)e(n)x(n)$$

clear;
clc;
snr=20;     % 信噪比
order=8;    % 自適應濾波器的階數為8
% 房間聲學環境脈沖回應
Hn =[0.8783 -0.5806 0.6537 -0.3223 0.6577 -0.0582 0.2895 -0.2710 0.1278 ...     % ...表示換行的意思
    -0.1508 0.0238 -0.1814 0.2519 -0.0396 0.0423 -0.0152 0.1664 -0.0245 ...
    0.1463 -0.0770 0.1304 -0.0148 0.0054 -0.0381 0.0374 -0.0329 0.0313 ...
    -0.0253 0.0552 -0.0369 0.0479 -0.0073 0.0305 -0.0138 0.0152 -0.0012 ...
    0.0154 -0.0092 0.0177 -0.0161 0.0070 -0.0042 0.0051 -0.0131 0.0059 ...
    -0.0041 0.0077 -0.0034 0.0074 -0.0014 0.0025 -0.0056 0.0028 -0.0005 ...
    0.0033 -0.0000 0.0022 -0.0032 0.0012 -0.0020 0.0017 -0.0022 0.0004 -0.0011 0 0];
Hn=Hn(1:order);
mu=0.5;             % mu表示步長
N=1000;             % 1000個音頻采樣點
Loop=150;           % 150次回圈
EE_NLMS=zeros(N,1); % 初始化總誤差
for nn=1:Loop       % epoch=150
    win_NLMS=zeros(1,order);         % 權重初始化w
    error_NLMS=zeros(1,N)';     % 初始化誤差
    % 均勻分布的輸入值
    r=sign(rand(N,1)-0.5);          % shape=(1000,1)的(0,1)均勻分布-0.5,sign(n)>0=1;<0=-1
    % 聲學環境音頻輸出:輸入卷積Hn得到 輸出
    output=conv(r,Hn);              % r卷積Hn,output長度=length(u)+length(v)-1
    output=awgn(output,snr,'measured');     % 將白高斯噪聲添加到信號中

    % N=1000,每個采樣點
    for i=order:N         % i=81000
      input=r(i:-1:i-order+1);  % 每次迭代取8個資料進行處理
      e_NLMS = output(i)-win_NLMS*input;
      win_NLMS=win_NLMS+e_NLMS*input'/(input'*input);   % NLMS更新權重
      error_NLMS(i)=error_NLMS(i)+e_NLMS^2;
    end
    
    EE_NLMS=EE_NLMS+error_NLMS;     % 把總誤差相加
end
% 對總誤差求平均值
error_NLMS=EE_NLMS/Loop;

figure;
error_NLMS=10*log10(error_NLMS(order:N));
plot(error_NLMS,'r');       % 紅色
axis tight;                 % 使用緊湊的坐標軸
legend('NLMS演算法');           % 圖例
title('NLMS演算法誤差曲線');     % 圖示題
xlabel('樣本');                     % x軸標簽
ylabel('誤差/dB');                  % y軸標簽
grid on;                            % 網格線
MATLAB演算法實作

我們將演算法用到音頻效果中,音頻獲取地址,代碼如下,,

clear;clc;
snr=20;     % 信噪比
order=8;    % 自適應濾波器的階數為8
[d, fs_orl] = audioread('./audio/handel.wav');      % 期望輸出(73113,1)
[x, echo] = audioread('./audio/handel_echo.wav');       % 回聲輸入

fai=0.0001;
mu=0.02;             % mu表示步長
N=length(x);
Loop=10;           % 150次回圈
EE_NLMS = zeros(N,1);       % 初始化總損失
y_NLMS = zeros(N,1);        % 初始化AEC音頻輸出
for nn=1:Loop       % epoch=150
    win_NLMS = zeros(order,1);   % 權重初始化w
    y = zeros(N,1);              % 輸出
    error_NLMS=zeros(N,1);       % 初始化誤差

    for i=order:N         % i=873113
      input=x(i:-1:i-order+1);  % 每次迭代取8個資料進行處理,(8,1)->(9,2)
      y(i)=win_NLMS'*input;   % (8,1)'*(8*1)=1
      error_NLMS(i) = d(i)-y(i);     % (8,1)'*(8,1)=1
      k = mu/(fai + input'*input);
      win_NLMS = win_NLMS+2*k*error_NLMS(i)*input;
      error_NLMS(i)=error_NLMS(i)^2;        % 記錄每個采樣點的誤差
    end
    % 把總誤差相加
    EE_NLMS = EE_NLMS+error_NLMS;
    y_NLMS=y_NLMS+y;
end

error_NLMS = EE_NLMS/Loop;  % 對總誤差求平均值
y_NLMS=y_NLMS/Loop;         % 對輸出求平均
audiowrite("audio/done.wav", y_NLMS, fs_orl);
sound(y_NLMS)    % 聽一聽回聲消除后的音效

figure;
error1_NLMS=10*log10(error_NLMS(order:N));
plot(error1_NLMS,'b.');              % 藍色
axis tight;                         % 使用緊湊的坐標軸
legend('NLMS演算法');                  % 圖例
title('NLMS演算法誤差曲線');           % 圖示題
xlabel('樣本');                     % x軸標簽
ylabel('誤差/dB');                  % y軸標簽
grid on;                            % 網格線

NLMS演算法的每次迭代需要3N+1個乘法,僅比標準LMS演算法多N個,考慮到所獲得的穩定性和回波衰減增益,這是一個可接受的增加,有的時候會對NLMS進行改進,設$\mu(n)=\frac{\eta}{\delta +x^T(n)x(n)}$,$0 <\eta < 2$,$\delta $為一個較小的整數,防止輸入資料矢量$x(n)$的內積過小使得$\mu(n)$過大而引起穩定性能下降,一般取0.0001,

NSAF演算法

MATLAB代碼實作

當需要同時進行語音通信(或全雙工傳輸)時,回聲消除對于音頻電話會議非常重要,在回聲消除中,測得的麥克風信號$d(n)$包含兩個信號:

  • 近端語音信號$v(n)$ 

  • 遠端回聲信號$\hat{d}(n)$

目的是從麥克風信號中去除遠端回聲信號,從而僅發送近端語音信號,本示例包含一些聲音片段,因此您可能現在要調整計算機的音量,

房間脈沖回應

首先,您需要對揚聲器所在的揚聲器到麥克風的信號路徑的聲學建模,使用長有限沖激回應濾波器來描述房間的特征,下面的代碼生成一個隨機的脈沖回應,該回應與會議室的顯示相同,假設系統采樣率為16000 Hz,

fs = 16000;
M = fs / 2 + 1; 
frameSize = 2048; 

[B,A] = cheby2(4,20,[0.1 0.7]); 
impulseResponseGenerator = dsp.IIRFilter(' Numerator ',[zeros(1,6)B],... 
    'Denominator',A); 

FVT = fvtool(impulseResponseGenerator);  %分析過濾器 
FVT.Color = [1 1 1];

roomImpulseResponse = impulseResponseGenerator( ...
        (log(0.99*rand(1,M)+0.01).*sign(randn(1,M)).*exp(-0.002*(1:M)))');
roomImpulseResponse = roomImpulseResponse/norm(roomImpulseResponse)*4;
room = dsp.FIRFilter('Numerator', roomImpulseResponse');

fig = figure;
plot(0:1/fs:0.5, roomImpulseResponse);
xlabel('Time (s)');
ylabel('Amplitude');
title('Room Impulse Response');
fig.Color = [1 1 1];

 

近端語音信號

電話會議系統的用戶通常位于系統麥克風附近,這是麥克風上男性講話的聲音,

load nearspeech

player          = audioDeviceWriter('SupportVariableSizeInput', true, ...
                                    'BufferSize', 512, 'SampleRate', fs);
nearSpeechSrc   = dsp.SignalSource('Signal',v,'SamplesPerFrame',frameSize);
nearSpeechScope = dsp.TimeScope('SampleRate', fs, ...
                    'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll', ...
                    'YLimits', [-1.5 1.5], ...
                    'BufferLength', length(v), ...
                    'Title', 'Near-End Speech Signal', ...
                    'ShowGrid', true);

% Stream processing loop
while(~isDone(nearSpeechSrc))
    % Extract the speech samples from the input signal
    nearSpeech = nearSpeechSrc();
    % Send the speech samples to the output audio device
    player(nearSpeech);
    % Plot the signal
    nearSpeechScope(nearSpeech);
end
release(nearSpeechScope);

 

遠端語音信號

在電話會議系統中,語音從揚聲器中傳播出去,在房間里彈跳,然后被系統的麥克風拾取,聆聽在沒有近端語音的情況下在麥克風處拾起語音時的聲音,

load farspeech
farSpeechSrc    = dsp.SignalSource('Signal',x,'SamplesPerFrame',frameSize);
farSpeechSink   = dsp.SignalSink;
farSpeechScope  = dsp.TimeScope('SampleRate', fs, ...
                    'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll', ...
                    'YLimits', [-0.5 0.5], ...
                    'BufferLength', length(x), ...
                    'Title', 'Far-End Speech Signal', ...
                    'ShowGrid', true);

% Stream processing loop
while(~isDone(farSpeechSrc))
    % Extract the speech samples from the input signal
    farSpeech = farSpeechSrc();
    % Add the room effect to the far-end speech signal
    farSpeechEcho = room(farSpeech);
    % Send the speech samples to the output audio device
    player(farSpeechEcho);
    % Plot the signal
    farSpeechScope(farSpeech);
    % Log the signal for further processing
    farSpeechSink(farSpeechEcho);
end
release(farSpeechScope);

 

 

麥克風信號

麥克風處的信號既包含近端語音,也包含在整個房間中回聲的遠端語音,回聲消除器的目的是消除遠端語音,從而僅將近端語音發送回遠端聽眾,

reset(nearSpeechSrc);
farSpeechEchoSrc = dsp.SignalSource('Signal', farSpeechSink.Buffer, ...
                    'SamplesPerFrame', frameSize);
micSink         = dsp.SignalSink;
micScope        = dsp.TimeScope('SampleRate', fs,...
                    'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll',...
                    'YLimits', [-1 1], ...
                    'BufferLength', length(x), ...
                    'Title', 'Microphone Signal', ...
                    'ShowGrid', true);

% Stream processing loop
while(~isDone(farSpeechEchoSrc))
    % Microphone signal = echoed far-end + near-end + noise
    micSignal = farSpeechEchoSrc() + nearSpeechSrc() + ...
                0.001*randn(frameSize,1);
    % Send the speech samples to the output audio device
    player(micSignal);
    % Plot the signal
    micScope(micSignal);
    % Log the signal
    micSink(micSignal);
end
release(micScope);

頻域自適應濾波器(FDAF)

該示例中的演算法是頻域自適應濾波器(FDAF)當待識別系統的脈沖回應較長時,此演算法非常有用,FDAF使用快速卷積技術來計算輸出信號和過濾器更新,該計算可在MATLAB®中快速執行,通過頻點步長歸一化,它還具有快速收斂性能,為濾波器選擇一些初始引數,并查看遠端語音在誤差信號中的消除程度,

% Construct the Frequency-Domain Adaptive Filter
echoCanceller    = dsp.FrequencyDomainAdaptiveFilter('Length', 2048, ...
                    'StepSize', 0.025, ...
                    'InitialPower', 0.01, ...
                    'AveragingFactor', 0.98, ...
                    'Method', 'Unconstrained FDAF');

AECScope1   = dsp.TimeScope(4, fs, ...
                'LayoutDimensions', [4,1], ...
                'TimeSpan', 35, 'TimeSpanOverrunAction', 'Scroll', ...
                'BufferLength', length(x));

AECScope1.ActiveDisplay = 1;
AECScope1.ShowGrid      = true;
AECScope1.YLimits       = [-1.5 1.5];
AECScope1.Title         = 'Near-End Speech Signal';

AECScope1.ActiveDisplay = 2;
AECScope1.ShowGrid      = true;
AECScope1.YLimits       = [-1.5 1.5];
AECScope1.Title         = 'Microphone Signal';

AECScope1.ActiveDisplay = 3;
AECScope1.ShowGrid      = true;
AECScope1.YLimits       = [-1.5 1.5];
AECScope1.Title         = 'Output of Acoustic Echo Canceller mu=0.025';

AECScope1.ActiveDisplay = 4;
AECScope1.ShowGrid      = true;
AECScope1.YLimits       = [0 50];
AECScope1.YLabel        = 'ERLE (dB)';
AECScope1.Title         = 'Echo Return Loss Enhancement mu=0.025';

% Near-end speech signal
release(nearSpeechSrc);
nearSpeechSrc.SamplesPerFrame = frameSize;

% Far-end speech signal
release(farSpeechSrc);
farSpeechSrc.SamplesPerFrame = frameSize;

% Far-end speech signal echoed by the room
release(farSpeechEchoSrc);
farSpeechEchoSrc.SamplesPerFrame = frameSize;

回波回波增強(ERLE)

由于您可以訪問近端和遠端語音信號,因此可以計算回聲回波損耗增強(ERLE),這是對回聲衰減量的平滑度量(以dB為單位),從圖中可以看出,在收斂周期結束時您獲得了大約35 dB的ERLE,

diffAverager = dsp.FIRFilter('Numerator', ones(1,1024));
farEchoAverager = clone(diffAverager);
setfilter(FVT,diffAverager);

micSrc = dsp.SignalSource('Signal', micSink.Buffer, ...
    'SamplesPerFrame', frameSize);

% Stream processing loop - adaptive filter step size = 0.025
while(~isDone(nearSpeechSrc))
    nearSpeech = nearSpeechSrc();
    farSpeech = farSpeechSrc();
    farSpeechEcho = farSpeechEchoSrc();
    micSignal = micSrc();
    % Apply FDAF
    [y,e] = echoCanceller(farSpeech, micSignal);
    % Send the speech samples to the output audio device
    player(e);
    % Compute ERLE
    erle = diffAverager((e-nearSpeech).^2)./ farEchoAverager(farSpeechEcho.^2);
    erledB = -10*log10(erle);
    % Plot near-end, far-end, microphone, AEC output and ERLE
    AECScope1(nearSpeech, micSignal, e, erledB);
end
release(AECScope1);

不同步長值的影響

為了獲得更快的收斂速度,可以嘗試使用更大的步長值,但是,這種增加會產生另一種效果:當近端揚聲器講話時,自適應濾波器“調整不當”,聆聽您選擇的步長比以前大60%時會發生什么,

% Change the step size value in FDAF
reset(echoCanceller);
echoCanceller.StepSize = 0.04;

AECScope2 = clone(AECScope1);
AECScope2.ActiveDisplay = 3;
AECScope2.Title = 'Output of Acoustic Echo Canceller mu=0.04';
AECScope2.ActiveDisplay = 4;
AECScope2.Title = 'Echo Return Loss Enhancement mu=0.04';

reset(nearSpeechSrc);
reset(farSpeechSrc);
reset(farSpeechEchoSrc);
reset(micSrc);
reset(diffAverager);
reset(farEchoAverager);

% Stream processing loop - adaptive filter step size = 0.04
while(~isDone(nearSpeechSrc))
    nearSpeech = nearSpeechSrc();
    farSpeech = farSpeechSrc();
    farSpeechEcho = farSpeechEchoSrc();
    micSignal = micSrc();
    % Apply FDAF
    [y,e] = echoCanceller(farSpeech, micSignal);
    % Send the speech samples to the output audio device
    player(e);
    % Compute ERLE
    erle = diffAverager((e-nearSpeech).^2)./ farEchoAverager(farSpeechEcho.^2);
    erledB = -10*log10(erle);
    % Plot near-end, far-end, microphone, AEC output and ERLE
    AECScope2(nearSpeech, micSignal, e, erledB);
end

release(nearSpeechSrc);
release(farSpeechSrc);
release(farSpeechEchoSrc);
release(micSrc);
release(diffAverager);
release(farEchoAverager);
release(echoCanceller);
release(AECScope2);

回聲回波損耗增強比較

步長較大時,由于近端語音引入的錯誤調整,導致ERLE性能不佳,為了解決此性能難題,聲學回聲消除器包括一種檢測方案,可告知何時存在近端語音并在這些時間段內降低步長值,沒有這種檢測方案,從ERLE圖可以看出,具有較大步長的系統的性能不如前者,

使用磁區減少延遲

對于長脈沖回應,傳統FDAF在數值上比時域自適應濾波更有效,但是由于輸入幀大小必須是指定濾波器長度的倍數,因此它具有高延遲,對于許多實際應用程式來說,這可能是不可接受的,通過使用磁區的FDAF可以減少延遲,該方法將過濾器脈沖回應分為較短的部分,將FDAF應用于每個部分,然后合并中間結果,在這種情況下,幀大小必須是磁區(塊)長度的倍數,從而大大減少了長脈沖回應的等待時間,

% Reduce the frame size from 2048 to 256
frameSize = 256;
nearSpeechSrc.SamplesPerFrame    = frameSize;
farSpeechSrc.SamplesPerFrame     = frameSize;
farSpeechEchoSrc.SamplesPerFrame = frameSize;
micSrc.SamplesPerFrame           = frameSize;
% Switch the echo canceller to Partitioned constrained FDAF
echoCanceller.Method      = 'Partitioned constrained FDAF';
% Set the block length to frameSize
echoCanceller.BlockLength = frameSize;

% Stream processing loop
while(~isDone(nearSpeechSrc))
    nearSpeech = nearSpeechSrc();
    farSpeech = farSpeechSrc();
    farSpeechEcho = farSpeechEchoSrc();
    micSignal = micSrc();
    % Apply FDAF
    [y,e] = echoCanceller(farSpeech, micSignal);
    % Send the speech samples to the output audio device
    player(e);
    % Compute ERLE
    erle = diffAverager((e-nearSpeech).^2)./ farEchoAverager(farSpeechEcho.^2);
    erledB = -10*log10(erle);
    % Plot near-end, far-end, microphone, AEC output and ERLE
    AECScope2(nearSpeech, micSignal, e, erledB);
end

 

開源的音頻處理庫

Speex:開源語音編解碼器

基于碼激勵線性預測編碼的 Speex 平臺來進行回音消除,兩者都是將多媒體通信的模塊集成在瀏覽器中,徹底屏蔽掉其它方案存在的作業系統和底層硬體間的差異,能夠用于所有作業系統和瀏覽器,

專為語音設計的無專利音頻壓縮格式,Speex專案旨在通過提供免費替代昂貴的專有語音編解碼器的方法來降低語音應用程式的進入門檻,此外,Speex非常適合Internet應用程式,并提供了大多數其他編解碼器中沒有的有用功能,

Speex基于CELP ,旨在以2到44 kbps的位元率壓縮語音Speex的一些功能包括:

  • 同一位流中的窄帶(8 kHz),寬帶(16 kHz)和超寬帶(32 kHz)壓縮
  • 強度立體聲編碼
  • 丟包隱藏
  • 可變位元率操作(VBR)
  • 語音活動檢測(VAD)
  • 不連續傳輸(DTX)
  • 定點埠
  • 回聲消除器
  • 噪聲抑制

Speex編解碼器已被Opus淘汰,它會繼續可用,但是由于Opus在各個方面都比Speex更好,因此建議用戶切換Opus,

Opus互動音頻編解碼器

Opus是一款完全開放,免版稅,功能廣泛的音頻編解碼器,Opus在互聯網上的互動式語音和音樂傳輸方面無可匹敵,但也適用于存盤和流媒體應用程式,

Opus可以處理各種音頻應用程式,包括IP語音,視頻會議,游戲內聊天,甚至是遠程現場音樂表演,它可以從低位元率的窄帶語音擴展到高質量的立體聲音樂,支持的功能有:

  • 位元率從6 kb / s到510 kb / s
  • 采樣率從8 kHz(窄帶)到48 kHz(全帶)
  • 幀大小從2.5毫秒到60毫秒
  • 支持恒定位元率(CBR)和可變位元率(VBR)
  • 音頻帶寬從窄帶到全帶
  • 支持語音和音樂
  • 支持單聲道和立體聲
  • 最多支持255個通道(多流幀)
  • 動態可調的位元率,音頻帶寬和幀大小
  • 良好的丟失健壯性和資料包丟失隱藏(PLC)
  • 浮點和定點實作

WebRTC(實時通信):谷歌公司在 2010 年收購了 Global IP Solutions 后逐步開源的音視頻解決方案 WebRTC,該方案中的 AEC 模塊是實作自適應濾波演算法的優秀平臺,它集成了聲學回聲消除所需要的所有結構,用戶可以通過簡單API為瀏覽器和移動應用程式提供實時通信(RTC)功能

推薦使用webrtc

回聲消除需要時候的技術:雙講檢測技術(語音活動檢測,區分希臘佛中是否存在雙端講話)、自適應濾波技術(主要性能指標:跟蹤性能、抗沖激性、魯棒性和計算復雜性)、后處理(消除自適應濾波器的輸出誤差)

 

參考

《基于自適應濾波器的聲學回聲消除研究——馮江浩》

MATLAB官網audio_Examples_Acoustic Echo Cancellation 

Hand Book of Speech Enhancement and Recognition

語音自適應回聲消除演算法代碼講解

WebRTC AEC AGC ANC NS示例

目前大家都在用什么演算法:黃翔. 基于麥克風陣列的回聲抵消系統研究[D]. 湖北工業大學, 2018.

京東科大訊飛聯合開發的叮咚智能音箱的回聲消除技術體系包括四大核心模塊:超低信噪比回聲消除、多通道回聲消除、非線性回聲消除和麥克風陣列回聲消除,

思必馳云知聲Nuance 等也正不斷開發出能適應越來越復雜環境的回聲消除演算法,

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/67661.html

標籤:其他

上一篇:性能提升40%: 騰訊 TKE 用 eBPF 繞過 conntrack 優化 K8s Service

下一篇:神經網路與機器學習第3版學習筆記-第0章 導言

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 網閘典型架構簡述

    網閘架構一般分為兩種:三主機的三系統架構網閘和雙主機的2+1架構網閘。 三主機架構分別為內端機、外端機和仲裁機。三機無論從軟體和硬體上均各自獨立。首先從硬體上來看,三機都用各自獨立的主板、記憶體及存盤設備。從軟體上來看,三機有各自獨立的作業系統。這樣能達到完全的三機獨立。對于“2+1”系統,“2”分為 ......

    uj5u.com 2020-09-10 02:00:44 more
  • 如何從xshell上傳檔案到centos linux虛擬機里

    如何從xshell上傳檔案到centos linux虛擬機里及:虛擬機CentOs下執行 yum -y install lrzsz命令,出現錯誤:鏡像無法找到軟體包 前言 一、安裝lrzsz步驟 二、上傳檔案 三、遇到的問題及解決方案 總結 前言 提示:其實很簡單,往虛擬機上安裝一個上傳檔案的工具 ......

    uj5u.com 2020-09-10 02:00:47 more
  • 一、SQLMAP入門

    一、SQLMAP入門 1、判斷是否存在注入 sqlmap.py -u 網址/id=1 id=1不可缺少。當注入點后面的引數大于兩個時。需要加雙引號, sqlmap.py -u "網址/id=1&uid=1" 2、判斷文本中的請求是否存在注入 從文本中加載http請求,SQLMAP可以從一個文本檔案中 ......

    uj5u.com 2020-09-10 02:00:50 more
  • Metasploit 簡單使用教程

    metasploit 簡單使用教程 浩先生, 2020-08-28 16:18:25 分類專欄: kail 網路安全 linux 文章標簽: linux資訊安全 編輯 著作權 metasploit 使用教程 前言 一、Metasploit是什么? 二、準備作業 三、具體步驟 前言 Msfconsole ......

    uj5u.com 2020-09-10 02:00:53 more
  • 游戲逆向之驅動層與用戶層通訊

    驅動層代碼: #pragma once #include <ntifs.h> #define add_code CTL_CODE(FILE_DEVICE_UNKNOWN,0x800,METHOD_BUFFERED,FILE_ANY_ACCESS) /* 更多游戲逆向視頻www.yxfzedu.com ......

    uj5u.com 2020-09-10 02:00:56 more
  • 北斗電力時鐘(北斗授時服務器)讓網路資料更精準

    北斗電力時鐘(北斗授時服務器)讓網路資料更精準 北斗電力時鐘(北斗授時服務器)讓網路資料更精準 京準電子科技官微——ahjzsz 近幾年,資訊技術的得了快速發展,互聯網在逐漸普及,其在人們生活和生產中都得到了廣泛應用,并且取得了不錯的應用效果。計算機網路資訊在電力系統中的應用,一方面使電力系統的運行 ......

    uj5u.com 2020-09-10 02:01:03 more
  • 【CTF】CTFHub 技能樹 彩蛋 writeup

    ?碎碎念 CTFHub:https://www.ctfhub.com/ 筆者入門CTF時時剛開始刷的是bugku的舊平臺,后來才有了CTFHub。 感覺不論是網頁UI設計,還是題目質量,賽事跟蹤,工具軟體都做得很不錯。 而且因為獨到的金幣制度的確讓人有一種想去刷題賺金幣的感覺。 個人還是非常喜歡這個 ......

    uj5u.com 2020-09-10 02:04:05 more
  • 02windows基礎操作

    我學到了一下幾點 Windows系統目錄結構與滲透的作用 常見Windows的服務詳解 Windows埠詳解 常用的Windows注冊表詳解 hacker DOS命令詳解(net user / type /md /rd/ dir /cd /net use copy、批處理 等) 利用dos命令制作 ......

    uj5u.com 2020-09-10 02:04:18 more
  • 03.Linux基礎操作

    我學到了以下幾點 01Linux系統介紹02系統安裝,密碼啊破解03Linux常用命令04LAMP 01LINUX windows: win03 8 12 16 19 配置不繁瑣 Linux:redhat,centos(紅帽社區版),Ubuntu server,suse unix:金融機構,證券,銀 ......

    uj5u.com 2020-09-10 02:04:30 more
  • 05HTML

    01HTML介紹 02頭部標簽講解03基礎標簽講解04表單標簽講解 HTML前段語言 js1.了解代碼2.根據代碼 懂得挖掘漏洞 (POST注入/XSS漏洞上傳)3.黑帽seo 白帽seo 客戶網站被黑帽植入劫持代碼如何處理4.熟悉html表單 <html><head><title>TDK標題,描述 ......

    uj5u.com 2020-09-10 02:04:36 more
最新发布
  • 2023年最新微信小程式抓包教程

    01 開門見山 隔一個月發一篇文章,不過分。 首先回顧一下《微信系結手機號資料庫被脫庫事件》,我也是第一時間得知了這個訊息,然后跟蹤了整件事情的經過。下面是這起事件的相關截圖以及近日流出的一萬條資料樣本: 個人認為這件事也沒什么,還不如關注一下之前45億快遞資料查詢渠道疑似在近日復活的訊息。 訊息是 ......

    uj5u.com 2023-04-20 08:48:24 more
  • web3 產品介紹:metamask 錢包 使用最多的瀏覽器插件錢包

    Metamask錢包是一種基于區塊鏈技術的數字貨幣錢包,它允許用戶在安全、便捷的環境下管理自己的加密資產。Metamask錢包是以太坊生態系統中最流行的錢包之一,它具有易于使用、安全性高和功能強大等優點。 本文將詳細介紹Metamask錢包的功能和使用方法。 一、 Metamask錢包的功能 數字資 ......

    uj5u.com 2023-04-20 08:47:46 more
  • vulnhub_Earth

    前言 靶機地址->>>vulnhub_Earth 攻擊機ip:192.168.20.121 靶機ip:192.168.20.122 參考文章 https://www.cnblogs.com/Jing-X/archive/2022/04/03/16097695.html https://www.cnb ......

    uj5u.com 2023-04-20 07:46:20 more
  • 從4k到42k,軟體測驗工程師的漲薪史,給我看哭了

    清明節一過,盲猜大家已經無心上班,在數著日子準備過五一,但一想到銀行卡里的余額……瞬間心情就不美麗了。最近,2023年高校畢業生就業調查顯示,本科畢業月平均起薪為5825元。調查一出,便有很多同學表示自己又被平均了。看著這一資料,不免讓人想到前不久中國青年報的一項調查:近六成大學生認為畢業10年內會 ......

    uj5u.com 2023-04-20 07:44:00 more
  • 最新版本 Stable Diffusion 開源 AI 繪畫工具之中文自動提詞篇

    🎈 標簽生成器 由于輸入正向提示詞 prompt 和反向提示詞 negative prompt 都是使用英文,所以對學習母語的我們非常不友好 使用網址:https://tinygeeker.github.io/p/ai-prompt-generator 這個網址是為了讓大家在使用 AI 繪畫的時候 ......

    uj5u.com 2023-04-20 07:43:36 more
  • 漫談前端自動化測驗演進之路及測驗工具分析

    隨著前端技術的不斷發展和應用程式的日益復雜,前端自動化測驗也在不斷演進。隨著 Web 應用程式變得越來越復雜,自動化測驗的需求也越來越高。如今,自動化測驗已經成為 Web 應用程式開發程序中不可或缺的一部分,它們可以幫助開發人員更快地發現和修復錯誤,提高應用程式的性能和可靠性。 ......

    uj5u.com 2023-04-20 07:43:16 more
  • CANN開發實踐:4個DVPP記憶體問題的典型案例解讀

    摘要:由于DVPP媒體資料處理功能對存放輸入、輸出資料的記憶體有更高的要求(例如,記憶體首地址128位元組對齊),因此需呼叫專用的記憶體申請介面,那么本期就分享幾個關于DVPP記憶體問題的典型案例,并給出原因分析及解決方法。 本文分享自華為云社區《FAQ_DVPP記憶體問題案例》,作者:昇騰CANN。 DVPP ......

    uj5u.com 2023-04-20 07:43:03 more
  • msf學習

    msf學習 以kali自帶的msf為例 一、msf核心模塊與功能 msf模塊都放在/usr/share/metasploit-framework/modules目錄下 1、auxiliary 輔助模塊,輔助滲透(埠掃描、登錄密碼爆破、漏洞驗證等) 2、encoders 編碼器模塊,主要包含各種編碼 ......

    uj5u.com 2023-04-20 07:42:59 more
  • Halcon軟體安裝與界面簡介

    1. 下載Halcon17版本到到本地 2. 雙擊安裝包后 3. 步驟如下 1.2 Halcon軟體安裝 界面分為四大塊 1. Halcon的五個助手 1) 影像采集助手:與相機連接,設定相機引數,采集影像 2) 標定助手:九點標定或是其它的標定,生成標定檔案及內參外參,可以將像素單位轉換為長度單位 ......

    uj5u.com 2023-04-20 07:42:17 more
  • 在MacOS下使用Unity3D開發游戲

    第一次發博客,先發一下我的游戲開發環境吧。 去年2月份買了一臺MacBookPro2021 M1pro(以下簡稱mbp),這一年來一直在用mbp開發游戲。我大致分享一下我的開發工具以及使用體驗。 1、Unity 官網鏈接: https://unity.cn/releases 我一般使用的Apple ......

    uj5u.com 2023-04-20 07:40:19 more