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で読み込んだファイルを再生するときにも

  • create()
    • シンセ作成。指定可能な引数はint position, int groupID、よくわかんないっす。
  • set(string arg, int value)
    • arg名のパラメータに、value値を送ります。
ofxBufferクラス
  • read(string path)
    • サンプルみたいにbuffer->read(ofToDataPath("bell.aif", true));で

とかありますが、細かいところはソース読んで使うしかなさそうです。
シンセをcreate()するときにはノードIDをずらして作成しますので、ノードIDを意識して組まないとsc3側でエラーが出ます。sc3側のノードの概念はよくわかっていないのでなんとも書けませんが。
以上。