Wizard Notes

Python, JavaScript を使った音楽信号分析の技術録、作曲活動に関する雑記

リアルタイム雑音抑圧処理:適応線スペクトル強調器 (ALE) のPython実装と適用例

音信号向けの簡単かつ実用的なリアルタイム向け雑音抑圧処理として,以下の書籍で適応線スペクトル強調器 (ALE) が紹介されていました.

上記の書籍を参考にしつつ,Pythonでの適応線スペクトル強調器 (ALE)の実装と,様々な信号への適用例を紹介します。

適応線スペクトル強調器 (ALE) の概要

適応線スペクトル強調器 (ALE) は雑音が含まれた観測信号から目的信号を強調/予測する処理です.

目的信号は音声や楽音のような周期性信号であり,周波数が未知でも抽出できることがこの手法の長所です.

以下がブロック図です。

遅延素子数Dは相関分離パラメタ (de-correlation parameter) と呼ばれており,目的信号と雑音の相関を低くする役割があります.

処理として適応線スペクトル強調器はDサンプル遅れた信号に線形フィルタを適用することで現在の目的信号を予測(線形予測)しています。

線形フィルタは,観測信号x(n)と予測信号y(n)から算出した予測誤差 e(n)を最小化する基準で算出/更新します.

フィルタ係数の更新方法は,同じく適応フィルタであるフィードバックキャンセラでも用いた LMS アルゴリズムを利用します.

以下はNLMSアルゴリズムによる更新式になります.

Python実装

import numpy as np


# 正弦波を生成する関数
def gen_sin_wave(sr, fo, duration, phase=0.0):
    t = np.arange(0, sr*duration) / sr
    t_ph = phase * 2 * np.pi
    return np.sin(2*np.pi*fo*t + t_ph)

# 正弦波を生成する関数
def adaptive_line_enhancer(
        x, y, e, thrd, mu, 
        n_sample, D, K
    ):
    
    # サンプルごとに処理
    for n in range(0, n_sample):
        # (相関分離パラメタD+フィルタ長K未満のサンプルでは処理しない)
        if n < D + K: 
            y[n] = 0.0
            e[n]  = x[n] - y[n]
            continue
        
        norm  = 0.0
        tmp_x = 0.0
        tmp_y = 0.0
        
        # 現在のサンプル n における予測値 y(n) を算出
        for k in range(0, K):
            tmp_x = x[n - D - k]
            tmp_y = w[k] * tmp_x
            norm += tmp_x**2
            y[n] += tmp_y
        
        e[n]  = x[n] - y[n] # 予測誤差
        
        # 無音部でフィルタ係数を更新しない場合   
        norm2 = np.mean(x[n-8:n]**2) 
        if norm > thrd and norm2 > thrd:
            for k in range(0, K):
                w[k] += mu * e[n] * x[n - D - k] / norm

# パラメタ
np.random.seed(2) #乱数シード
sr = 16000      # サンプリング周波数
fo = 440        # 目的信号の周波数
duration = 0.1  # 信号長
std = 0.5       # 雑音の大きさ(白色雑音の標準偏差)

D = 128         # 相関分離パラメタ
K = 257         # フィルタ長
mu = 0.1        # フィルタ係数更新におけるステップサイズ
thrd = 1e-5     # フィルタ係数更新実行判断用閾値

# 入力信号を作成
## 目的信号である周期性信号を作成
s = gen_sin_wave(sr, fo, duration)
## 雑音信号(白色雑音)を重畳
x = s + np.random.normal(0.0, std, s.shape)
x /= np.max(np.abs(x))

n_sample = x.shape[0]
y = np.zeros(n_sample) # 予測値
e = np.zeros(n_sample) # 予測誤差
w = np.random.normal(0.0, 0.01, K) # フィルタ係数
# 適応線スペクトル強調
adaptive_line_enhancer(
    x, y, e, thrd, mu, 
    n_sample, D, K)

様々な観測信号への適用例

単一正弦波+白色雑音

点線はフィルタ長K+相関分離パラメタDであり、このK+Dサンプルから適応線スペクトル強調処理が開始されます.

プロットを見ると,出力信号 y(n) では観測信号 x(n)よりも雑音が小さい正弦波が得られていることが確認できます.

次に、雑音を大きくした条件の結果を示します。

std = 1.0

std = 2.0

雑音が大きいほど出力信号 y(n)の雑音も増えていますが,観測信号x(n)と比べると単一正弦波を抽出できていることを確認できます.

複数正弦波+白色雑音

正弦波が複数であっても抽出できています.したがって,目的信号が音声*1や楽音*2でも適応線スペクトル強調は利用できると考えられます.

単一正弦波の基本周波数が途中で変化

この条件であっても、基本周波数の変化に追従して抽出できていることが確認できます.

ただし,出力(フィルタ係数)が安定するまでにフィルタ長程度のサンプル数が経過する必要があります.

観測信号に無音区間がある場合

先ほどのPython実装中の

        # 無音部でフィルタ係数を更新しない場合   
        norm2 = np.mean(x[n-8:n]**2) 
        if norm > thrd and norm2 > thrd:

がある場合、無音区間でも正弦波を予測することができます。

一方で、norm2を削除すると、振幅が小さくする方向にフィルタ係数が更新されます。

周波数が時間変化する信号

まとめ

適応線スペクトル強調器 (ALE) のPython実装と適用例を紹介しました.

リアルタイム処理が可能であり,周波数が未知の周期性信号に対しても抽出が可能な手法なので,例えば(リアルタイム)録音での雑音抑圧などに利用できると思います.

ただし,実用上では,雑音は有色の場合が多く,また,突発音のような非定常性も考慮する必要があります.

参考文献

*1:ただし,子音のような非周期区間は強調が困難

*2:打楽器やアタック音は困難