Fake Plastic Trees

音楽とシステム,及び機械学習との関わりについてのサーベイや実装など.https://twitter.com/iwa_sheep

PythonライブコーディングによるOSC/MIDIの生成,及びProcessingとの連携によるLive Performanceについて - LIVE at TUNNEL TOKYO

f:id:tty_tkhs_ml:20190718224859j:plain

Live Coding Performance in Deep Visualization Night at TUNNEL TOKYO 2019.07.17

 

1,はじめに


  Deep Visualization Night*1というイベントで,ライブコーディングのパフォーマンスを行ってきました.ライブコーディングとは何か,については下記記事を参照ください.

 本記事では当日行ったパフォーマンスの紹介と,技術的な解説を試みています.全てのアーキテクチャ,及びFoxDot,Processingのコードを公開しています.自由に再利用してください.

 

wired.jp

 

全コード

https://github.com/tatsuya-takahashi/creativecoding-live-at-TUNNEL_TOKYO

 

 2,何をしたか


  残念ながら当日の映像はないので,当日のプレイを再現した動画をYoutubeにアップロードしました.まずは下記をご覧ください.


サカナクション - ミュージック(FoxDot Livecoding with Processing)

 

 

3,コンセプト


  今回のコンセプトは2つで,1つは「現実世界とVisualizationの融合」と,もう1つは「相互作用」です.

 1つ目はTwitterとの連携により,リアルタイムにハッシュタグ付きのつぶやきを収集しVisualizeすることにより,人がVisalizeの中に入っていく様を表現しました.

 2つ目は,OCSとMIDI信号,それからプログラムの生成するリズムと人がキーボードで演奏するメロディをMashUpし,そこからお互いが干渉しあって生まれる音楽のシナジー効果を表現しました.

 

4,システム構成


 システム構成図は下記の通りです.やや複雑なのでパートごとに解説します.

f:id:tty_tkhs_ml:20190719213436p:plain

1. システム構成図

 

5,FoxDot(入力1)


f:id:tty_tkhs_ml:20190719192940p:plain

2, FoxDot

  今回のシステムのキモとなる部分です.

 FoxDot*2とは,ライブコーディングPythonで行うためのアプリケーションです.FoxDot自体に音を鳴らす機能はありません.FoxDotにあるのは,決められた時刻に,任意のOSCシグナル*3(あるいは,MIDIシグナル)を発生する機能のみです.

 では,どうやって音を鳴らすのでしょう?そもそもOCSとは?

 まずOSC(Open Sound Control)とは,通信プロトコル規格の一種です.世界的に決められている,「こういう音色の時にはこういうシグナルを送ってね」という細かい規約の集合です.我々はこの共通言を使って様々なアプリケーションと会話することができます.

 FoxDotは主にSuperCollider*4と会話するように設計されています.SuperColliderは,OSCを解釈し,音声を合成してくれるアプリケーションです.界隈では「スパコ」と呼ばれていたりします.

 また,FoxDotはMIDIという方言も話せます.世の中には,MIDIを受け取って音楽を合成するアプリケーションがたくさんあります.音楽システム界隈の(ほぼ)標準言語です.私は今回はその中でも,Studio One*5というDAWソフトを利用しました.理由としては使い慣れていることは勿論,初めから高音質の音源ソフトウェアやエフェクトが付属していたり、また,それらを自由に拡張できるためです.

 勿論SuperColliderの音も悪くはないのですが(むしろとても良い)、逆にOSC界隈ではメジャーでありすぎるが故に皆が似たような音になってしまいがちです.なので今回はMIDI+Studio Oneで,音源を拡張したり、エフェクトを効果的に利用しました.

  FoxDotのソースコードの全量はGitHubにあげています.是非参考にしてください.今後も行ったライブコーディングのソースコードは全て公開していく予定です

 

 

# init
Clock.bpm = 122
Scale.default = Scale.major
Root.default = -3

