pythonに作曲してもらう
カテゴリ:Pythonの話
こんにちは。inglowの開発担当です。
今回は、近年超キテる「python」を使って、作曲ができないかと思い試してみました。
musicXMLなる形式があると知って思いついたものを実験してみたものです。
また、弊社ではWebプロモーション成功事例集をまとめた限定資料を無料で配布しています。
Webマーケティングに興味がある方は、下記ページより目を通してみてください。
開発環境は下記です。
- バージョン:python3.8.3
- ライブラリ:music21、markovify
- エディタ:notepad++、MuseScore3(musicXMLの表示用)
今回作るもの
今回は、「マルコフ連鎖」というアルゴリズムを使用して、pythonに作曲をしてもらいます。
本当は、理論化されていて、伴奏等もできそうではあるのですが…。
今回は、既存のメロディを学習させて、その学習データをもとに作曲をしてもらうことにしました。
今回使用するメロディは、自分が敬愛する、とあるサウンドクリエイターの曲のサビ部分を学習させることにしました。
music21の下準備
music21を便利に使うために少し下準備が必要です。
musicXMLを読み取り・生成ができますが、XMLの形式のままだと、どんなメロディなのか想像がしづらいため、musicXMLから楽譜の形式で表示できるソフトウェアが必要になります。
今回は、以前利用したことがあり、操作方法も何となくわかる「MuseScore3」というソフトを利用することにしました。
ライブラリをインストールして最初に動かす際に、「MuseScore3」の実行ファイルにパスを通す(musicXMLを生成したらこのソフトで表示してね!という設定)必要があります。
1 2 3 4 5 6 7 8 |
#最初に設定をする場合 from music21 import * us = environment.UserSettings() us.create() #first time only #デフォルトでインストールした場合(適宜パスを変更してください。) us['musescoreDirectPNGPath'] = 'C:/Program Files/MuseScore 3/bin/MuseScore3.exe' us['musicxmlPath'] = 'C:/Program Files/MuseScore 3/bin/MuseScore3.exe' |
この設定をあとから変更する場合は、下記のようなプログラムになります。
1 2 3 |
from music21 import * environment.set("musescoreDirectPNGPath", "C:/Program Files/MuseScore 3/bin/MuseScore3.exe") environment.set("musicxmlPath", "C:/Program Files/MuseScore 3/bin/MuseScore3.exe") |
楽譜データを読み取る
まずは、musicXMLを作成していきますが、テキストファイルでがしがしつくるのは大変すぎるので、先ほどの工程でインストールした「MuseScore3」で1メロディ=1ファイルになるようにデータを作成していきました。
作成したmusicXMLのファイルを、pythonに読み取らせて、music21によってオブジェクトに変換させます。
また、今回は、複数のファイルがあるため、フォルダの中の「musicXML」ファイルをすべて読み取るような動きにしました。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
import music21 as m21 import os import glob #パスの生成 DS = os.sep bs = os.path.dirname(__file__) + DS xmlpath = bs + 'musicxml_simple' + DS #フォルダ内のxmlファイルを取得する xmls = glob.glob(xmlpath + "*.musicxml") for x in xmls: piece = m21.converter.parse(x) |
変数の「piece」が楽譜データのオブジェクトとなります。
楽譜データを学習モデルに変換する
楽譜データのオブジェクトが準備できたら、次は、「markovify」ライブラリで学習モデルが作成できるように、音符のデータをテキストに変換します。
markovifyで文章を作成する際には、文章を、単語ごとにスペースで区切った形に変換するので、今回は、1音毎にスペースで区切っていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#読み込ませるテキスト準備用 note_txts = [] #フォルダ内のxmlファイルを取得する xmls = glob.glob(xmlpath + "*.musicxml") for x in xmls: piece = m21.converter.parse(x) ntxt = [] for n in piece.flat.notesAndRests: #n.name:音名の取得 n.duration.quarterLength:音の長さの取得(1拍、0.5拍等…) ntxt.append(str(n.name) + '_' + str(n.duration.quarterLength)) #1曲が終わったら、追加する note_txts.append(note_txts.append(' '.join(ntxt))) #最後に、改行区切りでテキストデータを準備 txts = '\n'.join(note_txts) |
ここまででモデル作成のためのテキストデータができました。このデータを、markovifyで学習データに変換してもらいます。
1 |
text_model = mrkv.NewlineText(txts) #必要があれば、state_sizeを1~3で設定する。デフォルトは2 |
これで、学習モデルが出来上がりました。
生成された作曲データを楽譜に出力する
先ほど生成した学習モデルから、メロディのテキストを生成してもらいます。
1 |
sentence = text_model.make_sentence(min_chars=50, max_chars=150) |
これでメロディを作成してもらいましたが、学習モデルと同じ形式のテキストなので、「E_0.5 E_0.5 D_0.5 C#_0.5….」というようなテキストになっています。これを、MuseScore3で表示できるように、musicXMLに変換します。
変換の手順は、先ほどmusicXMLからテキストにした時の逆の手順で行っていきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#メロディをmusicXMLに変換する meas = m21.stream.Stream() #楽譜オブジェクトの生成 meas.append(m21.meter.TimeSignature('4/4')) #拍子を設定する 今回は4拍子で固定しました。 melo = sentence.split() #半角スペース区切りで配列にします for m in melo: #[E_0.5、E_0.5、D_0.5、C#_0.5、....]のデータを順番に処理 ptch, dist = m.split('_') #アンダーバーで区切る if(ptch == 'rest'): #rest=休符 この場合は休符の長さだけ追加 n = m21.note.Rest(quarterLength = float(dist)) else: #音と音符の長さを追加 n = m21.note.Note(ptch,quarterLength = float(dist)) #楽譜に追加 meas.append(n) #小節線を追加する meas.makeMeasures(inPlace=True) #楽譜をmusicxmlで表示する meas.show('musicxml', addEndTimes=True) |
MuseScore3で生成された楽譜が表示されました!
今回のソースの全文は下記のような感じになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import music21 as m21 import markovify as mrkv import os import glob #パスの生成 DS = os.sep bs = os.path.dirname(__file__) + DS xmlpath = bs + 'musicxml_simple' + DS #読み込ませるテキスト準備用 note_txts = [] #フォルダ内のxmlファイルを取得する xmls = glob.glob(xmlpath + "*.musicxml") for x in xmls: piece = m21.converter.parse(x) ntxt = [] for n in piece.flat.notesAndRests: #n.name:音名の取得 n.duration.quarterLength:音の長さの取得(1拍、0.5拍等…) ntxt.append(str(n.name) + '_' + str(n.duration.quarterLength)) #1曲が終わったら、追加する note_txts.append(' '.join(ntxt)) #最後に、改行区切りでテキストデータを準備 txts = '\n'.join(note_txts) text_model = mrkv.NewlineText(txts) #必要があれば、state_sizeを1~3で設定する。デフォルトは2 sentence = text_model.make_sentence(min_chars=50, max_chars=150) #メロディをmusicXMLに変換する meas = m21.stream.Stream() #楽譜オブジェクトの生成 meas.append(m21.meter.TimeSignature('4/4')) #拍子を設定する 今回は4拍子で固定しました。 melo = sentence.split() #半角スペース区切りで配列にします for m in melo: #[E_0.5、E_0.5、D_0.5、C#_0.5、....]のデータを順番に処理 ptch, dist = m.split('_') #アンダーバーで区切る if(ptch == 'rest'): #rest=休符 この場合は休符の長さだけ追加 n = m21.note.Rest(quarterLength = float(dist)) else: #音と音符の長さを追加 n = m21.note.Note(ptch,quarterLength = float(dist)) #楽譜に追加 meas.append(n) #小節線を追加する meas.makeMeasures(inPlace=True) #楽譜をmusicxmlで表示する meas.show('musicxml', addEndTimes=True) |
作成した曲を聞いてみる
表示された楽譜は、MuseScore3の再生機能でそのまま聞くことができます。いろいろなパターンで生成を試してみたので、生成した結果を一部紹介します。
パターン1:読み取ったキー(調)のまま学習モデルにする
調がバラバラなため、次に行く音のデータが少ないのか、結構な確率で生成されないことがありました。
パターン2:読み取った後、主音(キーになる音)を合わせて学習モデルにする
楽譜をそのまま読み取ると、調が合わず、そのまま学習させた時みたいにあちこちしてしまいます。
メロディーが返ってくるべき音(通常、最後になる音)を合わせることで、そのまま学習させるよりも統一感が出てきます。
一番それっぽい感じにできるのではないかなと思っています。主音を合わせることで、次に行く音のバリエーションが増えたのか、生成されないことがありませんでした。
パターン3:読み取った後、調合(楽譜の左にあるシャープとかフラット)を合わせて学習モデルにする
返ってくるべき音が違うものの、楽譜にした時に、左につけておく調号を合わせることでも統一感を出すことができます。
(気になる方は、「五度圏」や、「平行調」というワードを調べていただければと思います。)
主音は違うものの、使っている音はほぼ同じなはずなのですが、全体的に短くしか生成されませんでした。バリエーションが少なすぎるのでしょうか…。
パターン4:全部のメロディを12通りのキーにして学習させる
返ってくるべき音をド~シまでのすべてのパターンに変換したものを学習させました。
カラオケで言うと、キー変更の-6~+6までのパターンを学習させた感じです。
学習データが増えたはずなのですが、増えたからと言ってそれっぽいものがつくれるかとはまた違うみたいですね…。
ここまで総合すると、パターン2の主音を合わせるのが一番メロディ制作がしやすいのかなという感じになりました。
さらに実験で、モデル作成のstate_sizeを触ってみます。
ここまでは、3を設定して生成していたので、1と2も試してみます。
state_size=1
学習したメロディの片鱗はほぼなくなりましたが、生成されたメロディの長さが短いか長いかの二極化してしまいました。
state_size=2
少し元のメロディの片鱗は残っているものの、わりとそれらしくはなりました。
さいごに
せっかくpythonちゃんがメロディを作ってくれたので、一番それっぽかったメロデイに伴奏をつけてあげました。
データが少ないためか、めちゃくちゃだったりメロディアスでなかったりする時もありますが、それなりにそれっぽい感じで生成できたのではないかなと思います。
音楽はやはり、人の気持ちが乗ってこそとも思いますが、人が作るとついつい自分が演奏しやすいように作ってしまうといったこともあるかと思いますので、こういった形で作曲するのもまた新鮮かもしれません。
また、どういった感じになれば人が聞いて気持ちいメロディになるのか等、音楽の研究に使うのも面白いかなと思いました。
人の手が必要になりますが、「○○ぽいメロディだけ」といった風に学習させていけば、悲しい曲、盛り上がる曲といった感じでの作曲もさせられるのかなと思いました。
今度は別のアルゴリズムでも試してみたいです。
またwebで集客する方法を別の記事にまとめております。
詳しく解説しているので、web集客について深く知りたい方は、ぜひこちらもご覧ください。
弊社inglowでは、これから広告の運用を考えられている方、あるいはこれから広告代理店に運用をお願いされる方向けに、「業界別Web広告の成功事例」をまとめた資料を無料配布しております。
下記のフォームに入力いただくだけで、無料で資料をダウンロードしていただけます。ぜひご利用下さい。