こちらの記事は2022年11月に部内向けに書いたブログを、外部公開向けに書き直したものです。
そのため不勉強な部分もあるかと思いますが、温かい目でご覧いただければ幸いです。
はじめに
こんにちは。koukawa_ppです。
唐突ですが、「一度はやってみたいな」と思うことはありますか?
僕はタイトルにもありますが、「任意のファイルを音楽にできないか」という野望を抱えながら、つい最近まで過ごしてきました。
とはいえ、「どうすれば任意のファイルを音楽にできるだろうか?」という壁にぶち当たります。
今回は自分なりにですが、任意のファイルをmidiファイルに変換するプログラムを作成しましたので、皆さんに紹介したいと思います。
なお、使ったファイルは こちらのGitHubリポジトリ に入れてあります。
環境
- Windows 10 Home 64bit
- Visual Studio Code
- Python 3.11.0(ここまで新しくなくても動作するはずです)
いきさつ
ファイルをもとにして、何かしらの他のものに変えるというのは案外やりやすいと思っています。
なぜならファイルのバイナリを用いれば、そこから他のものを作り出すということはかなり容易だからです。
適当な話、これらビットに何かしらの役割を付与して、そこからmidiファイルを作り出そうという発想はありました。
では、得られたそのビット列を、どのようにして音楽にするかということです。
もちろん適当にコードとメロディーとリズムを組み合わせるだけで、それを音楽というのはいささか気が引けます。
素晴らしい音楽になるかはさておき、とりあえず「こういう曲ありそうだな」レベルまでには引き上げる必要があるわけです。
そこでこのビット列から、どのようにそういったものを作成する努力をしたか、説明していきたいと思います。
バイトの振り当て方
概要
このファイルを作るにあたって、以下のようなメモを用意しました。
1バイト
ドラムのリズム設定
2バイト
バスドラムについて1
3バイト
バスドラムについて2
4バイト
バスドラムについて3
5バイト
バスドラムについて4
6バイト
バスドラムについて5
7バイト
フィルインで叩くドラムを設定1
8バイト
フィルインで叩くドラムを設定2
9バイト
フィルインで叩くドラムを設定3
10バイト
フィルインで叩くドラムを設定4
11バイト
(1小節目)リズムを選択する
12バイト
(1小節目)コードを選択する
13~20バイト
(1小節目)鳴らす音を設定
(鳴らさないところは無視)
21バイト
(2小節目)リズムを選択する
22バイト
(2小節目)コードを選択する
23~30バイト
(2小節目)鳴らす音を設定
(鳴らさないところは無視)
31バイト
(3小節目)リズムを選択する
32バイト
(3小節目)コードを選択する
33~40バイト
(3小節目)鳴らす音を設定
(鳴らさないところは無視)
41バイト
(4小節目)リズムを選択する
42バイト
(4小節目)コードを選択する
43~50バイト
(4小節目)鳴らす音を設定
(鳴らさないところは無視)
以上のような設定に則って、どのように作ることにしたのかということをざっくり説明します。
ドラムのリズム設定(1バイト目)
そもそもドラムっていうのは、適当に作ってもかっこよく聞こえます(経験上)。
ただしそれが叩けるかどうかは別問題と言ったところです。
今回作ったリズムでは、ハイハットと同時にフィルインも鳴っているので、この状況においては叩けないリズムも作られていると思います。
いつも僕が作るときには、基本なるべく生身の人間でも叩けるように作っています。
今回、一度決めたドラムは基本的に4小節単位で繰り返されます。
まず1バイト目では、おおよそドラムのリズムを設定しています。
ビット列については、以下のように決めています。
| 桁数 | 概要 |
|---|---|
| 1, 2, 3, 4 | ハイハットのリズムを選択する |
| 5, 6 | スネアドラムを鳴らすか(00…鳴らさない, 01…4拍目で鳴らす, 10…3拍目で鳴らす, 11…2,4拍目で鳴らす) |
| 7, 8 | 最初にクラッシュシンバルを鳴らすか(00で鳴らさない、他は鳴らす) |
ハイハットのリズムの選択方法については、後で説明します。
バスドラムについて(2~5バイト目)
バスドラムについては、以下のように決定しています。
2バイト目
| 桁数 | 概要 |
|---|---|
| 1, 2, 3 | 一拍目でバスドラムを鳴らすか(000で鳴らさない) |
| 4, 5, 6 | 二拍目でバスドラムを鳴らすか(000で鳴らさない) |
| 7, 8 | 一拍目裏でバスドラムを鳴らすか(00で鳴らさない) |
3バイト目
| 桁数 | 概要 |
|---|---|
| 1, 2, 3 | 三拍目でバスドラムを鳴らすか(000で鳴らさない) |
| 4, 5, 6 | 四拍目でバスドラムを鳴らすか(000で鳴らさない) |
| 7, 8 | 二拍目裏でバスドラムを鳴らすか(00で鳴らさない) |
4バイト目
| 桁数 | 概要 |
|---|---|
| 1, 2, 3 | 一拍目二つ目の十六分音符でバスドラムを鳴らすか(111で鳴らす) |
| 4, 5, 6 | 二拍目二つ目の十六分音符でバスドラムを鳴らすか(111で鳴らす) |
| 7, 8 | 三拍目裏でバスドラムを鳴らすか(00で鳴らさない) |
5バイト目
| 桁数 | 概要 |
|---|---|
| 1, 2, 3 | 三拍目二つ目の十六分音符でバスドラムを鳴らすか(111で鳴らす) |
| 4, 5, 6 | 四拍目二つ目の十六分音符でバスドラムを鳴らすか(111で鳴らす) |
| 7, 8 | 四拍目裏でバスドラムを鳴らすか(00で鳴らさない) |
6バイト目
| 桁数 | 概要 |
|---|---|
| 1, 2 | 一拍目四つ目の十六分音符でバスドラムを鳴らすか(11で鳴らす) |
| 3, 4 | 二拍目四つ目の十六分音符でバスドラムを鳴らすか(11で鳴らす) |
| 5, 6 | 三拍目四つ目の十六分音符でバスドラムを鳴らすか(11で鳴らす) |
| 7, 8 | 四拍目四つ目の十六分音符でバスドラムを鳴らすか(11で鳴らす) |
二つ目とか四つ目の十六分音符とは何かということですが、

