Wizard Notes

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

Python: LibROSA で調波打楽器音分離 (HPSS)

調波打楽器音分離とは?

一般的な楽曲では、様々な楽器音が含まれています。 そのため、元の楽曲信号から直接、音楽的な情報(例:コード進行)を分析するのは 計算機ではなかなか難しいです*1

そこで、分析の前処理として、 打楽器の音と非打楽器(調波楽器)の音を分離する調波打楽器音分離 (HPSS: Hermonic/Percussive Source Seperation) *2が良く使われています。

この記事では、HPSSの概要とPython (LibROSA) のコードの解説します。



調波打楽器音分離のアイディア

フーリエ変換等を使って周波数に変換した楽曲信号を考えます。

f:id:Kurene:20191012204945p:plain

上記の画像のように、楽曲信号には複数の楽器の音が含まれています。

調波打楽器音分離では、楽曲信号は調波楽器音と打楽器音を足し合わせた構造であると考え、
楽曲信号から調波楽器音と打楽器音を推定
します。*3

ただし調波/打楽器音は未知であるため、何らかの仮定をおく必要があります。

調波打楽器音分離では、上記の画像を見て気づくように、

  • 調波楽器音: 時間方向に連続的
  • 打楽器音:  周波数方向に連続的

であるという特徴を用いることで、調波/打楽器音を推定します。 (もしくは、調波/打楽器音について何らかの事前学習を行う手法もあります。)

アルゴリズムの設計

非負値行列因子分解+基底クラスタリング

非負値行列因子分解を用いて複数の基底を求めた後、
その基底をSVMサポートベクターマシン)で調波/打楽器音基底に2クラス分類します。そして、調波/打楽器音に分類された基底のみを用いて対応するアクティベーション行列との行列積を求めることで、調波/打楽器音のみの信号のスペクトログラムを推定します。なお、基底分類用の2クラスSVMの学習を事前学習しています。

Separation of drums from polyphonic music using non-negative matrix factorization and support vector machine - ResearchGate

最適化問題(行列因子分解)として解く

次のコスト項と制約に基づく最適化問題として解く手法です。

  • 調波/打楽器音の時間/周波数方向の連続性に基づくコスト項
  • 等式制約
  • 非負値制約

以下の論文では補助関数に基づく反復アルゴリズムにより、調波/打楽器音のスペクトログラムを推定します。

Separation of a monaural audio signal into harmonic/percussive components by complementary diffusion on spectrogram - ResearchGate

深層学習を使った手法

MaD TwinNet というニューラルネットを使った手法が2018年に提案されています。

名称の通り、"MaD (Masker and Denoiser)"と呼ばれるネットワーク構造と、TwinNet という2つの構造から成るネットワークを使っています。

arg.cs.tut.fi

メディアンフィルタベースの手法

最適化問題として解く場合、アルゴリズムが複雑になり、また、どうしても計算量が多くなってしまいます。そのため、大量の楽曲データを解析したり、高速な処理が求められたり、計算資源が限られているユースケースでは使いづらいことがあります。

一方で、以下の論文では、画像処理などでよく使われているメディアンフィルタを用いることで、高速に調波/打楽器音を推定しています。

Harmonic/Percussive Separation using Median Filtering - ResearchGate

この手法では、周波数/時間方向の1次元メディアンフィルタを元の楽曲信号にフィルタリングすることで調波/打楽器音を算出します。LibROSAの実装もこちらの手法がベースとなっています。

LibROSAにおける調波打楽器分離(HPSS)の実装

使い方

# Extract harmonic and percussive components
y, sr = librosa.load(librosa.util.example_audio_file())
y_harmonic, y_percussive = librosa.effects.hpss(y)
# Get a more isolated percussive component by widening its margin
y_harmonic, y_percussive = librosa.effects.hpss(y, margin=(1.0,5.0))

librosa.effects.hpss()

実装の詳細

LibROSAのHPSS(調波打楽器音分離)の実装を見ていきます。

librosa.effects.hpss()

楽曲信号yをそのまま引数にできる、便利なラッパーです。
中身は、時間周波数領域への変換と逆変換を行っているだけです。

