"エンジニア"の目線でアコーディオンを修理する～librosaで複数音の分析～

## 前回までのあらすじ

壊れたアコーディオンを買った．[^1]
アコーディオンを修理する技能や知識はないので，音響処理を用いて演奏可能な状態に持っていこうと思う．

実際に演奏してみて，壊れた状態を確認した．
鍵盤を押すことなく蛇腹を動作させると，常に一定の音が鳴っており
蛇腹を開く時と閉じる時で別の音が鳴っていることがわかった．

https://qiita.com/Killinneko/items/39faf941688b8e42cd80

pythonの`librosa`を用いて，蛇腹を開く時と閉じる時で鳴っている音を分析することにした．
蛇腹を開く時は，単音が鳴り，閉じるときは複数の音が鳴っている．

蛇腹を開くときの単音は，**D (レ)の音かつ294Hz**が鳴っていることが分かった.
閉じる時になる複数音は，294Hzと何かしらの音が同時に鳴っているが，
それが何の音なのかわからなかった．

https://qiita.com/Killinneko/items/16a7a7604f5a9dc69248

今回は，蛇腹を閉じる時に鳴っている音を分析する．

## 現状

https://soundcloud.com/killinneko/czqhtk61orwh/s-aHrGpt0132y?si=c980bad90a2e4808bf50b0eb733ea127&utm_source=clipboard&utm_medium=text&utm_campaign=social_sharing

前半部分が蛇腹を開いたとき，後半部分が蛇腹を閉じたときの音である．
聞いてみるとわかる通り，前半と後半で鳴っている音は異なり，
後半は，前半の音に加えて何かしらの音が鳴っている．

単音分析と同じ方法は取れないことは前回わかったので，
今回は，一工夫して音を分析する．

言語はpython(jupyterNotebook)を利用し，
ライブラリのバージョンはこんな感じ．
```
librosa :  0.10.1
numpy :  1.23.5
SciPy :  1.10.0
Matplotlib :  3.7.0
```

## ノイズ処理：同じ音が鳴っているなら消せばいいじゃない

後半の音から前半の音を消せば，残った音は，分析したい音なのでは無いかという算段．
ノイズ処理で用いられる処理を使って消してみる．
．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3600683/185d2e81-8284-f874-b828-7ac2751d49e3.png)

### 実装

```python:ライブラリ読み込み
import librosa
import numpy as np
from scipy.io import wavfile
import matplotlib.pyplot as plt
```

```python:パワースペクトルの表示用関数
def plot_power(spectrogram):
    arr = np.average(spectrogram,axis=1)
    plt.plot(arr)
    plt.xscale('log')

    # グラフのタイトルとラベルを設定
    plt.title('パワースペクトル',fontproperties=japanese_font)
    plt.xlabel('周波数',fontproperties=japanese_font)
    plt.ylabel('パワー',fontproperties=japanese_font)

    # グラフを表示
    plt.show()
```

```python:メイン
filter_filepath = 'wav/non.wav' 
filter_start_time = 1
filter_end_time = 3

main_filepath =  'wav/non.wav' 
main_start_time =  5
main_end_time = 7


# 演奏データの読み込み
filter_audio, filter_sr = librosa.load(filter_filepath, offset=filter_start_time, duration=filter_end_time - filter_start_time)
main_audio, main_sr = librosa.load(main_filepath, offset=main_start_time, duration=main_end_time - main_start_time)

# スペクトログラムの取得
filter_spec = np.abs(librosa.stft(filter_audio))
main_spec = np.abs(librosa.stft(main_audio))

# スペクトログラムの引き算
filtered_spec = np.clip(main_spec  - filter_spec,a_min=0, a_max=None)

plot_power(filtered_spec)

# スペクトルで処理したスペクトログラムからwavを作成
filtered_audio = librosa.istft(filtered_spec)

# 音声を保存する
wavfile.write('filtered_audio.wav', main_sr, filtered_audio)
```

### 動作

蛇腹を閉じる時の音から，開いた時の音を引き算するとこんな感じになった．

https://soundcloud.com/killinneko/5nstsxxbkika/s-Lep9xaQ5JhW?si=b9c7424d3f0f4af08c98e22ebc7e513c&utm_source=clipboard&utm_medium=text&utm_campaign=social_sharing

処理した音を前回作成した，音階と基本周波数を取得するプログラムに入れてみる．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3600683/58d673dd-3638-c1b4-65d3-786912815202.png)

結果として出力されたのは，**A(ラ)で，43Hz**だった．これも多分違う...
試しに43Hzを聞いてみる．

手軽に正弦波を指定して聞けるブラウザアプリがあるので利用させてもらう．

https://www.pg-fl.jp/music/hzmaker.htm

確かに修正音に近い...

次に，蛇腹を閉じる際の音に近いか比較する．
2つの音を指定して`Play All`を実行する．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3600683/103eed8e-388c-bfa3-82a6-0bbec6e7e529.png)

「全然違う！」

### うまくいかない理由

上手く抽出できなかったのは，音響信号処理の複雑さにあると思う．
蛇腹を閉じる時に複数の音が鳴っていると，正確にそれを分離することは，上記で実装したような簡単な処理では難しい．

正弦波をフーリエ変換すると，特定の周波数にのみピークが経つが，
実環境の音では，特定の周波数に加えて音色を表す成分や空調ノイズなどが含まれる．
そういった他の要素を考慮せずに直接引き算したので，
特定の周波数のピークが抑えられて，情報として無くなってしまったと思われる．

## まとめ

壊れたアコーディオンの蛇腹を閉じる時に鳴る音を，`librosa`で分析したが，
複数音を正確に分離させて分析することはできなかった．
複数の音が鳴っている状態を，単純にスペクトルの差分をとって分離することはできないことが分かった．

簡単な実装では難しいので，機械学習の導入かパワープレイで周波数を当てに行こうと思う．

[^1]: 本記事はリアルタイムで進行しているため，完成まで行かない可能性もあります.
ただ，失敗した用例も大事だと思うのでやったことは残そうと思います．