上のような感じです。
このような表現方法は後でも出てきます。
このことから分かるように、バスドラムについては、鳴らす確率が以下のように決められていると分かります。
(ただし、ビットの並び方が同様に確からしいとき)
| バスドラムのインデックス | 確率 |
|---|---|
| 1 | 7/8 |
| 2 | 1/8 |
| 3 | 3/4 |
| 4 | 1/4 |
基本的に1拍目と3拍目が鳴りやすいように細工しています。
2つ目だけが鳴るということはかなり稀です。
フィルインで叩くドラムを設定(7~10バイト目)
ここでは、フィルインでどういった楽器を鳴らすかを決定します。
その表は以下の通りです。
基本的に4ビットで決定していることが分かると思います。
ただし、*はワイルドカードだと思ってください。
| ビット列 | 楽器 |
|---|---|
| 000* | スネアドラム |
| 001* | ハイ・トム |
| 010* | ハイ・ミッド・トム |
| 011* | ロー・ミッド・トム |
| 100* | ロー・トム |
| 101* | フロア・トム |
| 1100 | ライド・シンバル |
| 1101 | クローズド・ハイハット |
| 1110 | オープン・ハイハット |
| 1111 | 何も鳴らさない |
1/16の確率で何も鳴らない(休符になる)ということです。
そして、タイミングは以下の通りです。
7バイト目
| 桁数 | 概要 |
|---|---|
| 1, 2, 3, 4桁目 | 3拍目1個目の十六分音符で何を叩くか |
| 5, 6, 7, 8桁目 | 3拍目2個目の十六分音符で何を叩くか |
8バイト目
| 桁数 | 概要 |
|---|---|
| 1, 2, 3, 4 | 3拍目3個目の十六分音符で何を叩くか |
| 5, 6, 7, 8 | 3拍目4個目の十六分音符で何を叩くか |
9バイト目
| 桁数 | 概要 |
|---|---|
| 1, 2, 3, 4 | 4拍目1個目の十六分音符で何を叩くか |
| 5, 6, 7, 8 | 4拍目2個目の十六分音符で何を叩くか |
10バイト目
| 桁数 | 概要 |
|---|---|
| 1, 2, 3, 4 | 4拍目3個目の十六分音符で何を叩くか |
| 5, 6, 7, 8 | 4拍目4個目の十六分音符で何を叩くか |
メロディー・コード作り(11~50バイト)
めちゃくちゃ長いように見えますが、実際はそこまで大変ではありません。
というのもここはかなりズルしているからです。
内訳としては、
- 11~20が1小節目
- 21~30が2小節目
- 31~40が3小節目
- 41~50が4小節目
となっています。
ここでは、各バイトについて、10で割った余りのバイトについて、どのような役割を担っているか説明します。
10で割った余りが1のバイトについて
ここでは、メロディーのリズムを決定しています。
方法としては、323個のリズムパターンの中から、ビット列によってリズムを決定するというものです。
0~255までしか表現できないのに、323個あったって仕方ないと思われるかもしれませんが、確かにその通りです。
とはいえ、ここでどう考えても選ばれなかったリズムについては、そこまでということにしてあります。
10で割った余りが2のバイトについて
ここでは、コード(和音)を選択しています。
方法としては、7個のダイアトニック・コードから、ビット列により一つ選択するといった具合です。
ビット列は0~255まで表現できますから、7個のダイアトニック・コードから和音をランダムに選択するのは容易です。
ダイアトニック・コードとは、あるスケールにおける音のみによって構成された和音のことを言います。
C-majorにおいて、ダイアトニック・コードを五線上において表せば、以下の通りです。

