pythonのlibrosaでサクッと音声波形を表示する

# librosa

librosaは音声処理・音楽情報処理を行うときに使えるpythonのpackageです。

手っ取り早くmp3音源の波形を眺めたいなと考えたときに[こちら](https://qiita.com/tackey/items/225fdb5f07f2033377fa)の記事を見つけて、手軽そうなので試してみました。

2016年当時の記事を見てコードを書くと `AttributeError: module 'librosa' has no attribute 'display'` エラーが出てハマるので、解決方法を書きます。


## TL;DR

librosaはversion 0.5.0から displayモジュールを読み込まなくなりました。displayモジュールを利用する場合には明示的にimportする必要があります。

```python
import librosa
import librosa.display # この行が必要

a, sr = librosa.load('test.mp3')
librosa.display.waveplot(a, sr)
```

## 環境

```
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.14.2
BuildVersion:	18C54
$ python --version
Python 3.7.2
$ pip list | grep librosa
librosa      0.6.3
```

次のようなコードとmp3ファイルを用意します。

```python:main.py
import librosa

a, sr = librosa.load('test.mp3')
librosa.display.waveplot(a, sr)
```

## 実行

```bash
$ python main.py
Traceback (most recent call last):
  File "main.py", line 10, in <module>
    main()
  File "main.py", line 6, in main
    librosa.display.waveplot(a[0])
AttributeError: module 'librosa' has no attribute 'display'
```

librosa.displayなんて無いよとエラーが出ました。

## 確認

実際にlibrosa.displayが存在しないのか確認してみます。

```python
$ python
Python 3.7.2 (default, Feb 12 2019, 08:15:36)
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import librosa
>>> dir(librosa)
['A_weighting', 'LibrosaError', 'ParameterError', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', '__version__', 'amplitude_to_db', 'audio', 'autocorrelate', 'beat', 'cache', 'chirp', 'clicks', 'constantq', 'core', 'cqt', 'cqt_frequencies', 'db_to_amplitude', 'db_to_power', 'decompose', 'dtw', 'effects', 'estimate_tuning', 'feature', 'fft_frequencies', 'fill_off_diagonal', 'filters', 'fmt', 'frames_to_samples', 'frames_to_time', 'get_duration', 'harmonic', 'hybrid_cqt', 'hz_to_mel', 'hz_to_midi', 'hz_to_note', 'hz_to_octs', 'icqt', 'ifgram', 'iirt', 'interp_harmonics', 'istft', 'load', 'magphase', 'mel_frequencies', 'mel_to_hz', 'midi_to_hz', 'midi_to_note', 'note_to_hz', 'note_to_midi', 'octs_to_hz', 'onset', 'output', 'pcen', 'perceptual_weighting', 'phase_vocoder', 'piptrack', 'pitch', 'pitch_tuning', 'power_to_db', 'pseudo_cqt', 're', 'resample', 'salience', 'samples_like', 'samples_to_frames', 'samples_to_time', 'segment', 'sequence', 'show_versions', 'spectrum', 'stft', 'tempo_frequencies', 'time_frequency', 'time_to_frames', 'time_to_samples', 'times_like', 'to_mono', 'tone', 'util', 'version', 'warnings', 'zero_crossings']
```

ちょっと横に長くなってしまって見づらいですが、確かにlibrosaはdisplay attributeを持っていません。

実際に読み込まれているコードを見ると、[このあたり](https://github.com/librosa/librosa/blob/acd4e6fbd5b3b361e8c2e89797cfe3eb0b1a3aba/librosa/__init__.py#L12)でサブモジュールを読み込んでいます。しかしdisplayをimportしている様子はありません。

```python
# And all the librosa sub-modules
from ._cache import cache
from . import core
from . import beat
from . import decompose
from . import effects
from . import feature
from . import filters
from . import onset
from . import output
from . import segment
from . import sequence
from . import util
```

公式ドキュメントを探してみてもそれらしい原因は見当たらりません。

## 原因

なにがあったかというと、[このコミット](https://github.com/librosa/librosa/commit/3505a28ed1485f2ad4d8ed36c35030287a0c906f)で読み込まないように変更があったようです。

そのあたりの議論が[このissue](https://github.com/librosa/librosa/issues/343)でされています。

> We use librosa for signal processing only and use our own visualization tools when needed, but in production, where no visualization in python is needed, we really don't want to install and import matplotlib.

> (翻訳)私達はlibrosaを信号処理用途でだけ使っていて、可視化する必要があるときは別のツールを適宜使っている。可視化の必要がないときにもmatplotlibがimportされるなんてごめんだ。

議論からの引用ですが、これに尽きるかなと思います。

この議論の結果、 以前は次のように一行でdisplay submoduleまで読み込んでいたのですが、

```python
import librosa
```

librosa.displayを利用する際には2行importが必要になりました。
librosaで可視化を利用しない場合には2行目は不要です。

```python
import librosa
import librosa.display # この行が必要
```

この変更は v0.5.0から反映されています。
librosaのversionはpipで確認できます。

```bash
$ pip show librosa | grep Version
Version: 0.6.3 # 0.5.0以上
```

## 波形を表示

最終的にはこんな感じで、matplotlib.pyplot.show でグラフを表示させてやります。

```python
import librosa
import librosa.display

a, sr = librosa.load('test.mp3')
librosa.display.waveplot(a, sr=sr)

import matplotlib.pyplot as plt
plt.show()
```

![figure1.png](https://qiita-image-store.s3.amazonaws.com/0/266939/c4ee2375-a7c5-52ee-ba93-4bc279317a00.png)

できました✌

## 参考

https://qiita.com/tackey/items/225fdb5f07f2033377fa
https://librosa.github.io/librosa/
https://github.com/librosa/librosa