y_harmonicy_percussiveがそれぞれ調波/打楽器音です。これら信号を様々な分析に利用したり、```librosa.output.write_wav()````で書き出したりします。

HPSSの本体はlibrosa.decompose.hpss() であり、それに時間周波数領域の楽曲信号をstft渡しています。

def hpss(y, **kwargs):

    # Compute the STFT matrix
    stft = core.stft(y)

    # Decompose into harmonic and percussives
    stft_harm, stft_perc = decompose.hpss(stft, **kwargs)

    # Invert the STFTs.  Adjust length to match the input.
    y_harm = util.fix_length(core.istft(stft_harm, dtype=y.dtype), len(y))
    y_perc = util.fix_length(core.istft(stft_perc, dtype=y.dtype), len(y))

    return y_harm, y_perc

librosa.effects.hpss()

librosa.decompose.hpss()

処理の流れは以下通りです。

  1. 時間周波数領域の楽曲信号Sを、振幅Sと位相phaseに分離
  2. kernel_sizeより、調波/打楽器音成分用のメディアンフィルタの長さwin_harm/win_percを設定
  3. marginより、調波/打楽器音成分用の閾値を設定(後述)
  4. 周波数/時間方向の1次元メディアンフィルタを適用し、それぞれ調波/打楽器音の振幅スペクトログラムmask_harmmask_percを算出
  5. ソフトマスク(ウィーナーフィルタ)librosa.util.softmask()を適用
    • margin_harm/margin_perc > 1の場合、その値に応じて閾値処理
  6. 返り値
    • mask==True: 調波/打楽器の振幅スペクトログラムを返す
    • mask==False: 元の楽曲信号の位相をそのまま利用して、調波/打楽器の時間周波数領域の信号を返す
def hpss(S, kernel_size=31, power=2.0, mask=False, margin=1.0):
    # (1) 
    if np.iscomplexobj(S):
        S, phase = core.magphase(S)
    else:
        phase = 1

    # (2)
    if np.isscalar(kernel_size):
        win_harm = kernel_size
        win_perc = kernel_size
    else:
        win_harm = kernel_size[0]
        win_perc = kernel_size[1]
    
    # (3)
    if np.isscalar(margin):
        margin_harm = margin
        margin_perc = margin
    else:
        margin_harm = margin[0]
        margin_perc = margin[1]

    # (4) Compute median filters. Pre-allocation here preserves memory layout.
    harm = np.empty_like(S)
    harm[:] = median_filter(S, size=(1, win_harm), mode='reflect')

    perc = np.empty_like(S)
    perc[:] = median_filter(S, size=(win_perc, 1), mode='reflect')

    ...

    # (5)
    mask_harm = util.softmask(harm, perc * margin_harm,
                              power=power,
                              split_zeros=split_zeros)

    mask_perc = util.softmask(perc, harm * margin_perc,
                              power=power,
                              split_zeros=split_zeros)
    # (6)
    if mask:
        return mask_harm, mask_perc

    return ((S * mask_harm) * phase, (S * mask_perc) * phase)

librosa.decompose

LibROSAの実装では、メディアンフィルタによって算出した調波/打楽器音の振幅スペクトログラムをそのまま使うのではなく、ソフトマスク(ウィーナーフィルタ)を利用することで聴感的な歪みを軽減しています。

また、調波/打楽器音を抽出しやすくするために、mask_harmmask_percという変数の値によって閾値処理を行っています。これは下記の論文で提案されている手法です。

Extending Harmonic-Percussive Separation of Audio Signals - ISMIR 2014

以下のページでは、この手法を適用した結果が公開されています。

https://www.audiolabs-erlangen.de/resources/2014-ISMIR-ExtHPSep/www.audiolabs-erlangen.de

LibROSA: 調波打楽器分離(HPSS)の適用例

コード

import librosa
import numpy as np
import matplotlib.pyplot as plt

# Main
sr = 16000
margin = 1.5
offset = 116
duration = 20
filepath = "02 Retrograde Amnesia.mp3"

y, sr = librosa.load(filepath, sr=sr, offset=offset, duration=duration, mono=True)
y_harm, y_perc = librosa.effects.hpss(y, margin=margin)

# Dump signals
librosa.output.write_wav('hpss_org.wav', y, sr)
librosa.output.write_wav('hpss_harm.wav', y_harm, sr)
librosa.output.write_wav('hpss_perc.wav', y_perc, sr)

# Plot
plt.subplot(3,1,1)
plt.plot(y)
plt.title("Original signal")

plt.subplot(3,1,2)
plt.plot(y_harm)
plt.title("Harmonic signal")

plt.subplot(3,1,3)
plt.plot(y_perc)
plt.title("Percussive signal")

plt.tight_layout()
plt.show()

実際の楽曲への適用

自身の楽曲に適用してみました。


f:id:Kurene:20191014181212p:plain
HPSSの適用結果

課題としては、

  • 調波楽器(エレキギター、ボーカル)のアタック音が打楽器音の方に含まれてしまう
  • (特に)打楽器音の音質が悪い

が挙げられます。

コード進行やリズムの分析などの用途としては十分かもしれませんが、調波/打楽器音単体を聞きたいというような用途に対してはまだ余地があるように思います。そのような場合、パラメタ(例:フィルタサイズ、マージン)をチューニングしてみるか、最適化ベースの手法を検討してみるのもよいかもしれません。

また、調波音を取り出すメディアンフィルタが時間方向に大きな幅を持つことが、リアルタイム処理の場合は問題となります。従って、フィルタの形状等に何らかの工夫が必要になります。

まとめ

調波打楽器音分離のLibROSAでの実装を中心に解説しました。

*1:(熟練の)人間の場合、そういった信号を無視して所望の音を聞くことができます

*2:HPS: Hermonic/Percussive Seperationと呼ばれることもあります

*3:時間周波数領域における楽曲信号の振幅/パワースペクトログラムを入力として与えます