はじめに
どうもこんにちは。バイトの片山です。最近は暑すぎるせいか、前置きを考えるのも面倒になってきました(酒の飲み過ぎで脳みそが萎縮しているのかもしれません)
今日はこの間予告した通り、PythonとopenFrameworksとPuredataをOSCで繋いでいきたいと思います。
一応掲載しますと、今回使用するもののバージョンはそれぞれ
- Python : 3.5.2
 - openFrameworks : 0.9.8
 - Puredata : 0.43.4-extended
 
となっています。OSはいつも通りですがOS Xのエルキャピタソです。
本編
今回の予定は、Pythonを始点として、openFrameworks(映像)、Puredata(音)という順番にOSCメッセージで繋いでなんか作る、という感じです。
↓イメージとしては
[Python] --> port:8002 --> [openFrameworks] --> port:5111 --> [Puredata]
「なんか作る」がとても不穏な感じですが、ひとまず始めていきます。
とりあえずPuredataのパッチを作る
まずは終着点となるPuredataのプログラムから作っていきます。
今回は以前のようにサイン波の加算合成だけで終わるようなしょうもないのは避けたかったのでFMシンセを使ってみることにしました。(どちらにしろ下らない感があるのは否めませんが……)
3つほど使ってないメッセージがあるのは見逃して下さい。(上手い用途がまだ思いつきませんでした)
ランダムを使っているところも手動で調整すれば十分じゃないか……というツッコミも無しで。
まずはメインパッチとなるpyosc.pdです。
pyosc.pd
構造としては、左チャンネルと右チャンネルに分けて音が出るようにして、それぞれgain等パラメータをいじれるようにしてあげた感じです。
あと、gainを調整するための垂直スライダーは2つとも[右クリック>プロパティ]から「出力範囲 上:」を最大で1にしてください。振りではないので必ずやって下さい。
次にpyosc.pdの中に入っているサブパッチの、pd fm-synthパッチとpd modパッチです。
pd fm-synth
とてもシンプルなFMシンセのプログラムです。
FMシンセをそのままいくつも作るとpyosc.pdが異常に巨大化して編集し辛くなるのでサブパッチ化(部品化)しました。
pd mod
こちらはpd fm-synthのハーモニシティレシオとモジュレーションインデックスにかけるモジュレーターをひとまとめにしたものです。これ無しで各個設定してあげても良いのですが、折角なら時間的な変化を付けてあげたいということで今回作ってみました。
openFrameworksのプログラムを作る
別に大したものを作るつもりはありません。Pythonから受け取ったOSCでoFで映像を作りつつ、oFからPuredataにOSCをスルーパスするコードが参考になれば幸いです。ってレベルです。いつも通り雑コードです。ゴミです。もっとキレイにまとめられそうな気はしてます(とりあえず動いてるしいじるのが面倒くさい)
oFのプロジェクトをジェネレートする時にOSCのライブラリをインポートするのを忘れないようにしましょう。
ofApp.h
#pragma once
#include "ofMain.h"
#include "ofxOsc.h"
#define SERVER_PORT 8002 // 受信ポート
 #define CLIENT_PORT 5111 // 送信先ポート
