ローレンツアトラクタを聴く
SuperColliderに取り組み始めた。
できそうな範囲で遊んでみようということで、ローレンツアトラクタを使って音を鳴らす。
ついでにOSCでProcessingと連携して絵も描く。
SuperColliderのプログラム。
x座標が左右の定位に、y座標が音高に、z座標が音量に対応。
( SynthDef(\pfm, {|pan, freq, mul| var sig; sig = Pan2.ar(SinOsc.ar(freq, 0, mul), pan) * EnvGen.kr(Env.perc(0.05, 0.1, 0.4, -8), doneAction: 2); Out.ar(0, sig); }).send(s); r = Routine({ var sigma = 10, rho = 28, beta = 8/3, dt = 0.01; var pos = [20, 1, 0], px, py, pz, nx, ny, nz; var osc = NetAddr("127.0.0.1", 12000); loop { px = pos[0] + (sigma * (pos[1] - pos[0]) * dt); py = pos[1] + ((pos[0] * (rho - pos[2]) - pos[1]) * dt); pz = pos[2] + ((pos[0] * pos[1] - (beta * pos[2])) * dt); pos = [px, py, pz]; nx = clip2(px / 25, 1); ny = 10.0 * py + 330; nz = clip2(10.0 / pz, 1); Synth(\pfm, [pan: nx, freq: ny, mul: nz]); osc.sendMsg("/pos", nx, ny, nz); 0.05.wait; }; }); ) r.play; r.stop;
Processingのプログラム。
OSCを使った連携テストのためだけ。
import oscP5.*; import netP5.*; final int MAX = 500; final int FPS = 20; final int WSZ = 600; final int PORT = 12000; final color BGC = 0x333333; OscP5 osc; float pos[][]; int tail; int init; void setup() { tail = 1; init = MAX; pos = new float[MAX][3]; background(BGC); smooth(); frameRate(FPS); frame.setAlwaysOnTop(true); size(WSZ, WSZ); osc = new OscP5(this, PORT); } void draw() { translate(WSZ / 2, 50); background(BGC); int idx, idxn, c; for (int i = init; i < MAX - 3; i++) { idx = (tail + i) % MAX; idxn = (idx + 1) % MAX; stroke(constrain(i, 50, 255), constrain(i, 50, 255), 0); strokeWeight(pow(pos[idx][2] * 3, 2)); line(pos[idx][0] * WSZ / 2, WSZ - pos[idx][1], pos[idxn][0] * WSZ / 2, WSZ - pos[idxn][1]); } } void oscEvent(OscMessage msg) { if (msg.checkAddrPattern("/pos")) { pos[tail][0] = msg.get(0).floatValue(); pos[tail][1] = msg.get(1).floatValue(); pos[tail][2] = msg.get(2).floatValue(); if (init > 0) init--; tail = (tail + 1) % MAX; } }
いくつもあるsyntax sugar(なのか?)が全然甘くない。むしろ辛い。saltだよ!