#PythonでMIDIを扱う
音楽情報処理をするときにMIDIを扱うことがあります。
MIDIとはいわゆる楽譜の情報が載ったファイルです。つまり、どのタイミングでどの音をだすのか、というようなことが書かれています。
Pythonを使うにあたって、MIDIを処理するパッケージがあるので説明します。
midiとpretty_midiです
これらのパッケージはMagentaという人工知能で作曲を行ったりするプロジェクトでも使われています。
#インストール
ここではPython2を使います。
pipでインストールできるようです(2016/11/22訂正)
pip install python-midi
pip install pretty_midi
インポートができれば完了です。
import midi
import pretty_midi
Python3ではgithubからcloneしてインストールすれば、pretty_midiを使えます。(pipでやるとimport時にエラーが出ます)
git clone https://github.com/craffel/pretty-midi
cd pretty-midi
python setup.py install
midiのほうはここにPython3バージョンがあります。
#midi
MIDIファイルは基本的にトラックとイベントというものなどから構成されます。
トラックは楽器ごとの演奏データが入っています。
そして演奏データはイベントから構成されます。イベントとはどのタイミングでテンポを変更するかや、どのタイミングで音を鳴らし始める(終える)かといった情報のことです。
それらを直接記述するのがmidiです。
それでは、ソの四分音符の演奏情報が入ったmidiファイルを作ります。
import midi
file = "test.mid"
pattern = midi.Pattern(resolution=960) #このパターンがmidiファイルに対応しています。
track = midi.Track() #トラックを作ります
pattern.append(track) #パターンに作ったトラックを追加します。
ev = midi.SetTempoEvent(tick=0, bpm=120) #テンポを設定するイベントを作ります
track.append(ev) #イベントをトラックに追加します。
e = midi.NoteOnEvent(tick=0, velocity=100, pitch=midi.G_4) #ソの音を鳴らし始めるイベントを作ります。
track.append(e)
e = midi.NoteOffEvent(tick=960, velocity=100, pitch=midi.G_4) #ソの音を鳴らし終えるイベントを作ります。
track.append(e)
eot = midi.EndOfTrackEvent(tick=1) #トラックを終えるイベントを作ります
track.append(eot)
midi.write_midifile(file, pattern) #パターンをファイルに書き込みます。
今度はmidiファイルを読み込みます
pattern = midi.read_midifile("test.mid")
print(pattern)
以下の情報が表示されます。これらは先ほど書いた情報と同じものです。
midi.Pattern(format=1, resolution=960, tracks=\
[midi.Track(\
[midi.SetTempoEvent(tick=0, data=[7, 161, 32]),
midi.NoteOnEvent(tick=0, channel=0, data=[55, 100]),
midi.NoteOffEvent(tick=960, channel=0, data=[55, 100]),
midi.EndOfTrackEvent(tick=1, data=[])])])
#pretty_midi
pretty_midiはさらにわかりやすくなっています。
pretty_midiでも同じことができてこちらの方が短いコードでできます。
同じようにmidiファイルを作ります。
import pretty_midi
pm = pretty_midi.PrettyMIDI(resolution=960, initial_tempo=120) #pretty_midiオブジェクトを作ります
instrument = pretty_midi.Instrument(0) #instrumentはトラックみたいなものです。
note_number = pretty_midi.note_name_to_number('G4')
note = pretty_midi.Note(velocity=100, pitch=note_number, start=0, end=1) #noteはNoteOnEventとNoteOffEventに相当します。
instrument.notes.append(note)
pm.instruments.append(instrument)
pm.write('test.mid') #midiファイルを書き込みます。
(注)エラーが起こった時は
note = pretty_midi.Note(velocity=100, pitch=note_number, >start=1e-20, end=1) #noteはNoteOnEventとNoteOffEventに相当します。
と書き換えてください。これはPrettyMIDIのメソッドtime_to_tickが入力0の時のみになぜか、numpy.int64型で返してしまうことに起因します。(他の場合はint型で返します)
midiファイルを読み込みます
import pretty_midi
midi_data = pretty_midi.PrettyMIDI('test.mid') #midiファイルを読み込みます
print(midi_data.get_piano_roll()) #ピアノロールを出力します
print(midi_data.synthesize()) #サイン波を使って、波形を出力します。
このほかにも便利な関数があるみたいです。
#まとめ
基本的にPythonでmidiファイルを扱うときはpretty_midiを使った方が良さそうです。