Tech Racho エンジニアの「?」を「!」に。
  • 開発

応用編2: PythonとopenFrameworksとPuredataをOSCで接続する

はじめに

どうもこんにちは。バイトの片山です。最近は暑すぎるせいか、前置きを考えるのも面倒になってきました(酒の飲み過ぎで脳みそが萎縮しているのかもしれません)

今日はこの間予告した通り、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

python

構造としては、左チャンネルと右チャンネルに分けて音が出るようにして、それぞれgain等パラメータをいじれるようにしてあげた感じです。

あと、gainを調整するための垂直スライダーは2つとも[右クリック>プロパティ]から「出力範囲 上:」を最大で1にしてください。振りではないので必ずやって下さい。

次にpyosc.pdの中に入っているサブパッチの、pd fm-synthパッチとpd modパッチです。

pd fm-synth

python

とてもシンプルなFMシンセのプログラムです。
FMシンセをそのままいくつも作るとpyosc.pdが異常に巨大化して編集し辛くなるのでサブパッチ化(部品化)しました。

pd mod

python

こちらは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のスクリプトを実行します。

適当に数字を入力していくと、こんな感じになります。微妙……?

python

↓蛇足かもしれませんが説明付きのスクショです

python

これで一応音も出せたし映像も生成出来ました。

終わりに

OSCで2つのアプリケーションを繋ぐ的な記事は見かけますが、今回のように直列で3つ繋ぐものってあまりないかなと思いますので、何かの参考になると嬉しいです。

ちなみに次回のことは何も考えてませんのでなんかネタを見つけたら書きます。

関連記事


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。