独立成分分析による音源分離

独立成分分析 (Independent component analysis : ICA) によって、混合音源の分離を試みた。

STEP1

2つの独立音源$\boldsymbol{s_1}$, $\boldsymbol{s_2}$を用意する。

STEP2

$\boldsymbol{s_1}$, $\boldsymbol{s_2}$の音量をランダムな強さ $\boldsymbol{R}$によって加法合成した2つの合成音源$\boldsymbol{x_1}$, $\boldsymbol{x_2}$をつくる。これを観測音源と見立てて分離することを目指す。

STEP3

ICA の手法に基づいて観測音源$\boldsymbol{x_1}$, $\boldsymbol{x_2}$に含まれている独立音源$\boldsymbol{s_1}$, $\boldsymbol{s_2}$を推定して音源を分離し、分離音源$\boldsymbol{\hat{s_1}}$, $\boldsymbol{\hat{s_2}}$を得る。

ICA の手法としては、尖度 (kurtosis) 最大を探索する方法を使用する。 すなわち$\boldsymbol{x_1}$, $\boldsymbol{x_2}$について音量バランス$\boldsymbol{C}$を変化させながら合成音源を生成し、その音声音源の尖度が最大となる音量バランスを探索する。 尖度が最大となった時の合成音源は$\boldsymbol{x_1}$, $\boldsymbol{x_2}$間の独立性が最も高い状態であるから、 その時の音量バランスを$\boldsymbol{x_1}$, $\boldsymbol{x_2}$に掛けあわせた音源こそが求める分離音源$\boldsymbol{\hat{s_1}}$, $\boldsymbol{\hat{s_2}}$である。

尖度が信号間の独立性を評価する指標として有効な理由は参考文献 [1] および [3] が詳しい。

参考

事前準備

In [1]:
import pyaudio
import matplotlib.pyplot as plt
import numpy as np
import scipy.io.wavfile as wav
from scipy.stats import kurtosis
from IPython.display import display, Audio
%matplotlib inline
In [2]:
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 1024
RECORD_SECONDS = 5
In [3]:
# 録音用の関数を定義
def recording():
    audio = pyaudio.PyAudio()

    # start Recording
    stream = audio.open(format=FORMAT,
                        channels=CHANNELS,
                        rate=RATE, input=True,
                        frames_per_buffer=CHUNK)
    frames = []
    for i in range(0, int(RATE / CHUNK * RECORD_SECONDS)):
        data = stream.read(CHUNK)
        frames.append(data)

    stream.stop_stream()
    stream.close()
    audio.terminate()
    return frames

STEP1

2つの独立音源$\boldsymbol{s_1}$, $\boldsymbol{s_2}$を用意する。

録音するか、または録音済みの音声ファイルを読み込んで、2つの独立音源$\boldsymbol{s_1}$, $\boldsymbol{s_2}$の標本配列を得る。

In [4]:
# record and write
# s1 = np.fromstring(b''.join(recording()), np.int16)
# wav.write('data/s1.wav', RATE, s1)

# read second sample
s1 = wav.read('data/s1.wav')[1]

plt.xlim(len(s1))
plt.title('time series of first sample')
plt.plot(s1)

display(Audio(s1, rate=RATE))
In [5]:
# record and write
# s2 = np.fromstring(b''.join(recording()), np.int16)
# wav.write('data/s2.wav', RATE, s2)

# read second sample
s2 = wav.read('data/s2.wav')[1]

plt.title('time series of second sample')
plt.xlim(len(s2))
plt.plot(s2)

display(Audio(s2, rate=RATE))