Wizard Notes

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

Python:LibROSAのLPC (線形予測分析)を使って音声・歌声分析

Pythonの音楽分析モジュールLibROSAには、v.0.7から線形予測分析(https://ja.wikipedia.org/wiki/%E7%B7%9A%E5%BD%A2%E4%BA%88%E6%B8%AC%E6%B3%95)を行う関数librosa.lpcが追加されました。

線形予測分析は音声の音素や声色を分析するのによく利用されています。

今回は、このlibrosa.lpcの紹介と、利用例として歌声に適用してみたいと思います。

librosa.lpcの仕様

librosa/audio.py at main · librosa/librosa · GitHub

a = librosa.lpc(y, order)

  • y: np.array
    • モノラル信号(時間領域)
  • order: int
    • 線形予測次数
    • 線形予測分析で抽出する周波数スペクトル包絡の細かさを操作する
      • 大きい値になるほど、より細かい包絡成分を抽出する
    • サンプリング周波数にも依るが、12~24くらいがよく使われている
  • a: np.array
    • 線形予測係数
    • この係数をIIRフィルタの分母項の係数とすることで、そのフィルタは分析音源の周波数包絡を表す

librosa.lpcの実行例

分析する音源は、以下の合成音声(初音ミク)を使いました。

まず、librosa.display.specshowを使って2次元プロットしてみます。すると音声の音素ごとに線形予測係数のIIRフィルタの周波数応答が変わっていることが確認できます。

もう少し分かりやすい例として、各時間のスペクトル包絡を動画にしてみました。

youtu.be

この結果から、音声の音素変化とともに周波数スペクトル包絡が変化し、また、同じ音素では周波数スペクトル包絡が似ていることが確認できます*1

以上より、LibROSAの線形予測分析モジュールlibrosa.lpcを使うことで歌声の音素成分が分析できることを確認しました。

ソースコード

filepath = "audio/miku_doremi120.wav"
sr = 16000
y, _ = librosa.load(filepath, sr=sr, mono=True)

# パラメタ
order        = 12 #線形予測次数
frame_length = 320 #線形予測する信号長(フレーム長)
length       = y.shape[0]
n_frame      = length // frame_length

worN         = 513 #周波数応答プロット用
envelope     = np.zeros((worN, n_frame))
eps          = 1e-3

for k in range(n_frame):
    slc = slice(k*n_frame, (k+1)*n_frame)
    print(k)
    try:
        a = librosa.lpc(y[slc], order)
        freqs, h = scipy.signal.freqz(1.0, a, worN=worN)
        envelope[:,k] = np.abs(h)  
    except FloatingPointError:
        pass
        
env_min = -120
with np.errstate(divide='ignore'):
    envelope = 20 * np.log10(envelope/np.max(envelope))
envelope[envelope<env_min] = env_min

plt.clf()
librosa.display.specshow(
    envelope, 
    sr=sr, 
    hop_length=frame_length,
    x_axis='time', y_axis='log',
    cmap="jet"
)
plt.colorbar(format='%+2.0f dB')
plt.title(f"Spect

*1:なお、LPCでは基音が高い音声の分析は苦手としており、最後の方は基音が高いためFoにLPCが引っ張られていることも確認できます。ラグ窓かけたりしたら多少はマシになるかもしれません