music21でMIDIを扱う

# はじめに
今回はmusic21のを用いてMIDIファイルの情報を抽出したり、操作したりする基本的な操作を紹介します。

### MIDIファイルとは？
MIDIは、**楽曲の演奏データが保存されたファイル形式**のことです。
みなさんがイメージする音楽のファイル形式はmp3やwavが一般的だと思いますが、それらは演奏データではなく音声データです。
MIDIを演奏したときに出力される音声がmp3やwavになります。


### music21とは？
music21は、**pythonで音楽を扱うための音楽情報処理ライブラリ**です。
pythonでMIDIを扱う場合、他のモジュールとしてpretty_midiなどがありますが、MIDIファイルは情報が特殊なオブジェクトで保存されているので通常は扱いにくいことがあります。
music21はMIDIを読み取るだけでなく、作譜をしたりすることもできるので音楽処理には非常に優秀なライブリラリです。
インストールと環境構築に関してはこちらの記事がおすすめです。

https://qiita.com/nofrmm/items/c3662555b145f6b42d92


# MIDIを読み込む
まずはインポート
```diff_python
import music21 as m21
```

MIDIファイルを読み込みます。今回は**かえるのうた**の冒頭の演奏データが入ったMIDIを読み込みます。

```diff_python
path = './frog.mid'
frog = m21.converter.parse(path)
print(type(frog))

## <class 'music21.stream.base.Score'>
```
```converter.parse()```を使うと、**stream**というオブジェクトに変換できます。
**stream**は楽譜データを格納できるオブジェクトになります。

#### MIDIファイルを使いたい場合はこちら

https://drive.google.com/file/d/1lHZioAXqnStv8cFpsMc1vpn24_HDjxHC/view?usp=sharing

# MIDIの可視化
```show()```を使うとMIDIに格納されたデータをさまざまな形で可視化できます。
今回はその一例を紹介します。

### 楽譜
```diff_python
frog.show()
```
![frog.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/a961475c-0830-3c7d-aa23-0955d33c6cb9.png)


### 再生
専用の再生ディスプレイが出てきます。
`pygame`をインストールしないと動かないことがあるので、実行する際はインストールしておきましょう。
```diff_python
frog.show('midi')
```
![スクリーンショット 2023-02-01 0.31.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/910ba51b-90d0-05fc-4180-d4f8fdfe01cd.png)

### テキスト
MIDI内の情報の全様を```{オフセット} <オブジェクト名>```としてテキスト表示します。
**オフセット**とは、四分音符の長さを1.0（quarterLengthといいます）としたとき、曲が始まってからどの位置にあるかを示す指標です。
次の項目で詳細を説明します。
```diff_python
frog.show('text')

## {0.0} <music21.metadata.Metadata object at 0x11edb10f0>
## {0.0} <music21.stream.Part 0x12a5de470>
##    {0.0} <music21.stream.Measure 1 offset=0.0>
##        {0.0} <music21.clef.TrebleClef>
##        {0.0} <music21.tempo.MetronomeMark animato Quarter=120.0>
##        {0.0} <music21.meter.TimeSignature 4/4>
##        {0.0} <music21.instrument.Piano 'Piano'>
##        {0.0} <music21.note.Note C>
##        {1.0} <music21.note.Note D>
##        {2.0} <music21.note.Note E>
##        {3.0} <music21.note.Note F>
##    {4.0} <music21.stream.Measure 2 offset=4.0>
##        {0.0} <music21.note.Note E>
##        {1.0} <music21.note.Note D>
##        {2.0} <music21.note.Note C>
##        {3.0} <music21.note.Rest quarter>
##    {8.0} <music21.stream.Measure 3 offset=8.0>
##        {0.0} <music21.note.Note E>
##        {1.0} <music21.note.Note F>
##        {2.0} <music21.note.Note G>
##        {3.0} <music21.note.Note A>
##    {12.0} <music21.stream.Measure 4 offset=12.0>
##        {0.0} <music21.note.Note G>
##        {1.0} <music21.note.Note F>
##        {2.0} <music21.note.Note E>
##        {3.0} <music21.note.Rest quarter>
##        {4.0} <music21.bar.Barline type=final>
```

# streamの内部構造
```show('text')```で出力される内容はstreamの全体構造を表しています。
要約するとこんな感じになってます。
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/8d03f6ac-51b3-ca26-b903-95b6ad818b62.png">
**stream**は```list型```と同じような仕様になっているので、```append```や```pop``` を使って操作できます。
以下順に説明していきます。
### stream.metadata
※こちらはmusic21のver8.1のアップデートにより追加されたオブジェクトです。
曲のタイトルや作曲者名・作成日付などを格納するオブジェクトらしいのですが、今回は扱わないので詳しい説明は省きます。