#define HOST "localhost"
class ofApp : public ofBaseApp{
public:
    void setup();
    void update();
    void draw();
    void keyPressed(int key);
    void keyReleased(int key);
    void mouseMoved(int x, int y );
    void mouseDragged(int x, int y, int button);
    void mousePressed(int x, int y, int button);
    void mouseReleased(int x, int y, int button);
    void mouseEntered(int x, int y);
    void mouseExited(int x, int y);
    void windowResized(int w, int h);
    void dragEvent(ofDragInfo dragInfo);
    void gotMessage(ofMessage msg);
    void dumpOSC(ofxOscMessage m);
    void sendOSC(ofxOscMessage n);
    int list[10];
private:
    ofxOscReceiver receiver;
    ofxOscSender sender;
};
ofApp.cpp
#include "ofApp.h"
//--------------------------------------------------------------
void ofApp::setup(){
    ofBackground(0);
    ofSetFrameRate(30);
    ofSetCircleResolution(32);
    ofSetBackgroundAuto(false);
    ofEnableAlphaBlending();
    receiver.setup(SERVER_PORT);
    sender.setup(HOST, CLIENT_PORT);
    for (int i = 0; i < 10; i++ ) {
        list[i] = 0;
    }
}
//--------------------------------------------------------------
void ofApp::update(){
    while (receiver.hasWaitingMessages()) {
        ofxOscMessage m;
        receiver.getNextMessage(m);
        if (m.getAddress() == "/filter") {
            for (int i = 0; i < 10; i++) {
                list[i] = m.getArgAsInt(i);
            }
        }
        dumpOSC(m);
        sendOSC(m);
    }
}
//--------------------------------------------------------------
void ofApp::draw(){
    ofSetColor(0);
    ofDrawRectangle(0, 0, ofGetWidth(), ofGetHeight());
    for (int i = 0; i < 10; i++) {
        if (list[i] != 0) {
            ofSetColor(list[i] * 25);
            for (int j = 0; j < 5; j++) {
                ofDrawRectangle(ofGetWidth()/50*i + ofRandom(-1, 1) + ofGetWidth() / 5 * j, 0, ofGetWidth()/50 + ofRandom(-1, 1), ofGetHeight());
            }
        }
    }
}
//--------------------------------------------------------------
void ofApp::dumpOSC(ofxOscMessage m) {
    string msg_string;
    msg_string = m.getAddress();
    for (int i=0; i<m.getNumArgs(); i++ ) {
        msg_string += " ";
        if(m.getArgType(i) == OFXOSC_TYPE_INT32)
            msg_string += ofToString( m.getArgAsInt32(i));
        else if(m.getArgType(i) == OFXOSC_TYPE_FLOAT)
            msg_string += ofToString( m.getArgAsFloat(i));
        else if(m.getArgType(i) == OFXOSC_TYPE_STRING)
            msg_string += m.getArgAsString(i);
    }
    cout << msg_string << endl;
}
//--------------------------------------------------------------
void ofApp::sendOSC(ofxOscMessage n) {
    ofxOscMessage m;
    m.setAddress("/filter");
    for (int i=0; i<n.getNumArgs(); i++ ) {
        if(n.getArgType(i) == OFXOSC_TYPE_INT32)
            m.addInt32Arg(n.getArgAsInt32(i));
        else if(n.getArgType(i) == OFXOSC_TYPE_FLOAT)
            m.addFloatArg(n.getArgAsFloat(i));
        else if(n.getArgType(i) == OFXOSC_TYPE_STRING)
            m.addStringArg(n.getArgAsString(i));
    }
    sender.sendMessage(m);
}
無精してコメント付けてませんが、そんなに難しいコトやってるわけではないので許してください。
Pythonのスクリプトを書く
前回だかなんだかのスクリプトを流用してるだけですが、一応掲載します。
もちろんpython-oscをインストールするのを忘れないようにしてください。
pyosc.py
import argparse
import sys
from pythonosc import osc_message_builder
from pythonosc import udp_client
port_num = 8002
# setup
parser = argparse.ArgumentParser()
parser.add_argument("--ip", default="127.0.0.1", help="The ip of th OSC Server")
parser.add_argument("--port", type=int, default=port_num, help="The port the OSC server is listening on")
args = parser.parse_args()
client = udp_client.UDPClient(args.ip, args.port)
print("ip:127.0.0.1, port:" + str(port_num) + ", address:/filter")
def main():
  sys.stdout.write("type int:")
  input_str = input()
  osc_msg, osc_list = make_osc(input_str)
  print(osc_list)
  client.send(osc_msg)
def make_osc(input_str):
  msg = osc_message_builder.OscMessageBuilder(address= "/filter")
  input_list = list(input_str)  
  output_list = []
  try:
    for i in range(len(input_list)):
      output_list.append(int(input_list[i]))
      msg.add_arg(output_list[i])
  except ValueError:
    print("<ValueError!!>")
  msg = msg.build()
  return msg, output_list
if __name__ == "__main__":
  while True:
    main()
さて、これで一段落しました。
いざ動かす
PuredataとoFの当該プログラムを起動して、コンソールでPython3のスクリプトを実行します。
適当に数字を入力していくと、こんな感じになります。微妙……?
↓蛇足かもしれませんが説明付きのスクショです
これで一応音も出せたし映像も生成出来ました。
終わりに
OSCで2つのアプリケーションを繋ぐ的な記事は見かけますが、今回のように直列で3つ繋ぐものってあまりないかなと思いますので、何かの参考になると嬉しいです。
ちなみに次回のことは何も考えてませんのでなんかネタを見つけたら書きます。
関連記事
- 応用編1: pythonとopenFrameworksをOSCで接続する
 - python-oscを使ってみる:後編
 - python-oscを使ってみる:前編
 - 未経験大学生によるPython入門日誌-第1話
 - 未経験大学生によるPython入門日誌-第2話
 - 未経験大学生によるPython入門日誌-第3話
 - 未経験大学生によるpython入門日誌-第4話 OpenCV篇
 - 用務員バイトの日記:Pygameで遊んでみる
 
      



