SuperCollider+OSC+OpenFrameworks

SCでホワイトノイズをFFTして結果をOSCで送信するコード:

sendMsg で配列を送信するには、Int8Array を使う必要がある。

FFTの結果を floatArray に変換して、さらに IntArrayにして、さらにInt8Arrayに変換している。

もっと単純な方法がありそう。

FFTの結果を格納するバッファ c のサイズは、real と image の2つ分が確保されていることに注意する。周波数成分はその半分の個数で値は0.0~2.0(だと思う)。unsigned char に収まるように調整する。

FFTのサンプル数と同じ個数の配列の要素を送信している。

※ FFTのバッファはIFFTを走らせると窓関数が掛かる?(と思う)。↑で表示されているスペクトルは窓関数(wnn)が掛かった後のホワイトノイズ?

s.boot;

b = NetAddr.new("127.0.0.1", 8000);

c = Buffer.alloc(s,2048/4,1);

v = Int8Array.fill(1024/4,0);

(

x = { var in, chain, chainB, chainC;

in = WhiteNoise.ar;

chain = FFT(c, in);

0.01 * Pan2.ar(IFFT(chain));

}.play(s);

)

(

r = Routine({

loop {

c.getToFloatArray(action: { arg array;

var z, x, w;

z = array.clump(2).flop;

// Initially data is in complex form

z = [Signal.newFrom(z[0]), Signal.newFrom(z[1])];

x = Complex(z[0], z[1]);

w = (x.magnitude*127).asInt;

//w.postln;

(1024/4).do({arg i; v[i]=w[i]});

b.sendMsg("/test",v);

});

0.01.wait;

}}).play

)

r.stop;

x.free;



oFのコード:

ofApp.h

SCからOSCに送られたInt8ArrayはBlob型で受け取る。Blob型には終端文字が1文字追加されるらしいので、Int8Arrayのサイズ+1分のバッファを確保している。

class ofApp : public ofBaseApp {

public:

void setup();

void update();

void draw();

void dumpOSC(ofxOscMessage m);

//OSC receiver instance

ofxOscReceiver receiver;

//Mouse position

ofPoint remoteMouse;

//Mouse button state

string mouseButtonState;

int count;

unsigned char receivedBuffer[1024+1];

};

ofApp.cpp

OSCのアドレス /test にメッセージが届いていれば、その arg を Blob型で受け取って、receivedBufferに格納する。

送られてきた配列の要素全てをofDrawLineで周波数成分に応じた長さの直線で描画する。

配列のサイズが大きいと描画の負荷が高く、後半に描いた直線を描画しきれないことがある。送信側で配列のサイズを調整する必要あり。

または、より高速な描画方法が必要。

#include "ofApp.h"

void ofApp::setup() {

ofBackground(0, 0, 0);

//connect with port num

receiver.setup(PORT);

//init veriables

mouseButtonState = "";

count = 0;

}

void ofApp::update() {

std::stringstream strm;

strm << "fps: " << ofGetFrameRate();

ofSetWindowTitle(strm.str());

while (receiver.hasWaitingMessages()) {

//get next osc message

ofxOscMessage m;

receiver.getNextMessage(m);

//if the osc address is /mouse/position

if (m.getAddress() == "/mouse/position") {

//set the mouse position

remoteMouse.x = m.getArgAsInt32(0);

remoteMouse.y = m.getArgAsInt32(1);

}

//if the osc address is /mouse/botton

else if (m.getAddress() == "/mouse/button") {

mouseButtonState = m.getArgAsString(0);

}

else if (m.getAddress() == "/click") {

count = ++count % 3;

cout << count << endl;

}

else if (m.getAddress() == "/test") {

int i = 0;

ofBuffer buff = m.getArgAsBlob(0);

for (char c : buff) {

//cout << c;

receivedBuffer[i++] = c;

}

//cout << endl;

}

//dump osc messages

//sdumpOSC(m);

}

}

void ofApp::draw() {

int radius =10;

//if mouse pressed, draw red circle

if (mouseButtonState == "down") {

radius = 20;

ofSetColor(255, 127, 0);

}

else if (mouseButtonState == "up") {

//else, draw blue circle

radius = 10;

ofSetColor(0, 127, 255);

}

ofDrawCircle(remoteMouse.x, remoteMouse.y, radius * (count+1));

int x = 10;

for (char c : receivedBuffer) {

ofDrawLine(x,400,x,400+c*3.0);

x += 2;

}

}

//dump osc message into console

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);

else {

msg_string += m.getArgType(i);

}

}

cout << msg_string << endl;

}