### stream.Part
演奏パートのオブジェクトです。試しにかえるのうたに演奏パートを2つ追加してみましょう。
```diff_python
frog.append(m21.stream.Part())
frog.append(m21.stream.Part())

frog.show()
```
![frog2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/acb57b7f-d285-054e-553e-244a6cae015a.png)
 はい、追加されましたね。stream内では演奏パートごとに情報が格納されています。


### stream.Measure
小節のオブジェクトです。1小節ごとに情報が格納されています。
```diff_python
len(frog[1])

## 4
```
```frog[0]```は**stream.metadata**なので、```frog[1]```が演奏パートになります。
4小節あるので、```len```で数えると4になりますね。
こちらも試しに小節数を増やしてみましょう。
```diff_python
frog[1].append(m21.stream.Measure())
frog[1].append(m21.stream.Measure())

frog.show()
```
![frog3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/28b70744-9bd9-2641-d32e-23243aedeb91.png)
増えましたね。
終止線が動かないのは、かえるのうたの最後の小節に終止線のオブジェクトが残っているからですね。


### note.Note / note.Rest
音符と休符のことですね。かえるのうたの2小節目だけを```show()```で見てみましょう。
```diff_python
frog[1][1].show('text')

## {0.0} <music21.note.Note E>
## {1.0} <music21.note.Note D>
## {2.0} <music21.note.Note C>
## {3.0} <music21.note.Rest quarter>

frog[1][1].show()
```
![frog4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/25523b21-aac6-b4d8-5d47-e300337afc1e.png)
**stream.Measure**内にある最小オブジェクトのオフセットは、その小節が始まってからの位置を表します。
比較すると、楽譜通りの情報がテキストとして表示されているのがわかりますね。


### その他のオブジェクト
小節内の最小オブジェクトは音符と休符だけではありません。
かえるのうたの1小節目だけを```show()```で見てみましょう。
```diff_python
frog[1][0].show('text')

## {0.0} <music21.clef.TrebleClef>
## {0.0} <music21.tempo.MetronomeMark animato Quarter=120.0>
## {0.0} <music21.meter.TimeSignature 4/4>
## {0.0} <music21.instrument.Piano 'Piano'>
## {0.0} <music21.note.Note C>
## {1.0} <music21.note.Note D>
## {2.0} <music21.note.Note E>
## {3.0} <music21.note.Note F>

frog[1][0].show()
```
![frog5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/b56c8baf-278d-caf9-5bc9-ca774f9c9788.png)
音符以外の情報が入ってますね。これらの情報は```setup情報```として組み込まれます。
１小節目に必ず入っている情報で、あまりありませんが、曲の途中でテンポや拍子などが変化する箇所あると、その小節のオフセット```{0.0}```に組み込まれます。
今回は軽く触れておきます。

#### clef.Clef
音部記号。ト音記号やヘ音記号のことです。
#### tempo.MetronomeMark
テンポ。BPMのことです。
#### meter.TimeSignature
拍子。何拍子／何拍のような表記ができます。
#### instrument.Instrument
楽器。ピアノやヴァイオリン、トランペットなどにすることもできます。


# 情報の抽出と操作
さて、ここまでの内容を理解した上で情報を抜きとったり変えたりする基本的な操作を紹介します。


### キーの分析と変更
**stream**に対して```analyze('key')```を使うと、キーを解析して```key.Key```というオブジェクトで出力します。
この場合、```C```だけを取り出したい場合、```tonicPitchNameWithCase```を使います。
```diff_python
frog.analyze('key')

## <music21.key.Key of C major>

frog.analyze('key').tonicPitchNameWithCase

## 'C'
```
また、```transpose( ,inPlace=True)```を使うと、第一引数に入力した整数だけキーを半音上げ下げできます。
```diff_python
frog.transpose(-1, inPlace=True) # キーを半音下げる (Bメジャーになる)
frog.show()
```
![frog6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/e4c8c501-f42d-b968-e7f9-84a759e055e2.png)
```diff_python
frog.transpose(2, inPlace=True) # キーを半音上げる (Cメジャーに戻る)
frog.show()
```
![frog.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3092163/09d644a9-2e0b-f814-0ef0-6782ac354b0d.png)


### テンポの抽出
**stream**に対して```metronomeMarkBoundaries()```を使うと、stream内全てのテンポオブジェクトに関して```(開始オフセット, 終了オフセット, tempo.MetronomeMark```のタプルが入ったリストが出力されます。
**tempo.MetronomeMark**に対して```number```を使うと```float型```で取り出せます
```diff_python
frog.metronomeMarkBoundaries()

## [(0.0, 16.0, <music21.tempo.MetronomeMark animato Quarter=120.0>)]

tempo_metronomeMark = frog.metronomeMarkBoundaries()[0][2]
tempo_metronomeMark

## <music21.tempo.MetronomeMark animato Quarter=120.0>

tempo_metronomeMark.number

## 120.0
```


