今回も前回に引き続き、SuperColliderとProcessingの連携について紹介していきます。様々なサンプルを参考にしながら、音:SuperCollider、アニメーション:Processing という組合せで、一体どのような表現が可能となるのか探求していきます。
スライド資料
授業内で使用するスライド資料は、下記から参照してください。
サンプルプログラムのダウンロード
今日とりあげたサンプルプログラムは、以下からダウンロード可能です。
サンプルプログラム、ソースコード
SuperCollider(全サンプル共通)
// Geidai Interactive Music II 141211
// Simple Enveloped Sinwave
SynthDef("test_inst",{
arg freq=440, length=1.0, amp=0.5;
var env, out;
env = Env.perc(0.01, length);
out = SinOsc.ar([freq,freq*1.001])
* EnvGen.kr(env, doneAction:2) * amp;
Out.ar(0, out);
}).store;
//FM
SynthDef("fm1", { arg freq = 440, detune = 2, carPartial = 1, modPartial = 1, index = 3, mul = 0.2, pan=0.0, amp=0.5;
var mod, car;
mod = SinOsc.ar([freq, freq+detune] * modPartial,
0,
freq * index * LFNoise1.kr(10.reciprocal).abs);
car = Pan2.ar(SinOsc.ar((freq * carPartial) + mod, 0, mul), pan, amp);
Out.ar(0, car);
}).add;
// Saw wave
SynthDef("mySaw", {
arg fadeTime = 10, n = 0, rq = 0.3, detune = 0.001, base = 20, ratio = 1.5, harm = 1.5, amp = 0.2, gate=0;
var lfo, env, out;
env = EnvGen.kr(Env.new([0,1], [fadeTime], 'sine'));
lfo = SinOsc.ar(rrand(0.03, 0.05), 0, 100, 600);
out = Saw.ar([base+detune.rand, base+detune.rand] * (ratio ** n)) * amp
+ Saw.ar([base*harm+detune.rand, base*harm+detune.rand] * (ratio ** n)) * amp;
out = out * env;
out = RLPF.ar(out, lfo * (1.5 ** n), rq).clip2 * 0.5;
out = out * EnvGen.kr(Env.adsr(releaseTime:20), gate, doneAction: 2);
Out.ar(0, out);
}).store;
// Effect
SynthDef("fx", {
arg lpf=440, rq=0.5, amp=0.8;
var in, out;
in = In.ar(0, 2);
12.do({ in = AllpassL.ar(in, 0.1, LFNoise2.kr([rrand(0.0, 0.1),rrand(0.0, 0.1)],0.01,0.06), 4.0) });
out = CompanderD.ar(in) * amp;
ReplaceOut.ar(0, out);
}).store;
Processingサンプル01: クリックで拡がる円
import supercollider.*;
import oscP5.*;
Synth synth;
int NUM = 100;
float[] radius = new float[NUM];
PVector[] pos = new PVector[NUM];
int counter = 0;
void setup () {
size(800, 600);
frameRate(60);
noFill();
stroke(31, 127, 255);
strokeWeight(3);
for (int i = 0; i < NUM; i++) {
radius[i] = 0;
pos[i] = new PVector(width*2, width*2);
}
}
void draw() {
background(0);
for (int i = 0; i < NUM; i++) {
ellipse(pos[i].x, pos[i].y, radius[i], radius[i]);
radius[i] += 1;
if(radius[i] > width*1.5){
radius[i] = 0;
pos[i].x = width*2;
pos[i].y = width*2;
}
}
}
void mouseReleased() {
synth = new Synth("test_inst");
synth.set("amp", 0.5);
synth.set("freq", map(mouseY, height, 0, 20, 8000));
synth.create();
int n = counter % NUM;
radius[n] = 0;
pos[n].x = mouseX;
pos[n].y = mouseY;
counter++;
}
Processingサンプル02: 壁にぶつかると音が鳴りバウンド
import supercollider.*;
import oscP5.*;
int NUM = 400; //配列の数
//位置のベクトルの配列
PVector[] location = new PVector[NUM];
//速度のベクトルの配列
PVector[] velocity = new PVector[NUM];
//塗りの色の配列
color[] col = new color[NUM];
//円の大きさ(直径)の配列
float[] diameter = new float[NUM];
void setup() {
size(640, 480); //640x480pixelの画面を生成
frameRate(60); //フレームレート
noStroke();
for (int i = 0; i < NUM; i++) { //配列の数だけ繰り返し
//位置のベクトルの初期設定
location[i] = new PVector(random(width), random(height));
//速度のベクトルの初期設定
velocity[i] = new PVector(random(-4, 4), random(-4, 4));
//色の初期設定
col[i] = color(random(255), random(255), random(255), 127);
//大きさの初期設定
diameter[i] = random(3, 40);
}
}
void draw() {
background(15); //背景を描画
//配列の数だけ繰り返し
for (int i = 0; i < NUM; i++) {
fill(col[i]); //色を指定
//指定した位置に円を描画
ellipse(location[i].x, location[i].y, diameter[i], diameter[i]);
//位置のベクトルに速度のベクトルを加算、次の位置になる
location[i].add(velocity[i]);
//もし画面の左端、または右端に到達したら
if ((location[i].x > width) || (location[i].x < 0)) {
velocity[i].x *= -1; //X方向のスピドを反転
playSynth(location[i].x, location[i].y);
}
//もし画面の下端、または上端に到達したら
if ((location[i].y > height) || (location[i].y < 0)) {
velocity[i].y *= -1; //Y方向のスピードを反転
playSynth(location[i].x, location[i].y);
}
}
}
void playSynth(float x, float y) {
//新規に楽器を定義(まだ生成はされず)
Synth synth = new Synth("test_inst");
//引数を設定
synth.set("amp", 0.1);
synth.set("freq", map(x, height, 0, 20, 8000));
//楽器を生成
synth.create();
}
Processingサンプル03: 拡がる四角形とFM
import supercollider.*;
import oscP5.*;
int NUM = 100;
int count = 0;
PVector pos[] = new PVector[NUM];
float size[] = new float[NUM];
Synth fm[] = new Synth[NUM];
void setup() {
size(640, 480);
frameRate(60);
for (int i = 0; i < NUM; i++) {
pos[i] = new PVector(width*2, height*2);
size[i] = 0;
}
rectMode(CENTER);
}
void draw() {
background(0);
stroke(255, 127);
noFill();
strokeWeight(3);
for (int i = 0; i < NUM; i++) {
rect(pos[i].x, pos[i].y, size[i], size[i]);
if (size[i] > 0) {
size[i] += 1;
fm[i].set("index", size[i]);
}
if (size[i] > width/4) {
size[i] = 1;
}
}
}
void mouseReleased() {
int n = count % NUM;
pos[n].x = mouseX;
pos[n].y = mouseY;
size[n] = 1;
count++;
fm[n] = new Synth("fm1");
fm[n].set("amp", 0.2);
fm[n].set("freq", map(mouseY, height, 0, 40, 800));
fm[n].set("pan", map(mouseX, 0, width, -1.0, 1.0));
fm[n].set("modPartial", map(mouseY, 0, height, 1.0, 20.0));
fm[n].set("index", 0.0);
fm[n].create();
}
Processingサンプル04: 移動する帯と持続音
import supercollider.*;
import oscP5.*;
int BAR_NUM = 100;
int count = 0;
float[] x = new float[BAR_NUM];
float[] xSpeed = new float[BAR_NUM];
float[] bWidth = new float[BAR_NUM];
color[] bColor = new color[BAR_NUM];
void setup() {
size(640, 480);
frameRate(30);
colorMode(HSB, 360, 100, 100, 100);
noStroke();
for (int i=0; i width || x[i] < -bWidth[i]) {
xSpeed[i] *= -1;
}
}
}
void mouseReleased() {
int n = int(random(1, 12));
x[count] = mouseX;
bWidth[count] = n * 40;
count++;
Synth synth = new Synth("mySaw");
synth.set("n", n);
synth.set("gate", 1);
synth.create();
}