2021年07月

Python MidoでのMIDIファイル生成方法メモ

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生成することにも活用できます。

バッハの平均律クラヴィーアみたいのをランダム生成する

 バッハの「平均律クラヴィーア曲集 第1巻 第1番 ハ長調」の冒頭のパターンをランダムに繰り返したらどうなるか?という発想で音楽を生成しました。

・音のパターンは、冒頭に出てくる3パターンの音の動きをランダムに使用しています。
・そのパターンを再生する際の基準となる音は一定範囲内でランダムに生成しています。

 今回の生成結果はこちらです。
 映像はMIDITrailにより生成しています。繋がりの部分は、パターンをもっと都合よく選べばより自然にな流かもしれません。

棋譜クラッピング

棋譜クラッピング

棋譜音楽の1つとして、クラッピング(拍手)で棋譜を表現した作品を公開しました。左右から違う音が出るためヘッドフォン推奨です。

仕組み

棋譜の三,七,五行目を1手ずつ9音の拍手で表現しました。例えば三行目なら三1〜三9にかけて駒が存在するなら手拍子を叩き、駒が存在しなければ音を出しません。
出力しているのは三,五,七行目で、それぞれ以下の場所から聞こえるようになっています。
右耳 ... 三行目
左耳 ... 七行目
中央 ... 五行目

三,五,七行目を選んだのは、三と七はゲーム開始時には整列しているが勝負のために動き続けるのが面白く、五はどちらのプレイヤーにも属さない領域として選択しています。
それぞれの音を区別して感じるため、三行目と五行目は左右それぞれ片方の耳から音が出るようにし、五行目は拍手の音を少し変えて中央から出るようにしています。

音について

音としては手拍子を採用しました。
今回は棋譜の1手から9個の音が出ます。そして次の小節では1つもしくは2つだけ音が変わっていきます。このように少しづつ音が変わっていくのはミニマルミュージックだったので音はシンプルにしました。
このあたりはスティーヴ・ライヒのClapping Musicに影響を受けています。

音の作り方はタモリ倶楽部でやってた偶然日常音楽の影響を強く受けています。対象データへの考え方が異なりますが表現方法はほぼ同一です。情報から見えるパターンを楽譜として読み取ることで音楽を作っています。
西村直晃 Nishimura NaoakiさんはTwitterを使っています 「譜面です。 https://t.co/v5HXkPfxoj」 / Twitter

動画について

記譜データを1手ずつ読み取り、駒の場所を1、駒がない場所を0としてヒートマップとして解釈することで、画像を作成しています。画像の作成にはPythonのseabornを使っています。
アニメーションが細かくなったため、画像は1584枚使っています。これらをffmpegで動画にし、そこに別に作成した音声を結合しています。
このサイトについて
Webアプリケーション開発のことや、iPhone・Android向けアプリ開発の話題が中心です。
管理:えんたつ twitter: @tattyamm
mimage
一部のリンクにはアフィリエイトが含まれます。
カテゴリ別アーカイブ
RSS
プログラミング本
古い本含めてメモです
iPhoneプログラミングUIKit詳解リファレンス iPhoneプログラミングUIKit詳解リファレンス Android Layout Cookbook アプリの価値を高める開発テクニック パーフェクトPHP (PERFECT SERIES 3) JavaプログラミングBlack Book 2nd Edition (Black Bookシリーズ)
表記
当サイトではGoogle Analyticsを使用しております。詳細はこちらを御覧ください