Python
processing
SuperCollider
OSC
foxdot

FoxDotとProcessingをOSC通信で連携させてみる

FoxDotでライブコーディング演奏しつつ、各サウンドの発音タイミングに合わせてProcessingになにかさせてみる、というのが目的です。
そもそもFoxDotとは?についてはこちらの記事がわかりやすかったのでこれから始める人におすすめです。

FoxDot側の準備

FoxDotは予めOSC通信でデータを送信する仕組みを備えています。(これはFoxDotが音響合成アプリケーションであるSuperColliderにOSC通信で命令を送ることで音を出しているため)
なので、この仕組みを利用すれば任意の宛先にメッセージを送信することも可能なはずです。
具体的には、FoxDotのソースコードのServerManager.py内に数行追記するだけで済みました。
まずSCLangServerManagerクラス内のコンストラクタに以下を追加します。

ServerManager.py
        # General SuperCollider OSC connection
        self.client = OSCClientWrapper()
        self.client.connect( (self.addr, self.port) )

        #!!!以下2行を追加!!!#
        self.myOSC = OSCClient() 
        self.myOSC.connect( ("127.0.0.1", 12345) ) #ここでOSC受信側のIPアドレスとポートを設定します

続いて同クラス内でsendOSCメソッドが定義されている部分に以下を追加します。

ServerManager.py
    def sendOSC(self, osc_message):
        """ Sends an OSC message to the server. Checks for midi messages """

        if osc_message.address == OSC_MIDI_ADDRESS:

            self.sclang.send( osc_message )

        else:

            self.client.send( osc_message )

            #!!!以下を追加!!!#
            self.myOSC.send( osc_message )

これで設定した宛先(ここではローカルホストのポート番号12345)へFoxDotの情報が届くようになりました。

Processing側の準備

続いてProcessing側でFoxDotからのOSC信号を受けとるための設定をします。
ProcessingでOSC通信を扱うためにはoscP5というライブラリが必要なのでまずはインストールします。
oscP5の使用方法についてはこちらの記事がわかりやすかったです。
インストールが済んだら、まずは受信ポートなどの設定をしていきます。

osctest.pde
import oscP5.*;
OscP5 oscP5;

void setup() {

  size(500, 500);
  background(255);

  //OSC通信を受け取るポートの設定
  oscP5 = new OscP5(this,12345);

}

続いて、FoxDotから信号を受け取った際になんらかのアクションをさせる部分を記述していきます。
FoxDotはサウンドの発音命令を\s_newというOSCアドレス宛に送信するので、このメッセージを受け取ったらコンソールに表示させてみます。

osctest.pde
void oscEvent(OscMessage msg) {

  if(msg.checkAddrPattern("/s_new")==true) {
        println("heard!");
   }
}

これでFoxDotがなにか音を出したタイミングでProcessingに反応させることができました。
しかし「すべての音」に対して一律に反応させるのではなく、鳴らされるサウンドによって挙動を分けたい場合があると思います。
例としてFoxDotでbassとサンプル音源を鳴らすことを想定してみます。

FoxDotTest.py
p1 >> bass([0,3,5])

p2 >> play('xs--', dur=1/2)

この時、さっきの\s_newメッセージの先頭にプレイヤーの情報が入ってくるので、これを利用すればサウンドの種類ごとに挙動をわけることが可能です。
以下のコードはp1のbassとp2のサンプラー音源で挙動を分けてみた例です。

osctest.pde
void oscEvent(OscMessage msg) {
   if(msg.checkAddrPattern("/s_new")==true) {
      if(msg.get(0).stringValue().equals("bass")){
        println("bass");
      }
      if(msg.get(0).stringValue().equals("play1") || msg.get(0).stringValue().equals("play2")){
        println("sample");
      }
  }
}

0番目に入っているメッセージを確認して場合わけしています。ここにはプレイヤーの名前(bass、padsなど)もしくは、サンプル音源の場合はplayer1かplayer2という文字列が格納されてきます(これはサンプル音源のステレオ/モノラルの違いによってどちらかが選ばれます)
しかしさらにサンプル音源の種類によっても挙動を分けたいという場合、もっと情報を深掘りしていく必要が出て来ます。

いろいろ探した結果、一応ここを特定する方法があったので共有しておきます。(もっと綺麗な方法があるかもしれません、わかる方はご教示いただけると幸いです)

FoxDotはサンプル音源を再生された順番でバッファに格納しています。
それぞれのバッファには1からインデックス番号がふられているので、いまどのインデックス番号のバッファが再生されているかが分かればよさそうです。
このインデックス番号が\s_new宛のメッセージの67番目に格納されてくるので、これを利用します。
改めて以下のp2の例では、xの音源はバッファのインデックス番号1に、sは2に、-は3に格納されてくることになります。

FoxDotTest.py
p1 >> bass([0,3,5])

p2 >> play('xs--', dur=1/2)

osctest.pde
void oscEvent(OscMessage msg) {

  if(msg.checkAddrPattern("/s_new")==true) {
     if(msg.get(0).stringValue().equals("bass")){
        println("bass");
     }
     if(msg.get(0).stringValue().equals("play1") || msg.get(0).stringValue().equals("play2")){
      if(msg.get(67).floatValue()==1.0){
        println("x");
      }else if(msg.get(67).floatValue()==2.0){
        println("s");
      }else{
        println("other");
      }
     }
   }
}

これでサンプルごとに挙動を振り分けることができるようになりました。

これを使ってちょっとしたおふざけデモも作ってみたのでなにかの参考になれば嬉しいです。(まったくライブコーディング性がないのは単に自分の技術不足です・・・)

FoxDot to Processing via OSC Communication

今後は音程の変化に合わせてなにかさせる、というのもやってみたいです。