# Chords
BmM7add9 = (1,3,5,7,9)
E6add9 = (4,6,8,9,12)
AMaj7_h = (7,9,11,13) 
AMaj7 = (0,2,4,6, 8)
Fs7 = (5, 7, 9, 14)
Cs7add9 = (2,4,6,8,10.5)
PAMaj7 = P[0,2,4,6, 8]
PCs7add9 = P[2,4,6,8,10.5]

  上記は演奏開始前にあらかじめ用意していたコードです.プログラマーたるもの0ベースで作っていくのがやっぱりかっこいいとは思うのですが,余程自身の記憶力とタイピング力に自信がないと,いつまで立っても音色が変わらずオーディエンスを退屈させてしまうかもしれません.なので私は,DJが前日に夜鍋してキュー打ちをするように,当日有効そうなコードやリズムパターンなどは,あらかじめ用意しておきます.

 ライブコーディングの醍醐味って,例えばドミソを(0,2,4.... などとタイピングしていくことではなく、Forループやnumpyによる行列処理,各種アルゴリズムなどを効果的に利用した高度で抽象化されたものを披露するところにある,と私は考えます.

 私自身がまだその域にはいけていないですが,自分も(オーディエンスも)退屈だと感じることは,なるべく当日のパフォーマンスから除外することをお勧めします.

 

# Adjust
Clock.midi_nudge = 0.75

 これは,MIDI信号のディレイ命令です.FoxDotのOSC発振と,MIDI発振の間には僅かなディレイがあります.その差分を,このmidi_nudge命令によってずらしながら調整します*6

 

pad >> MidiOut(P[AMaj7,AMaj7, Cs7add9, Cs7add9], channel=1, sus=0.3, dur=4, amp=0.7)

  これはMIDI信号の発振命令です.AMaj7と,C#add9の音をchannel1に飛ばしています.今回のチャンネル構成は下記の通りです.

f:id:tty_tkhs_ml:20190719200226p:plain

3. MIDI Channel構成

 Channel1をMIDIキーボードに割り当て,Channel2 - 9をFoxDotに割り当てています.StudioOneでChannelそれぞれに楽器をアサインしています.

f:id:tty_tkhs_ml:20190719200551p:plain

4. Studio Oneの楽器のアサイ

 このMIDI連携には,仮想MIDI*7という技術を使っています.なぜなら、FoxDotが発振したMIDIを直接Studio Oneに送ることができないためです.MIDIMIDI INから入ってきて,処理され,MIDI OUTから出ていくという約束事があります.なのでFoxDotのMIDIも,仮想MIDIに一旦出力し,その仮想MIDIからループバックされてきたMIDIをStudio Oneに入力しています.

 この仕組みには大きな利点が1つあります.それは,仮想MIDIから入力されてきたMIDI信号を,複数のアプリケーションで取り込むことができることです.なので今回は同じMIDI信号をStudio One,Processingに入力することにより,音と映像を同期したインスタレーションを作成することに成功しています

 ちなみに,ProcessingにはMIDIだけではなくOSCも送信しています.なので,ProcessingはFoxDotから発振された,OSC,MIDI,両方を受け取りVisualizeしています.

 

 

  実は以前FoxDot(もといライブコーディング)の入門本を書いています.よろしければ.

booth.pm

 

6,MIDIキーボード(入力2)


  

 KORG microKEY Air2の61鍵モデル*8を使用しました.軽量,コンパクト,BlueTooth接続,電池駆動可能,タッチ感良しであり,パフォーマンス用MIDIキーボードの一種の完成形ではないかと思っています.

 これはその名の通り、鍵盤を押したりピッチベンドをいじったりすると,MIDIが出力されるだけの発振器です.音は鳴りません.なのでこのMIDIキーボードも,Studio Oneに接続して音を鳴らしてあげます.

 そして同時にProcessingにもMIDIを飛ばし,FoxDot同様音と映像の同期を図っています.

 

 

 これは完全に余談ですが、当該キーボードには公式のケースがありません.色々探したのですが,サバイバルゲームで利用されるライフのケースがとてもピッタリであり、たくさんのポケットにコード類やダンパーペダルを収納できるので本当にお勧めです.......

 

  

 ここまで2つの入力を紹介しました.入力は2つあるのですが、我々はこれらを同時に制御できません.すなわち、プログラミングを書きながら鍵盤を演奏することは極めて困難です(手は2本しかないためです).

 なのでどうすれば良いかというと,「どちらかをやる」という解になります…….本当はどちらかを学習モデルでMIDIの自動生成とかやりたいのですが,まだ研究中です.

f:id:tty_tkhs_ml:20190719205554j:plain

7, 完全に鍵盤を弾いているためライブコーディングしていない状況

7,Twitterの収集(入力3)


 

 Twitter APIを利用し,特定のハッシュタグ付きのTwitterを収集しています.

 Pythonで処理を記載し,Cronで1分間隔で定期実行しています.

 今回はソースコードを省略します.

 

8,Processing(出力)


 

 今までで入力の準備は整いました.あとはやってきた信号を活用するだけです.

 入力をおさらいします.

  1. FoxDotからのOSC信号
  2. FoxDotからのMIDI信号
  3. MIDIキーボードからのMIDI信号
  4. Twitterの特定のハッシュタグ付きの

 以上の4つです. 今回はこれらを下記のように利用します.

  1. (今回はOSCは利用しませんでした.MIDIで処理を統一したいためです)
  2. 機械的で周期的なVisualize
  3. 音の高さや強さに合わせて有機的に変化するVisualize
  4. Worldに登場する生き物のようなイメージのVisualize

  です.

 

 まず2と3についてですが,ProcessingにはMIDI,OSCと接続するプラグインが用意されています.今回はMIDIのみですが,下記を利用しました.

qiita.com

 

 コードを見てみましょう.

void noteOn(int channel, int number, int value){
  // println("a");
  // println("channel:" + channel + " number:" + number +" value:"+ value);
  
  // void spawn(int  x,int y, int h, int s, int b, float size1, float size2) {
  if (channel == 0) {
    // nanoKey
    spawn((number - 36) * 20,  900 - (number - 36) * 20, int(random(0, 360)),  50, 100, value * 5, value * 5);
  }
  if (channel == 1) {
    // pads
    spawn(100,         height - 200, 200,  50, 100, value, value);
    spawn(width - 100, height - 200, 200,  50, 100, value, value);
......
....
..

  noteOn()関数は,MIDIからnoteOn(MIDIキーボードでいうと,MIDIキーボードを押した時のメッセージです)メッセージを受け取った時に呼ばれる関数です.ここでChannelごとで分岐し,それぞれの処理を行うことができます.

 

 MIDI信号には情報が乗っています.上記では,noteとvalueを利用しています.MIDIキーボードの場合は,noteは音の高さ,valueは音の強さを表しています.今回のインスタレーションではnoteではVisualの位置を,valueではVisualの大きさを決めるファクターとしています.

 

void controllerChange(int channel, int number, int value) {
  // Receive a controllerChange
  cc = value - 64;
}

 上記はピッチベンドなどのつまみをいじった時に呼ばれる関数です.今回はつまみに合わせて背景色を変更するインスタレーションにしました.フィルターがかかっている時(高音がカットされている時)は暗くするなど,音と映像の直感的なリンクを試みました.

    File f = new File("/Users/tatsuya.takahashi/tweets.csv");
    BufferedReader br = new BufferedReader(new FileReader(f));
 
    String[][] data = new String[maxCount][10];
    String line = br.readLine();
    this.text = "";
    for (int row = 0; line != null; row++) {
      if (row >= maxCount) {
        this.text = line.split(",", 0)[4];
        maxCount = maxCount + 1;
        break;
      }
      data[row] = line.split(",", 0);
      line = br.readLine();

 上記はTwitterインスタレーションです.draw関数内で毎回実行し,Tweetファイルを読み込み,新規のTwitterがあればそれを,無ければ今までのTweetからランダムにセレクトしてProcessingに登場させています.

 なお,Tweetの可視化にはBox2Dを利用して世界を構築しています.Deepな深海のようなイメージです.

 

9,今後の展望


 

  • MIDIを時系列モデルで学習し,その後MIDIの自動生成を行えるような研究を行っています.今後このブログでサーベイを公開します.
  • バンドをやってるので,ライブコーディングのバンドとして活動を行う予定です.
  • 映像と音楽のマルチモーダルな深層学習に極めて強い関心があります.Youtube8Mのデータセットを利用し,まずは1方向で,音楽から映像を自動生成する仕組みを作成中です.