一つも#や♭が付かない親切設計というわけです。
これをつなげることにより、多少の違和感はあれど、
そこまで大きな違和感にならないというのが、今回のミソです。
10で割った余りが3~9と、0のバイトについて
10で割った余りが1のバイトにおいては、メロディーのリズムを選択しました。
これは、多くとも8分音符8つからなるメロディーです。
すなわち、使われるか否かはとりあえず置いておいて、8つの音の候補さえ用意しておけば、メロディーを完成させることができるという寸法です。
メロディーの音の選び方についてですが、もちろん聞いて選ぶことはできません。
そこで、和音の構成音からメロディーの音を選択するという方法を取ります。
昔は僕もそういう作り方をしていた時代もありましたし、今も和音の音に釣られて音を決めることは多々あります。
そういうわけで、これが一番シンプルな決め方なのです。
さて、
10で割った余りが3のバイトを1つ目の八分音符の音にして、
10で割った余りが4のバイトを2つ目の八分音符の音にして……と続け、
10で割った余りが9のバイトを7つ目の八分音符の音にして、
10で割り切れるバイトを8つ目の八分音符の音にすれば、
全ての音符に音を割り当てることが出来るようになります。
リズムパターンによっては使われないバイトも出てきますが、その時はその時でしょう。
さて、各バイトについて、どのように音を決めているかは以下の通りです。
| 桁数 | 概要 |
|---|---|
| 1, 2 | 和音のどの構成音を基準にするか(00, 01なら主音、10なら三度に値する音(長三度、短三度)、11なら五度に値する音(完全五度、増五度、減五度)) |
| 3 | 4, 5桁目で「そのまま」以外が選択されたとき、基準音から上がるか、下がるか(0なら上がる、1なら下がる) |
| 4, 5 | 音を何度変化させるか(00...そのまま, 01...二度, 10...三度, 11...四度、変化はスケールに則る) |
| 6, 7, 8 | これらの桁が全て0かつ、1が0, 4, 5が01のとき、すなわち基準音が主音で、一つとなりの音が選択されたとき、この音を半音上げるか下げる。上げるか下げるかについては与えられたビットによってランダムに選択される。 |
これらを8つのバイトについて行えば、音が選択できるはずです。
時々ぶつかる音もあると思いますが、それもそれで一興です。
バイト割り当てのまとめ
以上をプログラムに書き換えることが出来れば、目的としては達成です。
今回は50バイトで4小節作ることが出来る形になっています。
プログラムを作成する
さて、ここまでは楽典でしたが、ここからはプログラムパートです。
実行だけされたい方は、GitHub のリポジトリからファイルをダウンロードして実行してください。
(「プログラムを実行する」の項まで飛んでいただきますと使い方をご覧いただけます。)
データを作成する
データは以下のようなものです。
長いので途中省略しています。
フルバージョンはGitHubからご覧ください。
projectData.pyrhythm_data = [
# 略
]
note_list = [
'C',
'C#',
'D',
'Eb',
'E',
'F',
'F#',
'G',
'Ab',
'A',
'Bb',
'B'
]
scale_list = [
0,
2,
4,
5,
7,
9,
11
]
chord_list = [
['C4', 'E4', 'G4'],
['D4', 'F4', 'A4'],
['E4', 'G4', 'B4'],
['F4', 'A4', 'C4'],
['G4', 'B4', 'D5'],
['A4', 'C5', 'E5'],
['B4', 'D5', 'F5'],
]
hihat_list = [
[0, 0, 0, 0],
[1, 0, 0, 0],
[0, 0, 1, 0],
[1, 0, 1, 0],
[0, 0, 1, 1],
[1, 1, 1, 0],
[1, 1, 0, 1],
[1, 0, 1, 1],
[0, 1, 1, 1],
[1, 1, 1, 1],
[2, 0, 0, 0],
[0, 0, 2, 0],
[1, 0, 2, 0],
[0, 1, 2, 0],
[1, 1, 2, 0],
[1, 1, 2, 2],
]
これを、この後作成するプログラムと同じディレクトリに保存してください。
プログラムに必要なライブラリを用意する
今回は、midiファイルを書き出す必要がありますので、そのためのライブラリを手に入れることにします。
名前は、pretty_midiです。
必要に応じて仮想環境を作成し、その中で
pip install pretty_midi
などでインストールできますので、コマンドプロンプトやターミナルなどから実行してください。
こちらのライブラリはかなり優れもので、分かりやすく書かれています。
General Midiの規格をある程度知っていれば、様々なmidiファイルを作ることができると思うのでオススメです。
プログラムの定義部を書き出す
先ほどprojectData.pyを保存したディレクトリに、filetomidi.pyというファイルを作成してください。
ここに作曲のためのプログラムを書いていきます。
filetomidi.pyimport os
import math
import pretty_midi as pm
import projectData as pd
# 変更すべき場所
bpm = 120
chord_vel = 80
drum_vel = 80
melody_vel = 100
song_path = "fileToMidi_py.mid"
file_path = "filetomidi.py"
log_path = "log.txt"
# 曲の小節の一単位。基本的にこの小節ごとにドラムのリズム等が変化する。
bar_unit = 4
# barUnit小節作るのに必要なバイト数。必要に応じて変更する必要がある。
bar4_data = 50
bar_time = 240 / bpm
beat_time = bar_time / 4
st_time = bar_time / 16
# グローバル変数
buf = []
一番上の変更すべき場所は、このプログラムを使ううえで変更することがほぼ必須のものです。
これらの説明は以下の通りです。
| 変数名 | 概要 |
|---|---|
| bpm | 曲のテンポを設定します。 |
| chord_vel | 和音の音量を設定します。100前後をお勧めします。 |
| drum_vel | ドラムの音量を設定します。100前後をお勧めします。 |
| melody_vel | メロディーの音量を設定します。100前後をお勧めします。 |
| song_path | midiファイルの名称を設定してください。プログラム中でos.path.dirname(file)が補われるので、フルパスは入力しないでください。 |
| file_path | midiファイルに変換するファイルの名称を設定してください。プログラム中で os.path.dirname(file)が補われるので、フルパスは入力しないでください。 |
| log_path | ログファイルを保存する場合、この名前で保存されます。初期ではログファイルは出力されません。プログラム中でos.path.dirname(file)が補われるので、フルパスは入力しないでください。 |
プログラムを実行する
さて、あとはプログラムを実行するだけです。
一つ上で若干実行方法をお伝えしましたが、ここでもう一回説明します。
1. ファイルの準備
filetomidi.pyprojectData.py- midiファイルに変換したいファイル
これら三つのファイルを同じディレクトリに入れます。
2. プログラムを変更する
プログラム「filetomidi.py」を若干変更します。
分からなければ、song_pathとfile_pathだけ変更しましょう。
filetomidi.pyimport os
import math
import pretty_midi as pm
import projectData as pd
# 変更すべき場所
bpm = 120
chord_vel = 80
drum_vel = 80
melody_vel = 100
song_path = "fileToMidi_py.mid"
file_path = "filetomidi.py"
log_path = "log.txt"
この上のsong_pathを、変換後のmidiファイルのファイル名、
file_pathを、midiファイルに変換するファイルのファイル名に変更してください。
3. 実行
あとはfiletomidi.pyを実行するだけです。
しばらく待てば、同じディレクトリに、指定した名前でmidiファイルが生成されるはずです。
4. 聞いてみる
せっかくなので聞いてみましょう。
注意点
一つだけお知らせしておきます。
大きめのファイルを変換すると、それだけでかなり時間が掛かります。
この「時間が掛かる」というのは、生成する時間も聞く時間もです。
生成する時間については(僕個人としては)待てるレベルですが、
再生時間は僕は待てません。
たとえば先ほど、50バイトで4小節作れると言いました。
bpmを120のままにしていれば、50バイトで8秒間の曲を作ります。
さて、100kBのファイルを入れると、曲はどれくらいの時間になるかというと、
16384秒の曲が爆誕します。これは4時間超の曲となります。
とてもではないですが聞いてられないと思います。
逆算しましょう。3分間の、カップラーメンが作れる時間であれば、
これはおよそ1kBほどのファイルが望ましいという結果になります。
試しに1kBのファイルで作ってみましたが、2分40秒でした。
このくらいが多分ちょうどいいと思います。
本来は4小節や8小節作るのにもっとビット列を使えばいいのですが、
これ以上どうやって組み合わせようかと迷ったので、
ここまでにしてあります。
もし何かほかに「こうしてほしい」などありましたら、
お知らせいただけるとやってみるかもしれません。
いろいろ作ってみる
さて、ここまでくると、持っているファイルでいろいろ試すのもありですが、
ランダムなビット列についてもやってみたいと思った方もいらっしゃるかもしれません。
そこで僕は、同様にPythonで、指定されたファイルサイズでランダムにビットを埋めるプログラムを作成しました。
以下の通りです。GitHub にも同じファイルをアップロードしています。
もしよろしければご利用ください。
filemaker.pyimport os
import math
import random
# input file name
file_path = "sample_1k"
# input file size(bytes)
file_size = 1024
with open(os.path.dirname(__file__) + "/" + file_path, "wb") as f:
number_list = [math.floor(random.random() * 256) for _ in range(file_size)]
f.write(bytes(number_list))
今回製作されたmidiファイルについて
今回このfiletomidi.pyで作成されたmidiファイルにつきましては、koukawa_ppは権利を主張いたしません。
ただし、この楽曲はfiletomidi.pyによって作成されたということを明記いただけますとありがたいです。
また、元のプログラムはMITライセンスで提供しておりますので、これをさらに発展させたプログラム等も大歓迎です。
おわりに
今回の内容は確かに難しいとは思いましたが、一度アイデアが出てしまえばどうにかなったので、取り組み始めのころからすれば、割合うまく事が運んだのではないかと思っています。
何かこのプログラムにおいて、「こういったものを追加してみるといいのでは」といったものがあれば、ぜひ追加してみてください。GitHubでのフォークなども大歓迎です。
最後までお読みいただき、ありがとうございました。
参考文献
https://github.com/craffel/pretty-midi
↑こちらはpretty_midiのGitHubです。
https://craffel.github.io/pretty-midi/
↑関数の説明がかなり丁寧になされています。
https://qiita.com/marshi/items/18bf9199b1b164ec1856
↑みんな大好きQiitaです。