Mido

midoはMIDIを扱うことができるpythonライブラリです。
このライブラリを使うことでMIDIファイルの読み書きが可能です。今回はpython3で音を指定してMIDIファイルを作成するmidoの使い方についてメモしていきます。

基本形

まずはこのドキュメント通りに出力して、noteの値を変えて音が変わることを確認してみましょう。
MIDI Files — Mido 1.2.10 documentation
import mido
from mido import Message, MidiFile, MidiTrack

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)

# MIDIの音色を指定
track.append(Message('program_change', program=12, time=0))
# C3(note=60, ド)の音を、指定の音量と時間で再生
track.append(Message('note_on', note=60, velocity=64, time=32))
track.append(Message('note_off', note=60, velocity=127, time=32))

mid.save('new_song.mid')
※Messageに指定する値と範囲はこちらのドキュメントを参照してください。
Message Types — Mido 1.2.10 documentation

テンポを指定するにはset_tempoを使います。4分音符の長さを480tickとして、tick数により長さを指定します。
テンポを指定しつつ、いくつかの音を出してみましょう。
import mido
from mido import Message, MidiFile, MidiTrack, MetaMessage

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)
track.append(MetaMessage('set_tempo', tempo=mido.bpm2tempo(120)))

track.append(Message('note_on', note=60, velocity=64, time=0))
track.append(Message('note_off', note=60, time=480))
track.append(Message('note_on', note=60+4, velocity=64, time=0))
track.append(Message('note_off', note=60+4, time=480))
track.append(Message('note_on', note=60+7, velocity=64, time=0))
track.append(Message('note_off', note=60+7, time=480))

mid.save('new_song.mid')
このような内容のmidiが出力されます。
sample2_midi

和音の出し方

音を重ねるにはこのように指定します。
import mido
from mido import Message, MidiFile, MidiTrack, MetaMessage

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)
track.append(MetaMessage('set_tempo', tempo=mido.bpm2tempo(120)))

track.append(Message('note_on', note=64, velocity=60, time=0))
track.append(Message('note_on', note=64+4, velocity=60, time=0))
track.append(Message('note_on', note=64+7, velocity=60, time=0))
track.append(Message('note_off', note=64, time=480))
track.append(Message('note_off', note=64+4, time=0))
track.append(Message('note_off', note=64+7, time=0))

mid.save('new_song.mid')
sample3_midi

複数のトラックの作成

トラックを複数用意し、それぞれに音を入れていきましょう。
import mido
from mido import Message, MidiFile, MidiTrack, MetaMessage

mid = MidiFile()
track1 = MidiTrack()
track2 = MidiTrack()
mid.tracks.append(track1)
mid.tracks.append(track2)
track1.append(MetaMessage('set_tempo', tempo=mido.bpm2tempo(120)))
track2.append(MetaMessage('set_tempo', tempo=mido.bpm2tempo(120)))

track1.append(Message('note_on', note=64, velocity=60, time=0))
track1.append(Message('note_on', note=64+7, velocity=60, time=0))
track1.append(Message('note_off', note=64, time=480))
track1.append(Message('note_off', note=64+7, time=0))

track2.append(Message('note_on', note=64+4, velocity=60, time=40))
track2.append(Message('note_off', note=64+4, time=480))

mid.save('new_song.mid')

複雑な音階

midoはプログラムの中で扱えるので、このように適当に変数を使いつつ組み立てていくことになるでしょう。今回はバッハ 平均律クラヴィーア曲集 第1巻 みたいな音を作ります。
ただしコードを見て分かるように、読みやすいものを作るのは難易度が高そうです。
import mido
from mido import Message, MidiFile, MidiTrack, MetaMessage

mid = MidiFile()
track = MidiTrack()
mid.tracks.append(track)
track.append(MetaMessage('set_tempo', tempo=mido.bpm2tempo(120)))

node_time = 480 * 2
node_pos = 60
node_16 = int(node_time/8)

track.append(Message('note_on', note=node_pos, velocity=60, time=0))
track.append(Message('note_on', note=node_pos+4, velocity=60, time=node_16 ))
track.append(Message('note_on', note=node_pos+7, velocity=60, time=node_16 ))
track.append(Message('note_off', note=node_pos+7, velocity=60, time=node_16 ))
track.append(Message('note_on', note=node_pos+12, velocity=60, time=0 ))
track.append(Message('note_off', note=node_pos+12, velocity=60, time=node_16 ))
track.append(Message('note_on', note=node_pos+16, velocity=60, time=0 ))
track.append(Message('note_off', note=node_pos+16, velocity=60, time=node_16 ))
track.append(Message('note_on', note=node_pos+7, velocity=60, time=0 ))
track.append(Message('note_off', note=node_pos+7, velocity=60, time=node_16 ))
track.append(Message('note_on', note=node_pos+12, velocity=60, time=0 ))
track.append(Message('note_off', note=node_pos+12, velocity=60, time=node_16 ))
track.append(Message('note_on', note=node_pos+16, velocity=60, time=0 ))
track.append(Message('note_off', note=node_pos+16, velocity=60, time=node_16 ))
track.append(Message('note_off', note=node_pos+4, velocity=60, time=0 ))
track.append(Message('note_off', note=node_pos, velocity=60, time=0))

mid.save('new_song.mid')
このような内容が出力されます。
sample4_midi
これを使った作例がこちらです。
バッハの平均律クラヴィーアみたいのをランダム生成する - えんたつの記録

このようにMidoはプログラムでMidi生成することにも活用できます。