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