ReaperとTouchDesignerでアルゴリズム作曲がしたい


やりたいこと

アルゴリズム作曲といえばMax/MspやPure Dataなどを思い浮かべる人が多いと思いますが、基本的にこれらのツールはリアルタイムで音楽を生成することに長けているかわりにタイムライン的に音を配置していくような作曲は少々やりずらく感じます。
そこでRaeperというDAWを使って、より一般的なDAWっぽい操作感でアルゴリズム作曲をしたいと考えました。

Reaperについて

ReaperはオープンソースのDAWであり、無料でも使えるため、DTM初心者がとりあえず手を出すDAWソフト、という印象を持っている方も多いと思います。ですが実際は非常に多機能かつ拡張性のあるソフトなんです!

中でもReascriptという機能はlua, eel, python用に用意されているAPIを用いることで書き出しの自動化やマクロの作成、エフェクトの作成などができるようになります。
このReascriptをPythonで使って簡単なアルゴリズム作曲をしてみます。

CSVファイルの準備

今回はアルゴリズム作曲というか現代音楽でよくありがちなデータマッピングで音楽を作る手法をやってみます。音楽におけるデータマッピングについては松本昭彦さんのこのページ http://akihikomatsumoto.com/maxmsp/guido.html が非常にわかりやすいと思います。
今回は芸術的な云々ということはおいておき、CSVファイルの情報からMIDIノートを生成してみます。
今回使わせていただいたデータは http://www2.obirin.ac.jp/tsubota/eq/menu3.html ここで公開されている地震発生時刻と震源の深さ、発生地点の移動経度などがまとめられているのデータです。

まずはこのデータをTouchDesignerを使って扱いやすくしていきます。
今回はなるべくシンプルにするために発生時刻、発生地点の緯度、震源の深さの3つデータを発音タイミング、ピッチ、ベロシティにマッピングすることにします。
まず、TouchDesignerに先ほどのCSVを読み込みます。

TouchDesignerでは難しいことはやらない(というか使わなくてもいいけど視覚的にデータ処理がしやすいから使う)ので細かい説明は省きます。
発生時間の時間単位を掻けたり足したりしてhourにまとめて一年間を一列の時単位にまとめます。

発生時間と緯度と震源の深さをそれぞれ発音タイミング、ピッチ、ベロシティに最適な値となるようにレンジを調節し、ピッチ、ベロシティに関しては0-127の整数にしておきます(MIDI規格の範囲)。発音タイミングに関しては今回書くプログラムでは秒指定になるので、そのつもりでレンジを調節します。

fileout datでCSVファイルとして書き出します。

3列目(深度のデータ)は計測されていない部分もあるので欠けていますが問題ありません。

Reaperの準備

Reaperで準備することはトラックを一つ作って任意の名前を付け、media item(MIDIノートを入力できるボックス)を一つ作っておくだけです。media itemはトラック上でctrlを押しながらドラックすると作成できます。これらもReascriptで自動化することはできますが今回はMIDIノートの生成だけのプログラムを書きます。

Reascript

Reascriptでpythonを使う場合は設定が必要になります。
Options > Preferenecs > ReaScriptのpythonの有効化にチェック入れ、python.dllのパスと名前を入れることでpythonでreascriptが使えるようになります。

tomoさんのnoteにわかりやすくのっています。
https://note.com/suntomo/n/nac439dbafb66

次にメニューバーのActionsからShow action listをクリック→開いたウィンドウの右下のNew actionをクリック→New reascriptを選択し.pyで任意の名前で保存します。

するとこのようなウィンドウが出るので

以下のコードを貼り付け、トラック名とcsvのパスを入れます。

csv_for_reaper.py
from csv import reader

search = "自分でつけたトラック名"
notelength = 3 

ppq = 960

with open(r'csvデータのパス', 'r') as csv_file:
    csv_reader = reader(csv_file, delimiter = '\t')
    notelist = list(csv_reader)

notenum = len(notelist)

tracknum = RPR_CountTracks(0)
for index in range(tracknum):
    track = RPR_GetTrack(0,index)
    name = RPR_GetSetMediaTrackInfo_String(track, "P_NAME","", False)[3]
    if name == search:
        trackindex = index

item = RPR_GetMediaItem(0, trackindex)
take = RPR_GetMediaItemTake(item, 0)
for addnote in range(notenum):
    startsec = float(notelist[addnote][0])*ppq
    endsec = startsec + (notelength*ppq)
    pitch = int(notelist[addnote][1])
    velocity = int(notelist[addnote][2])
    RPR_MIDI_InsertNote(take, True, False, startsec, endsec, 0, pitch, velocity, False)

保存すると同時に実行されるはずなので、うまくいけばこのようにmidiノートが生成されます。

うまくいかなかった場合は
・csvライブラリが入ってない
・トラック名が間違ってる
・csvのパスが間違ってる
などが考えられると思います。
エラーメッセージも出るのでそれを読んでみてください。

pythonプログラムについて軽く説明すると

csv_for_reaper.py
with open(r'csvデータのパス', 'r') as csv_file:
    csv_reader = reader(csv_file, delimiter = '\t')
    notelist = list(csv_reader)

notenum = len(notelist)

ここでcsvを読み込んでリスト化し、リストの数を取得しています。

csv_for_reaper.py
tracknum = RPR_CountTracks(0)
for index in range(tracknum):
    track = RPR_GetTrack(0,index)
    name = RPR_GetSetMediaTrackInfo_String(track, "P_NAME","", False)[3]
    if name == search:
        trackindex = index

ここで任意の名前のトラックを探し、

csv_for_reaper.py
item = RPR_GetMediaItem(0, trackindex)
take = RPR_GetMediaItemTake(item, 0)
for addnote in range(notenum):
    startsec = float(notelist[addnote][0])*ppq
    endsec = startsec + (notelength*ppq)
    pitch = int(notelist[addnote][1])
    velocity = int(notelist[addnote][2])
    RPR_MIDI_InsertNote(take, True, False, startsec, endsec, 0, pitch, velocity, False)

ここでリスト化したcsvのデータを取り出してstartsec(発音タイミング),pitch,velocityに代入するのをリストの数ぶんforでくりかえしています。
あたまにRPRがついているのはreaperのAPI functionなので詳しくは
https://www.reaper.fm/sdk/reascript/reascripthelp.html
こちらを参照してください。

基本は以上です!
かなりいろんなことができそうなので、これからもちょくちょくやっていこうと思ってます!
私はコードベースのプログラミングが得意ではないのでおかしい部分などもあると思いますが、もしよかったら教えていただけると幸いです。