### 拍数・拍子の抽出
**meter.TimeSignature**に対し、```ratioString```を使うと、```/```の表記が入った文字列で出力され、```numerator```を使うと拍子（分子の数字）、```denominator```を使うと拍数（分母の数字）を出力します。
```diff_python
meter_TimeSignature = frog[1][0][2]
meter_TimeSignature

## <music21.meter.TimeSignature 4/4>

meter_TimeSignature.ratioString

## '4/4'

meter_TimeSignature.ratioString = '3/4'
meter_TimeSignature.numerator

## 3

meter_TimeSignature.denominator

## 4
```


### 音符や休符情報の抽出
**note.Note**に対して、```name```を使うと、音階(ABC表記)を出力できます。
また、```pitches[0].midi```を使うと、ノートナンバー(C4=60を基準とした音階の数値化)を出力できます。
```diff_python
for measure in frog[1]:
    for item in measure:
        if isinstance(item, m21.note.Note):
            print(item, item.name ,item.pitches[0].midi)

## <music21.note.Note C> C 60
## <music21.note.Note D> D 62
## <music21.note.Note E> E 64
## <music21.note.Note F> F 65
## <music21.note.Note E> E 64
## <music21.note.Note D> D 62
## <music21.note.Note C> C 60
## <music21.note.Note E> E 64
## <music21.note.Note F> F 65
## <music21.note.Note G> G 67
## <music21.note.Note A> A 69
## <music21.note.Note G> G 67
## <music21.note.Note F> F 65
## <music21.note.Note E> E 64
```
全てのオブジェクトに対し、```offset```を使うと、そのオブジェクトのオフセットを出力します。また、**note.Note**、**note.Rest**に対して```quarterLength```を使うと、四分音符を1.0としたときにどれぐらいの長さの音符・休符なのかを示す指標を出力します。
```diff_python
for measure in frog[1]:
    for item in measure:
        print(item.offset, item.quarterLength, item)

## 0.0 0.0 <music21.clef.TrebleClef>
## 0.0 0.0 <music21.tempo.MetronomeMark animato Quarter=120.0>
## 0.0 0.0 <music21.meter.TimeSignature 3/4>
## 0.0 1.0 <music21.note.Note C>
## 0.0 0.0 Piano
## 1.0 1.0 <music21.note.Note D>
## 2.0 1.0 <music21.note.Note E>
## 3.0 1.0 <music21.note.Note F>
## 0.0 1.0 <music21.note.Note E>
## 1.0 1.0 <music21.note.Note D>
## 2.0 1.0 <music21.note.Note C>
## 3.0 1.0 <music21.note.Rest quarter>
## 0.0 1.0 <music21.note.Note E>
## 1.0 1.0 <music21.note.Note F>
## 2.0 1.0 <music21.note.Note G>
## 3.0 1.0 <music21.note.Note A>
## 0.0 1.0 <music21.note.Note G>
## 1.0 1.0 <music21.note.Note F>
## 2.0 1.0 <music21.note.Note E>
## 3.0 1.0 <music21.note.Rest quarter>
## 4.0 0.0 <music21.bar.Barline type=final>
```
こんな感じにすれば○分音符・休符表記ができますね。
```diff_python
for measure in frog[1]:
    for item in measure:
        if isinstance(item, m21.note.Note):
            print(str(4/item.quarterLength) + '分音符', item)
        if isinstance(item, m21.note.Rest):
            print(str(4/item.quarterLength) + '分休符', item)

## 4.0分音符 <music21.note.Note C>
## 4.0分音符 <music21.note.Note D>
## 4.0分音符 <music21.note.Note E>
## 4.0分音符 <music21.note.Note F>
## 4.0分音符 <music21.note.Note E>
## 4.0分音符 <music21.note.Note D>
## 4.0分音符 <music21.note.Note C>
## 4.0分休符 <music21.note.Rest quarter>
## 4.0分音符 <music21.note.Note E>
## 4.0分音符 <music21.note.Note F>
## 4.0分音符 <music21.note.Note G>
## 4.0分音符 <music21.note.Note A>
## 4.0分音符 <music21.note.Note G>
## 4.0分音符 <music21.note.Note F>
## 4.0分音符 <music21.note.Note E>
## 4.0分休符 <music21.note.Rest quarter>
```

# 終わりに
music21での演奏データの操作と処理の優秀さを実感しました。
今回はほんの一部を紹介しましたが、music21はこれだけではなくまだまだ更なる可能性を秘めています。
ぜひ興味のある方は自分なりに遊んでみてはいかがでしょう。
ここまで読んでくださりありがとうございました。

**参考公式ドキュメント**

https://web.mit.edu/music21/doc/index.html