SuperColliderとopenFrameworks2
sc3からoFにOSCメッセージを送る方法は前回書きました。あまり良い方法ではありませんが。では、oFからSC3にOSCメッセージを送る方法はどうかといいますと、ofxOSC使えばちょい手間ですが簡単に出来ます。ただ、さらに簡単に行えるプラグインがofxSuperColliderです。ofxSuperColliderのソース見ていただければお分かりですが、中身はofxOSCを使用しています。より細かく制御したいならofxOSC使って自分でクラス組んでねってことです。
ofxSuperColliderのサンプルですと、oFを実行した際にシンセを作成、サーバに送っているため、ちょっと参考にならない場合がありますので、以下キーを押してシンセ作成、サーバに送る、音を出す。ってサンプルになってます。じゃあコードから行くっすよ。
testApp.cpp
#include "testApp.h" //-------------------------------------------------------------- void testApp::setup(){ ofSetWindowTitle("oF2sc3Test"); ofSetFrameRate(60); ofBackground(0,0,0); receiver.setup( PORT ); //OSCメッセージ受信ポート。今回は関係ないです flag = 0; //ここも今回は関係ないです //ofxsupercolliderここから //oFを実行した際にsc3側で作成したシンセをデフォルト値でサーバに送ります、本コードでは不要になります。サンプルではここでシンセをサーバに送っています。デフォルト値とは、sc3側で var amp = 0.4;とかの0.4の値のことです。 //synty1 = new ofxSCSynty("ofxsynth1"); //synth1->create(); //デフォルト値ではなく初期値を設定する場合setしてからcreate() //synth2 = new ofxSCSynth("ofxsynth2"); //synth2->set("vol",0.6); //synth2->create(); //サンプルは上記のようになっていますが、oF実行した際にシンセ作成してしまうと、後の処理が面倒になる可能性があります。どこかのタイミングでシンセを音を消したいときに、単に音量を0にするかシンセを解放するかなどの制御が必要です。解放した方がメモリ的に優しいですよね。ここではなく別のところでシンセ作成した方が良いと思います。 } //-------------------------------------------------------------- void testApp::update(){ oscReceive();//受け取り用なので今回は関係なし } //-------------------------------------------------------------- testApp::~testApp() { //free()によってシンセを解放します、必要ならばデストラクタで解放を忘れないようにしましょう。sc3側でdoneAction:2を指定し、かつgateに0を送ってシンセを解放している場合は不要。本コードではgateに0を送ってシンセを解放していますので、不要になります。試しにコメントを外してみると、実行アプリを閉じたときにノードがないっすよ、とかのエラーが確認できます。 //synth1->free(); //synth2->free(); } //-------------------------------------------------------------- void testApp::draw(){ string buf; buf = "listening for osc messages on port" + ofToString( PORT ); ofDrawBitmapString( buf, 10, 20 ); ofDrawBitmapString("flag : "+ofToString(flag), 50, 60); ofDrawBitmapString("sinAmp : "+ofToString(sinAmp), 50, 70); } //受け取り用なので今回は不要 void testApp::oscReceive() { // hide old messages for ( int i=0; i<NUM_MSG_STRINGS; i++ ) { if ( timers[i] < ofGetElapsedTimef() ) msg_strings[i] = ""; } // check for waiting messages while( receiver.hasWaitingMessages() ) { // get the next message ofxOscMessage receiveSC; receiver.getNextMessage( &receiveSC ); if(receiveSC.getAddress() == "/scSinReceive") { flag = 1; //テスト用 sinAmp = receiveSC.getArgAsFloat(0); } } } //-------------------------------------------------------------- void testApp::keyPressed (int key){ if (key == 'f'){ ofToggleFullscreen(); } } //-------------------------------------------------------------- //ここから本サンプルのメイン。a押すとシンセを作成して、グループのケツとしてシンセをサーバに送り、gate,ampを0にして音を鳴らします。sを押すとgateを0にすることで、シンセを解放します。sc3側ではdoneAction:7としていますので、このシンセより前のシンセノードも全て解放します。 void testApp::keyReleased (int key){ if(key == 'a') { synth1 = new ofxSCSynth("ofxsynth1"); synth1->set("gate",1); synth1->set("amp",1.0); synth1->addToTail(); } if(key =='s') { synth1->set("gate",0); //sc3側でdoneAction:2にしており、gateに0を送っているため下のfreeは必要なし //念のためdoneAction:7にして同じグループに追加したノードを解放している、もっと良い方法があるはず! //synth1->free(); } //必要ないとは思いますが念のためfree()でシンセを解放できるキーを定義。 if(key =='d') { synth1->free(); } if(key =='z') { synth2->set("gate",1); synth2->set("amp",1.0); synth2->addToTail(); } if(key =='x') { synth2->set("gate",0); //synth2->free(); } } //-------------------------------------------------------------- void testApp::mouseMoved(int x, int y ){ } //-------------------------------------------------------------- void testApp::mouseDragged(int x, int y, int button){ } //-------------------------------------------------------------- void testApp::mousePressed(int x, int y, int button){ if (button ==0) { //マウスクリックでオブジェクトsynty1のパラメータを変更。 // synth1->set("amp",1.0); } } //-------------------------------------------------------------- void testApp::mouseReleased(int x, int y, int button){ //synth1->set("amp",0.0); }
testApp.h
ofxSuperCollider.hをインクルードすればおkです。
#ifndef _TEST_APP #define _TEST_APP #include "ofMain.h" #include "ofxOsc.h" #include "ofxSuperCollider.h" #define HOST "localhost" #define PORT 57124 #define NUM_MSG_STRINGS 20 class testApp : public ofBaseApp{ public: ~testApp(); 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 windowResized(int w, int h); void oscReceive(); ofxSCSynth *synth1; //ここの定義も忘れずに。作りたいシンセのオブジェクトを定義します。 ofxSCSynth *synth2; //同様 ofxSCBus *bus; //出力ofxSCBusを指定したい場合はこれも ofxSCBuffer *buffer; //ofxSCBufferはファイルを読み込んで使いたい場合に private: ofxOscReceiver receiver; int current_msg_string; string msg_strings[NUM_MSG_STRINGS]; float timers[NUM_MSG_STRINGS]; float sinAmp; int flag; }; #endif
sc3側
普通にシンセを定義してやれば良いです。ofxSuperColliderのサンプルシンセで問題ないです。doneAction:7にしていますが、これはgateに0を送ったときの挙動を指定しています。7だとこのグループにおける指定したシンセノードを含め、それより前のノードを全て解放する、の意味になります。ofxsynth2の方は上手く解放できないはずです。
( SynthDef("ofxsynth1", { arg amp, dur, freq = 440, gate = 1, pan = 0, outBus = 0, fxBus = 10, fx = 0.0, vol = 0.8; var env, signal, outSend, fxSend,scale,key; env = Env.adsr(); scale = Scale.ionian.postln; key = DegreeToKey.kr( scale.as(LocalBuf), SinOsc.kr(0.08*MouseX.kr(1,10000),0,12), // mouse indexes into scale scale.stepsPerOctave, 1, // mul = 1 70 // offset by 70 notes ).midicps; signal = SinOsc.ar(key, 0, amp) * vol; fxSend = signal * fx; outSend = signal * (1.0 - fx); Out.ar(fxBus, Pan2.ar(EnvGen.ar(env, gate, doneAction: 7) * fxSend, pan) ); Out.ar(outBus, Pan2.ar(EnvGen.ar(env, gate, doneAction: 7) * outSend, pan) ); }).store; SynthDef("ofxsynth2", { arg amp, dur, freq = 220, gate = 1,pan = 0, outBus = 0, fxBus = 10, fx = 0.0, vol = 0.8; var env, signal, outSend, fxSend; env = Env.adsr(); signal = SinOsc.ar(freq, 0, amp); signal = Blip.ar(signal+WhiteNoise.ar(0.00001), 2, 0.8); signal = LPF.ar(signal, 1400); fxSend = signal * fx; outSend = signal * (1.0 - fx); Out.ar(fxBus, Pan2.ar(EnvGen.ar(env, gate, doneAction: 2) * fxSend, pan) ); Out.ar(outBus, Pan2.ar(EnvGen.kr(env, gate, doneAction: 2) * outSend, pan) ); }).store; )
関数
ofxSCSynthクラス
シンセ定義、bufferで読み込んだファイルを再生するときにも
ofxBufferクラス
- read(string path)
- サンプルみたいにbuffer->read(ofToDataPath("bell.aif", true));で
とかありますが、細かいところはソース読んで使うしかなさそうです。
シンセをcreate()するときにはノードIDをずらして作成しますので、ノードIDを意識して組まないとsc3側でエラーが出ます。sc3側のノードの概念はよくわかっていないのでなんとも書けませんが。
以上。